From 70b03d35d09dd98620bec16efb73e41861bd652a Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Mon, 6 Sep 2021 23:55:14 +0800 Subject: [PATCH 01/27] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _examples/config/postar.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_examples/config/postar.ini b/_examples/config/postar.ini index 73292e8..cd8b8ba 100644 --- a/_examples/config/postar.ini +++ b/_examples/config/postar.ini @@ -47,6 +47,6 @@ [server] #address = ":5897" #use_http2 = false -#tls_cert = "../conf/postar_cert.pem" -#tls_cert_key = "../conf/postar_cert.key" +#tls_cert = "/opt/postar/conf/postar_cert.pem" +#tls_cert_key = "/opt/postar/conf/postar_cert.key" #wait_for_closing = 30 \ No newline at end of file From d7912fddef79b58cfd9aa54dac02bb2de545fa39 Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Thu, 16 Sep 2021 02:06:42 +0800 Subject: [PATCH 02/27] =?UTF-8?q?=E5=B7=A5=E7=A8=8B=E5=8C=96=E5=AE=9E?= =?UTF-8?q?=E8=B7=B5=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _examples/config/postar.ini | 52 ----------- api/postard/postard.proto | 8 ++ base/model/response.go | 42 --------- build.sh | 40 --------- cmd/postard/main.go | 13 +++ internal/postard/server/grpc.go | 9 ++ internal/postard/server/http.go | 9 ++ internal/postard/service/smtp.go | 9 ++ main.go | 80 ----------------- module/basic.go | 34 ------- module/config.go | 110 ----------------------- module/logger.go | 76 ---------------- module/sender/basic.go | 63 ------------- module/sender/smtp.go | 116 ------------------------ module/server/basic.go | 53 ----------- module/server/http.go | 147 ------------------------------- postar.go | 61 ------------- 17 files changed, 48 insertions(+), 874 deletions(-) delete mode 100644 _examples/config/postar.ini create mode 100644 api/postard/postard.proto delete mode 100644 base/model/response.go delete mode 100644 build.sh create mode 100644 cmd/postard/main.go create mode 100644 internal/postard/server/grpc.go create mode 100644 internal/postard/server/http.go create mode 100644 internal/postard/service/smtp.go delete mode 100644 main.go delete mode 100644 module/basic.go delete mode 100644 module/config.go delete mode 100644 module/logger.go delete mode 100644 module/sender/basic.go delete mode 100644 module/sender/smtp.go delete mode 100644 module/server/basic.go delete mode 100644 module/server/http.go delete mode 100644 postar.go diff --git a/_examples/config/postar.ini b/_examples/config/postar.ini deleted file mode 100644 index cd8b8ba..0000000 --- a/_examples/config/postar.ini +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright 2021 Ye Zi Jie. All rights reserved. -# Use of this source code is governed by a MIT style -# license that can be found in the LICENSE file. -# -# Postar 配置文件案例 -# Author: FishGoddess - -# Global 是全局配置区块,主要用于配置整体的运行状态。 -# sender_type 配置邮件发送器的类型,可选项为 smtp。默认是 smtp。 -# server_type 配置网络服务器的类型,可选项为 http。默认是 http。 -[global] -#sender_type = "smtp" -#server_type = "http" - -# Logger 是日志配置区块,主要用于配置日志的运行状态。 -# level 配置日志记录的级别,可选项为 debug/info/warn/error/off,其中 off 表示关闭日志记录。默认是 info。 -# time_format 配置日志记录的时间格式化模板,若配置为 "",则将时间记录为 unix 秒级时间戳。默认为 2006-01-02 15:04:05.000。 -# output_file 配置非错误日志的输出文件路径。windows 默认是 ../log/postar.log,其他系统默认是 /opt/postar/log/postar.log。 -# error_output_file 配置错误日志的输出文件路径。windows 默认是 ../log/postar.error.log,其他系统默认是 /opt/postar/log/postar.error.log。 -[logger] -#level = "info" -#time_format = "2006-01-02 15:04:05.000" -#output_file = "/opt/postar/log/postar.log" -#error_output_file = "/opt/postar/log/postar.error.log" - -# Sender 是邮件发送器配置区块,主要用于配置邮件发送器的运行状态。 -# smtp_host 配置 smtp 服务器的主机名。默认从环境变量中取 POSTAR_SMTP_HOST 的值。 -# smtp_port 配置 smtp 服务器的端口号。默认为 587。 -# smtp_user 配置 smtp 服务器的用户名。默认从环境变量中取 POSTAR_SMTP_USER 的值。 -# smtp_password 配置 smtp 服务器的密码。默认从环境变量中取 POSTAR_SMTP_PASSWORD 的值。 -# worker_number 配置邮件发送器的异步工作协程数,仅在 sender_type 为 smtp 时有效。默认为 64。 -# request_channel_size 配置发送请求任务队列的最大大小,若任务堆积导致队列满了,则新的发送请求会被阻塞。默认为 65536。 -[sender] -#smtp_host = "smtp.xxx.com" -#smtp_port = 587 -#smtp_user = "xxx@xxx.com" -#smtp_password = "xxx" -#worker_number = 64 -#request_channel_size = 65536 - -# Server 是网络服务器配置区块,主要用于配置网络服务器的运行状态。 -# address 配置网络服务器的绑定地址,包括 IP 和端口号。默认为 :5897。 -# use_http2 选择是否使用 http2(建议使用 h2 标准配置证书),可选项为 true/false。默认为 false。 -# tls_cert 配置 TLS 证书文件的路径,只有 use_http2 = true,该选项才会生效。windows 默认为 ../conf/postar_cert.pem,其它系统默认是 /opt/postar/conf/postar_cert.pem。 -# tls_cert_key 配置 TLS 证书 key 文件的路径,只有 use_http2 = true,该选项才会生效。windows 默认为 ../conf/postar_cert.key,其它系统默认是 /opt/postar/conf/postar_cert.key。 -# wait_for_closing 配置服务器关闭等待时间,服务关闭时会等待请求处理完成,若等待超过改时间,服务会强制退出。单位为秒,默认是 30 秒。 -[server] -#address = ":5897" -#use_http2 = false -#tls_cert = "/opt/postar/conf/postar_cert.pem" -#tls_cert_key = "/opt/postar/conf/postar_cert.key" -#wait_for_closing = 30 \ No newline at end of file diff --git a/api/postard/postard.proto b/api/postard/postard.proto new file mode 100644 index 0000000..d04834e --- /dev/null +++ b/api/postard/postard.proto @@ -0,0 +1,8 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/16 01:42:33 +syntax = "proto3"; \ No newline at end of file diff --git a/base/model/response.go b/base/model/response.go deleted file mode 100644 index 4543f54..0000000 --- a/base/model/response.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/08/16 02:19:43 - -package model - -var ( - SuccessfulResponse = &Response{Code: 0, Msg: "ok"} - RequestMethodNotAllowedResponse = &Response{Code: -1001, Msg: "request method not allowed"} -) - -var ( - GetSendRequestFailedResponse = &Response{Code: -10001, Msg: "get send request failed"} - SendEmailTimeoutResponse = &Response{Code: -10002, Msg: "send email timeout"} - SendEmailFailedResponse = &Response{Code: -10003, Msg: "send email failed"} -) - -type Response struct { - Code int `json:"code"` - Msg string `json:"msg"` - Data interface{} `json:"data"` -} - -func NewResponse(code int, msg string, data interface{}) *Response { - return &Response{ - Code: code, - Msg: msg, - Data: data, - } -} - -func NewSuccessfulResponse(data interface{}) *Response { - return &Response{ - Code: SuccessfulResponse.Code, - Msg: SuccessfulResponse.Msg, - Data: data, - } -} diff --git a/build.sh b/build.sh deleted file mode 100644 index 854d1b8..0000000 --- a/build.sh +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright 2021 Ye Zi Jie. All rights reserved. -# Use of this source code is governed by a MIT style -# license that can be found in the LICENSE file. -# -# Postar build script -# Author: fishgoddess -VERSION=v0.2.3-alpha -BUILD_TARGET=target -CONFIG_FILE=_examples/config/postar.ini - -# Before building -echo "Start building to: $BUILD_TARGET" -mkdir $BUILD_TARGET -rm -r ${BUILD_TARGET:?}/bin ${BUILD_TARGET:?}/conf ${BUILD_TARGET:?}/log - -# Go build: windows, linux and darwin -echo "Building windows version..." -CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o $BUILD_TARGET/bin/postar-$VERSION-windows.exe - -echo "Building linux version..." -CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o $BUILD_TARGET/bin/postar-$VERSION-linux -chmod +x $BUILD_TARGET/bin/postar-$VERSION-linux - -echo "Building darwin version..." -CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o $BUILD_TARGET/bin/postar-$VERSION-darwin -chmod +x $BUILD_TARGET/bin/postar-$VERSION-darwin - -# Before packaging -echo "Before packaging..." -mkdir -p $BUILD_TARGET/conf -mkdir -p $BUILD_TARGET/log -cp $CONFIG_FILE $BUILD_TARGET/conf/ - -# Packaging to one -echo "Packaging to one: postar-$VERSION.tar.gz" -cd $BUILD_TARGET || exit -tar -czf postar-$VERSION.tar.gz bin conf log - -# Done -echo "Done." diff --git a/cmd/postard/main.go b/cmd/postard/main.go new file mode 100644 index 0000000..5688188 --- /dev/null +++ b/cmd/postard/main.go @@ -0,0 +1,13 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/16 01:33:43 + +package main + +func main() { + +} diff --git a/internal/postard/server/grpc.go b/internal/postard/server/grpc.go new file mode 100644 index 0000000..217f6b5 --- /dev/null +++ b/internal/postard/server/grpc.go @@ -0,0 +1,9 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/16 02:05:02 + +package server diff --git a/internal/postard/server/http.go b/internal/postard/server/http.go new file mode 100644 index 0000000..3665780 --- /dev/null +++ b/internal/postard/server/http.go @@ -0,0 +1,9 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/16 02:04:54 + +package server diff --git a/internal/postard/service/smtp.go b/internal/postard/service/smtp.go new file mode 100644 index 0000000..273d09d --- /dev/null +++ b/internal/postard/service/smtp.go @@ -0,0 +1,9 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/16 02:05:37 + +package service diff --git a/main.go b/main.go deleted file mode 100644 index 8718042..0000000 --- a/main.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/08/11 00:22:42 - -package main - -import ( - "flag" - "fmt" - "io/ioutil" - "runtime" - "strings" - "time" - - "github.com/avino-plan/postar/module" - "github.com/go-ini/ini" -) - -func configFile() *module.Config { - - defaultConfigFile := "/opt/postar/conf/postar.ini" - if strings.Contains(runtime.GOOS, "windows") { - defaultConfigFile = "../conf/postar.ini" - } - - configFile := flag.String("conf", defaultConfigFile, "The file path of Postar configuration.") - flag.Parse() - fmt.Printf("Postar got config file: %s\n", *configFile) - - config := module.DefaultConfig() - if *configFile == "" { - return config - } - - err := ini.MapTo(config, *configFile) - if err != nil { - panic(err) - } - - fmt.Printf("Postar's config:\n%+v\n", config) - return config -} - -func recordStartError(msg string, err error) { - msg = fmt.Sprintf("[%s] [%s] %+v\n", time.Now().Format("2006-01-02 15:04:05.000"), msg, err) - ioutil.WriteFile("./start_error.log", []byte(msg), 0644) -} - -func main() { - - beginTime := time.Now() - - postar := newPostar() - err := postar.Initialize(configFile()) - if err != nil { - recordStartError("Initialize error", err) - panic(err) - } - - endTime := time.Now() - fmt.Printf("Postar initialized successfully! It took %dms.\n", endTime.Sub(beginTime).Milliseconds()) - - go func() { - err = postar.Run() - if err != nil { - recordStartError("Run error", err) - panic(err) - } - }() - - err = postar.WaitForShutdown() - if err != nil { - recordStartError("Shutdown error", err) - panic(err) - } -} diff --git a/module/basic.go b/module/basic.go deleted file mode 100644 index ade4d97..0000000 --- a/module/basic.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/08/12 23:00:43 - -package module - -const ( - Version = "v0.2.3-alpha" -) - -var ( - initializations = []func(config *Config) error{ - initLogger, - } -) - -func Initialize(config *Config) error { - - if config == nil { - config = DefaultConfig() - } - - for _, initialize := range initializations { - err := initialize(config) - if err != nil { - return err - } - } - return nil -} diff --git a/module/config.go b/module/config.go deleted file mode 100644 index 52c404f..0000000 --- a/module/config.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/08/12 22:59:05 - -package module - -import ( - "fmt" - "os" - "runtime" - "strconv" - "strings" -) - -type GlobalConfig struct { - SenderType string `ini:"sender_type"` - ServerType string `ini:"server_type"` -} - -type LoggerConfig struct { - Level string `ini:"level"` - TimeFormat string `ini:"time_format"` - OutputFile string `ini:"output_file"` - ErrorOutputFile string `ini:"error_output_file"` -} - -type SenderConfig struct { - SmtpHost string `ini:"smtp_host"` - SmtpPort int `ini:"smtp_port"` - SmtpUser string `ini:"smtp_user"` - SmtpPassword string `ini:"smtp_password"` - WorkerNumber int `ini:"worker_number"` - RequestChannelSize int `ini:"request_channel_size"` -} - -type ServerConfig struct { - Address string `ini:"address"` - UseHttp2 bool `ini:"use_http2"` - TLSCert string `ini:"tls_cert"` - TLSCertKey string `ini:"tls_cert_key"` - WaitForClosing int `int:"wait_for_closing"` -} - -type Config struct { - Global *GlobalConfig `ini:"global"` - Logger *LoggerConfig `ini:"logger"` - Sender *SenderConfig `ini:"sender"` - Server *ServerConfig `ini:"server"` -} - -func (c *Config) String() string { - return fmt.Sprintf("- GlobalConfig is %+v\n- LoggerConfig is %+v\n- SenderConfig is %+v\n- ServerConfig is %+v\n", *c.Global, *c.Logger, *c.Sender, *c.Server) -} - -func DefaultConfig() *Config { - - logFile := "/opt/postar/log/postar.log" - errorLogFile := "/opt/postar/log/postar.error.log" - if strings.Contains(runtime.GOOS, "windows") { - logFile = "../log/postar.log" - errorLogFile = "../log/postar.error.log" - } - - tlsCert := "/opt/postar/conf/postar_cert.pem" - tlsCertKey := "/opt/postar/conf/postar_cert.key" - if strings.Contains(runtime.GOOS, "windows") { - tlsCert = "../conf/postar_cert.pem" - tlsCertKey = "../conf/postar_cert.key" - } - - port, err := strconv.Atoi(os.Getenv("POSTAR_SMTP_PORT")) - if err != nil { - port = 587 - } - return &Config{ - Global: &GlobalConfig{ - SenderType: "smtp", - ServerType: "http", - }, - Logger: &LoggerConfig{ - Level: "info", - TimeFormat: "2006-01-02 15:04:05.000", - OutputFile: logFile, - ErrorOutputFile: errorLogFile, - }, - Sender: &SenderConfig{ - SmtpHost: os.Getenv("POSTAR_SMTP_HOST"), - SmtpPort: port, - SmtpUser: os.Getenv("POSTAR_SMTP_USER"), - SmtpPassword: os.Getenv("POSTAR_SMTP_PASSWORD"), - WorkerNumber: 64, - RequestChannelSize: 65536, - }, - Server: &ServerConfig{ - Address: ":5897", - UseHttp2: false, - TLSCert: tlsCert, - TLSCertKey: tlsCertKey, - WaitForClosing: 30, // 30s - }, - } -} - -type Configurer interface { - Configure(config *Config) error -} diff --git a/module/logger.go b/module/logger.go deleted file mode 100644 index 87297d2..0000000 --- a/module/logger.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/08/16 00:57:03 - -package module - -import ( - "io" - "os" - - "github.com/FishGoddess/logit" -) - -var ( - globalLogger *logit.Logger -) - -func loggerLevelOptionFrom(config *Config) logit.Option { - - options := logit.Options() - if config.Logger.Level == "off" { - // TODO 替换为 OffLevel - return options.WithErrorLevel() - } - - if config.Logger.Level == "error" { - return options.WithErrorLevel() - } - - if config.Logger.Level == "warn" { - return options.WithWarnLevel() - } - - if config.Logger.Level == "info" { - return options.WithInfoLevel() - } - return options.WithDebugLevel() -} - -func outputFilesFrom(config *Config) (io.Writer, io.Writer, error) { - - outputFile, err := os.OpenFile(config.Logger.OutputFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) - if err != nil { - return nil, nil, err - } - - errorOutputFile, err := os.OpenFile(config.Logger.ErrorOutputFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) - if err != nil { - return nil, nil, err - } - return outputFile, errorOutputFile, nil -} - -func initLogger(config *Config) error { - - options := logit.Options() - outputFile, errorOutputFile, err := outputFilesFrom(config) - if err != nil { - return err - } - globalLogger = logit.NewLogger( - loggerLevelOptionFrom(config), - options.WithTimeFormat(config.Logger.TimeFormat), - options.WithWriter(outputFile, true), - options.WithErrorWriter(errorOutputFile, false), - ) - return nil -} - -func Logger() *logit.Logger { - return globalLogger -} diff --git a/module/sender/basic.go b/module/sender/basic.go deleted file mode 100644 index ce50a53..0000000 --- a/module/sender/basic.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/08/12 23:20:22 - -package sender - -import ( - "errors" - "fmt" - - "github.com/avino-plan/postar/module" -) - -var ( - timeoutErr = errors.New("timeout") - - senders = map[string]func() Sender{ - "smtp": newSmtpSender, - } -) - -type Email struct { - To []string `json:"to"` - Subject string `json:"subject"` - Content string `json:"content"` -} - -type SendOptions struct { - Async bool `json:"async"` - Timeout int `json:"timeout"` // 发送超时,单位为 ms -} - -func DefaultSendOptions() SendOptions { - return SendOptions{ - Async: false, - Timeout: 10000, - } -} - -type Sender interface { - module.Configurer - SendEmail(email *Email, options *SendOptions) error - Close() error -} - -func Initialize(config *module.Config) (Sender, error) { - - newSender, ok := senders[config.Global.SenderType] - if !ok { - return nil, fmt.Errorf("sender type %s not found", config.Global.SenderType) - } - - sender := newSender() - return sender, sender.Configure(config) -} - -func IsTimeout(err error) bool { - return err == timeoutErr -} diff --git a/module/sender/smtp.go b/module/sender/smtp.go deleted file mode 100644 index fb346bc..0000000 --- a/module/sender/smtp.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/08/11 23:34:02 - -package sender - -import ( - "sync" - "time" - - "github.com/avino-plan/postar/module" - "gopkg.in/gomail.v2" -) - -type smtpRequest struct { - message *gomail.Message - errorCh chan error -} - -func (sr *smtpRequest) reset() { - sr.message.Reset() - sr.errorCh = make(chan error, 1) -} - -type SmtpSender struct { - host string - port int - user string - dialer *gomail.Dialer - requestCh chan *smtpRequest - requestPool *sync.Pool -} - -func newSmtpSender() Sender { - return &SmtpSender{ - requestPool: &sync.Pool{ - New: func() interface{} { - return &smtpRequest{ - message: gomail.NewMessage(), - errorCh: make(chan error, 1), - } - }, - }, - } -} - -func (sms *SmtpSender) newRequest() *smtpRequest { - return sms.requestPool.Get().(*smtpRequest) -} - -func (sms *SmtpSender) releaseRequest(request *smtpRequest) { - request.reset() - sms.requestPool.Put(request) -} - -func (sms *SmtpSender) Configure(config *module.Config) error { - - sms.host = config.Sender.SmtpHost - sms.port = config.Sender.SmtpPort - sms.user = config.Sender.SmtpUser - sms.dialer = gomail.NewDialer(sms.host, sms.port, sms.user, config.Sender.SmtpPassword) - sms.requestCh = make(chan *smtpRequest, config.Sender.RequestChannelSize) - - for i := 0; i < config.Sender.WorkerNumber; i++ { - go func() { - for request := range sms.requestCh { - request.errorCh <- sms.dialer.DialAndSend(request.message) - sms.releaseRequest(request) - } - }() - } - return nil -} - -func (sms *SmtpSender) SendEmail(email *Email, options *SendOptions) error { - - if email == nil { - return nil - } - - if options == nil { - opts := DefaultSendOptions() - options = &opts - } - - request := sms.newRequest() - request.message.SetHeader("From", sms.user) - request.message.SetHeader("To", email.To...) - request.message.SetHeader("Subject", email.Subject) - request.message.SetBody("text/plain;charset=utf-8", email.Content) - module.Logger().Debug("before sending email").Any("email", email).Any("options", options).End() - - sms.requestCh <- request - if options.Async { - return nil - } - - select { - case err := <-request.errorCh: - return err - case <-time.After(time.Duration(options.Timeout) * time.Millisecond): - return timeoutErr - } -} - -func (sms *SmtpSender) Close() error { - for len(sms.requestCh) > 0 { - time.Sleep(100 * time.Millisecond) - } - close(sms.requestCh) - return nil -} diff --git a/module/server/basic.go b/module/server/basic.go deleted file mode 100644 index 285483f..0000000 --- a/module/server/basic.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/08/12 23:27:54 - -package server - -import ( - "fmt" - - "github.com/avino-plan/postar/module" - "github.com/avino-plan/postar/module/sender" -) - -var ( - servers = map[string]func() Server{ - "http": newHttpServer, - } -) - -type SendRequest struct { - Email *sender.Email `json:"email"` - Options *sender.SendOptions `json:"options"` -} - -func newSendRequest() *SendRequest { - sendOptions := sender.DefaultSendOptions() - return &SendRequest{ - Email: nil, - Options: &sendOptions, - } -} - -type Server interface { - module.Configurer - ConfigureSender(sender sender.Sender) - Serve() error - Close() error -} - -func Initialize(config *module.Config) (Server, error) { - - newServer, ok := servers[config.Global.ServerType] - if !ok { - return nil, fmt.Errorf("server type %s not found", config.Global.ServerType) - } - - server := newServer() - return server, server.Configure(config) -} diff --git a/module/server/http.go b/module/server/http.go deleted file mode 100644 index 74507af..0000000 --- a/module/server/http.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/08/12 00:26:28 - -package server - -import ( - "context" - "encoding/json" - "net/http" - "time" - - "github.com/avino-plan/postar/base/model" - "github.com/avino-plan/postar/module" - "github.com/avino-plan/postar/module/sender" -) - -type HttpServer struct { - address string - useHttp2 bool - tlsCert string - tlsCertKey string - waitForClosing int - sender sender.Sender - server *http.Server -} - -func newHttpServer() Server { - return &HttpServer{} -} - -func (hs *HttpServer) Configure(config *module.Config) error { - hs.address = config.Server.Address - hs.useHttp2 = config.Server.UseHttp2 - hs.waitForClosing = config.Server.WaitForClosing - hs.tlsCert = config.Server.TLSCert - hs.tlsCertKey = config.Server.TLSCertKey - return nil -} - -func (hs *HttpServer) ConfigureSender(sender sender.Sender) { - hs.sender = sender -} - -func (hs *HttpServer) writeResponse(writer http.ResponseWriter, statusCode int, response *model.Response) { - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - - marshaled, err := json.Marshal(response) - if err != nil { - module.Logger().Error("marshal response to Json failed").Error("err", err).End() - writer.WriteHeader(http.StatusInternalServerError) - writer.Write([]byte(`{"code": -1, "msg": "marshal response to Json failed", "data": null}`)) - return - } - - writer.WriteHeader(statusCode) - writer.Write(marshaled) -} - -func (hs *HttpServer) rootHandler(writer http.ResponseWriter, request *http.Request) { - - if request.Method != "GET" { - module.Logger().Error("request method not allowed").String("request.Method", request.Method).End() - hs.writeResponse(writer, http.StatusMethodNotAllowed, model.RequestMethodNotAllowedResponse) - return - } - hs.writeResponse(writer, http.StatusOK, model.NewSuccessfulResponse(map[string]interface{}{ - "service": "postar", - "introduction": "You know, for sending emails!", - "version": module.Version, - "protocol": request.Proto, - })) -} - -func (hs *HttpServer) getSendRequestFrom(request *http.Request) (*SendRequest, error) { - defer request.Body.Close() - sendRequest := newSendRequest() - return sendRequest, json.NewDecoder(request.Body).Decode(sendRequest) -} - -func (hs *HttpServer) sendEmailHandler(writer http.ResponseWriter, request *http.Request) { - - if request.Method != "PUT" { - module.Logger().Error("request method not allowed").String("request.Method", request.Method).End() - hs.writeResponse(writer, http.StatusMethodNotAllowed, model.RequestMethodNotAllowedResponse) - return - } - - sendRequest, err := hs.getSendRequestFrom(request) - if err != nil { - module.Logger().Error("get send request failed").Error("err", err).End() - hs.writeResponse(writer, http.StatusBadRequest, model.GetSendRequestFailedResponse) - return - } - - err = hs.sender.SendEmail(sendRequest.Email, sendRequest.Options) - if sender.IsTimeout(err) { - module.Logger().Error("send timeout").Error("err", err).Any("email", sendRequest.Email).Any("options", sendRequest.Options).End() - hs.writeResponse(writer, http.StatusInternalServerError, model.SendEmailTimeoutResponse) - return - } - - if err != nil { - module.Logger().Error("send email failed").Error("err", err).Any("email", sendRequest.Email).Any("options", sendRequest.Options).End() - hs.writeResponse(writer, http.StatusInternalServerError, model.SendEmailFailedResponse) - return - } - - hs.writeResponse(writer, http.StatusOK, model.SuccessfulResponse) -} - -func (hs *HttpServer) Serve() error { - - handlers := http.NewServeMux() - handlers.HandleFunc("/", hs.rootHandler) - handlers.HandleFunc("/send", hs.sendEmailHandler) - hs.server = &http.Server{Addr: hs.address, Handler: handlers} - - var err error - if hs.useHttp2 { - err = hs.server.ListenAndServeTLS(hs.tlsCert, hs.tlsCertKey) - } else { - err = hs.server.ListenAndServe() - } - - if err == http.ErrServerClosed { - return nil - } - return err -} - -func (hs *HttpServer) Close() error { - - ctx, cancel := context.WithTimeout(context.Background(), time.Duration(hs.waitForClosing)*time.Second) - defer cancel() - - err := hs.server.Shutdown(ctx) - if err != nil { - return err - } - return hs.sender.Close() -} diff --git a/postar.go b/postar.go deleted file mode 100644 index 385e580..0000000 --- a/postar.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/08/14 15:00:07 - -package main - -import ( - "fmt" - "os" - "os/signal" - "syscall" - - "github.com/avino-plan/postar/module" - "github.com/avino-plan/postar/module/sender" - "github.com/avino-plan/postar/module/server" -) - -type Postar struct { - svr server.Server -} - -func newPostar() *Postar { - return &Postar{} -} - -func (p *Postar) Initialize(config *module.Config) error { - - err := module.Initialize(config) - if err != nil { - return err - } - - sdr, err := sender.Initialize(config) - if err != nil { - return err - } - - p.svr, err = server.Initialize(config) - if err != nil { - return err - } - - p.svr.ConfigureSender(sdr) - return nil -} - -func (p *Postar) Run() error { - return p.svr.Serve() -} - -func (p *Postar) WaitForShutdown() error { - signalCh := make(chan os.Signal, 1) - signal.Notify(signalCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM) - <-signalCh - fmt.Println("Postar is shutdown gracefully...") - return p.svr.Close() -} From d3275e1e344f416b3e47cb8f084a9f3563375b86 Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Fri, 17 Sep 2021 01:55:52 +0800 Subject: [PATCH 03/27] =?UTF-8?q?=E9=82=AE=E4=BB=B6=E5=8F=91=E9=80=81?= =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/postard/server/server.go | 18 ++++ internal/postard/service/error.go | 9 ++ internal/postard/service/service.go | 34 +++++++ internal/postard/service/smtp.go | 64 +++++++++++++ pkg/concurrency/error.go | 20 ++++ pkg/concurrency/error_test.go | 25 +++++ pkg/concurrency/pool.go | 137 ++++++++++++++++++++++++++++ pkg/concurrency/pool_test.go | 39 ++++++++ 8 files changed, 346 insertions(+) create mode 100644 internal/postard/server/server.go create mode 100644 internal/postard/service/error.go create mode 100644 internal/postard/service/service.go create mode 100644 pkg/concurrency/error.go create mode 100644 pkg/concurrency/error_test.go create mode 100644 pkg/concurrency/pool.go create mode 100644 pkg/concurrency/pool_test.go diff --git a/internal/postard/server/server.go b/internal/postard/server/server.go new file mode 100644 index 0000000..83f937a --- /dev/null +++ b/internal/postard/server/server.go @@ -0,0 +1,18 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/17 00:15:34 + +package server + +import "github.com/avino-plan/postar/internal/postard/service" + +func DefaultSendOptions() *service.SendEmailOptions { + return &service.SendEmailOptions{ + Async: false, + Timeout: 10000, + } +} diff --git a/internal/postard/service/error.go b/internal/postard/service/error.go new file mode 100644 index 0000000..8421697 --- /dev/null +++ b/internal/postard/service/error.go @@ -0,0 +1,9 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/17 01:38:30 + +package service diff --git a/internal/postard/service/service.go b/internal/postard/service/service.go new file mode 100644 index 0000000..7fbb706 --- /dev/null +++ b/internal/postard/service/service.go @@ -0,0 +1,34 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/17 00:06:09 + +package service + +import ( + "context" + "time" +) + +// Email is an email. +type Email struct { + To []string // The receivers of one email. + Subject string // The subject of one email. + BodyType string // The content type of body. + Body string // The body of one email. +} + +// SendEmailOptions is the options of sending one email. +type SendEmailOptions struct { + Async bool // The mode of sending one email. + Timeout time.Duration // The timeout of sending one email. +} + +// SmtpService is the service of smtp. +type SmtpService interface { + // SendEmail sends email with options and returns an error if something wrong happens. + SendEmail(ctx context.Context, email *Email, options *SendEmailOptions) error +} diff --git a/internal/postard/service/smtp.go b/internal/postard/service/smtp.go index 273d09d..b87d20a 100644 --- a/internal/postard/service/smtp.go +++ b/internal/postard/service/smtp.go @@ -7,3 +7,67 @@ // Created at 2021/09/16 02:05:37 package service + +import ( + "context" + "time" + + "github.com/avino-plan/postar/pkg/concurrency" + "gopkg.in/gomail.v2" +) + +// smtpService is the service of smtp. +type smtpService struct { + host string // The host of smtp server. + port int // The port of smtp server. + user string // The user of smtp server. + password string // The password of smtp server. + pool *concurrency.Pool // The pool of workers. +} + +// NewSmtpService returns a new SmtpServer. +func NewSmtpService(host string, port int, user string, password string, pool *concurrency.Pool) SmtpService { + return &smtpService{ + host: host, + port: port, + user: user, + password: password, + pool: pool, + } +} + +// sendEmail sends email and returns an error if something wrong happens. +func (ss *smtpService) sendEmail(email *Email) error { + msg := gomail.NewMessage() + msg.SetHeader("From", ss.user) + msg.SetHeader("To", email.To...) + msg.SetHeader("Subject", email.Subject) + msg.SetBody(email.BodyType, email.Body) + return gomail.NewDialer(ss.host, ss.port, ss.user, ss.password).DialAndSend(msg) +} + +// SendEmail send +func (ss *smtpService) SendEmail(ctx context.Context, email *Email, options *SendEmailOptions) error { + + done := make(chan error, 1) + err := ss.pool.Go(ctx, func(ctx context.Context) { + done <- ss.sendEmail(email) + }) + if err != nil && concurrency.IsEnqueueTimeout(err) { + // TODO wraps err + return err + } + + if options.Async { + return nil + } + + timer := time.NewTimer(options.Timeout) + select { + case err = <-done: + return err + case <-timer.C: + // TODO timeout error + return nil + } +} diff --git a/pkg/concurrency/error.go b/pkg/concurrency/error.go new file mode 100644 index 0000000..5282efd --- /dev/null +++ b/pkg/concurrency/error.go @@ -0,0 +1,20 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/17 01:35:47 + +package concurrency + +import "errors" + +var ( + ErrEnqueueTimeout = errors.New("pool: enqueue a task to pool timeout") // An error: enqueue a task to pool timeout. +) + +// IsEnqueueTimeout returns if the err is ErrEnqueueTimeout. +func IsEnqueueTimeout(err error) bool { + return err == ErrEnqueueTimeout +} diff --git a/pkg/concurrency/error_test.go b/pkg/concurrency/error_test.go new file mode 100644 index 0000000..28ea0d6 --- /dev/null +++ b/pkg/concurrency/error_test.go @@ -0,0 +1,25 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/17 01:36:24 + +package concurrency + +import "testing" + +// go test -v -cover -run=^TestIsEnqueueTimeout$ +func TestIsEnqueueTimeout(t *testing.T) { + + err := ErrEnqueueTimeout + if !IsEnqueueTimeout(err) { + t.Error("err should be ErrEnqueueTimeout") + } + + err = nil + if IsEnqueueTimeout(err) { + t.Error("err should not be ErrEnqueueTimeout") + } +} diff --git a/pkg/concurrency/pool.go b/pkg/concurrency/pool.go new file mode 100644 index 0000000..c978af3 --- /dev/null +++ b/pkg/concurrency/pool.go @@ -0,0 +1,137 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/16 22:05:23 + +package concurrency + +import ( + "context" + "sync" + "sync/atomic" +) + +// PoolOptions is the options of Pool. +type PoolOptions func(pool *Pool) + +// WithMaxWorkers sets maxWorkers of pool. +func WithMaxWorkers(maxWorkers int) PoolOptions { + return func(pool *Pool) { + pool.maxWorkers = maxWorkers + pool.taskQueues = make([]chan func(), maxWorkers) + } +} + +// WithMaxWorkerTasks sets maxWorkerTasks of pool. +func WithMaxWorkerTasks(maxWorkerTasks int) PoolOptions { + return func(pool *Pool) { + pool.maxWorkerTasks = maxWorkerTasks + } +} + +// Pool is a set of goroutines. +// This is for controlling the number of goroutines and managing their lifecycles. +type Pool struct { + // maxWorkers is the max number of workers. + maxWorkers int + + // maxWorkerTasks is the max number of one task queue. + // The Go() method will block until the task queue has enough capacity. + maxWorkerTasks int + + // currentQueue records the current sequence of task queues. + currentQueue int64 + + // taskQueues stores all task queues. + // Every worker has its own task queue. + taskQueues []chan func() + + // onRecover is a function which will call in defer after finishing task. + onRecover func(cause interface{}) + + // wg is for managing all goroutines. + wg *sync.WaitGroup +} + +// NewPool returns a new Pool holder. +func NewPool(options ...PoolOptions) *Pool { + pool := &Pool{ + maxWorkers: 64, + maxWorkerTasks: 1024, + taskQueues: make([]chan func(), 64), + currentQueue: -1, + wg: &sync.WaitGroup{}, + } + + for _, option := range options { + option(pool) + } + return pool +} + +// OnRecover sets onRecover to p. +func (p *Pool) OnRecover(onRecover func(cause interface{})) { + p.onRecover = onRecover +} + +// Start starts all workers in pool and starts receiving tasks. +func (p *Pool) Start() *Pool { + for i := 0; i < p.maxWorkers; i++ { + taskQueue := make(chan func(), p.maxWorkerTasks) + p.taskQueues[i] = taskQueue + + p.wg.Add(1) + go func() { + defer p.wg.Done() + + for task := range taskQueue { + task() + } + }() + } + return p +} + +// nextTaskQueue returns next task queue. +func (p *Pool) nextTaskQueue() chan<- func() { + currentQueue := atomic.AddInt64(&p.currentQueue, 1) + if currentQueue >= int64(p.maxWorkers)-1 { + atomic.StoreInt64(&p.currentQueue, -1) + } + return p.taskQueues[currentQueue] +} + +// wrapTask wraps task to a complete task of pool. +func (p *Pool) wrapTask(ctx context.Context, task func(ctx context.Context)) func() { + return func() { + defer func() { + if cause := recover(); p.onRecover != nil { + p.onRecover(cause) // Notice: Just do some simple records which don't panic... + } + }() + task(ctx) + } +} + +// Go sends the task to task queue and waits for executing. +func (p *Pool) Go(ctx context.Context, task func(ctx context.Context)) error { + taskQueue := p.nextTaskQueue() + select { + case taskQueue <- p.wrapTask(ctx, task): + return nil + case <-ctx.Done(): + // TODO wraps ctx.Err() + return ErrEnqueueTimeout + } +} + +// Stop closes all task queues and waits for all workers to be shutdown. +func (p *Pool) Stop() { + for _, taskQueue := range p.taskQueues { + close(taskQueue) + } + p.wg.Wait() +} diff --git a/pkg/concurrency/pool_test.go b/pkg/concurrency/pool_test.go new file mode 100644 index 0000000..8017c6a --- /dev/null +++ b/pkg/concurrency/pool_test.go @@ -0,0 +1,39 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/16 23:07:51 + +package concurrency + +import ( + "context" + "testing" + "time" +) + +// go test -v -cover -run=^TestNewPool$ +func TestNewPool(t *testing.T) { + + ctx := context.Background() + pool := NewPool(WithMaxWorkers(4), WithMaxWorkerTasks(16)).Start() + + numbers := [1000]int{} + for i := 0; i < 1000; i++ { + no := i + pool.Go(ctx, func(ctx context.Context) { + numbers[no] = no + }) + } + + time.Sleep(time.Second) + pool.Stop() + + for i, num := range numbers { + if i != num { + t.Errorf("i %d != num %d", i, num) + } + } +} From 10f1b0f168181afa1c66e03b082ee258326b4858 Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Wed, 22 Sep 2021 01:08:21 +0800 Subject: [PATCH 04/27] =?UTF-8?q?=E4=B8=9A=E5=8A=A1=E5=B1=82=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 1 + go.sum | 2 + {pkg => internal/pkg}/concurrency/pool.go | 17 ++++--- .../pkg}/concurrency/pool_test.go | 7 ++- internal/postard/service/service.go | 8 +++ .../service/{error.go => service_test.go} | 9 +++- internal/postard/service/smtp.go | 36 +++++++------- internal/postard/service/smtp_test.go | 49 +++++++++++++++++++ pkg/concurrency/error.go | 20 -------- pkg/concurrency/error_test.go | 25 ---------- 10 files changed, 100 insertions(+), 74 deletions(-) rename {pkg => internal/pkg}/concurrency/pool.go (89%) rename {pkg => internal/pkg}/concurrency/pool_test.go (84%) rename internal/postard/service/{error.go => service_test.go} (59%) create mode 100644 internal/postard/service/smtp_test.go delete mode 100644 pkg/concurrency/error.go delete mode 100644 pkg/concurrency/error_test.go diff --git a/go.mod b/go.mod index e6fbedb..cce3e64 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.15 require ( github.com/FishGoddess/logit v0.4.4-alpha github.com/go-ini/ini v1.62.0 + github.com/pkg/errors v0.9.1 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df ) diff --git a/go.sum b/go.sum index a919aff..1c63154 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/FishGoddess/logit v0.4.4-alpha h1:eKe9XUslcU0QPVVhm+29N3OCnyXeii16Q5b github.com/FishGoddess/logit v0.4.4-alpha/go.mod h1:a2dFSibwfZXNNZ/V1jMOQBImPoySaxdPFThbsm17ZdY= github.com/go-ini/ini v1.62.0 h1:7VJT/ZXjzqSrvtraFp4ONq80hTcRQth1c9ZnQ3uNQvU= github.com/go-ini/ini v1.62.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= diff --git a/pkg/concurrency/pool.go b/internal/pkg/concurrency/pool.go similarity index 89% rename from pkg/concurrency/pool.go rename to internal/pkg/concurrency/pool.go index c978af3..7603f22 100644 --- a/pkg/concurrency/pool.go +++ b/internal/pkg/concurrency/pool.go @@ -105,27 +105,30 @@ func (p *Pool) nextTaskQueue() chan<- func() { } // wrapTask wraps task to a complete task of pool. -func (p *Pool) wrapTask(ctx context.Context, task func(ctx context.Context)) func() { +func (p *Pool) wrapTask(ctx context.Context, fn func(ctx context.Context) error, errorCh chan<- error) func() { return func() { + var err error defer func() { + errorCh <- err if cause := recover(); p.onRecover != nil { p.onRecover(cause) // Notice: Just do some simple records which don't panic... } }() - task(ctx) + err = fn(ctx) } } // Go sends the task to task queue and waits for executing. -func (p *Pool) Go(ctx context.Context, task func(ctx context.Context)) error { +func (p *Pool) Go(ctx context.Context, fn func(ctx context.Context) error) <-chan error { + errorCh := make(chan error, 1) + taskQueue := p.nextTaskQueue() select { - case taskQueue <- p.wrapTask(ctx, task): - return nil + case taskQueue <- p.wrapTask(ctx, fn, errorCh): case <-ctx.Done(): - // TODO wraps ctx.Err() - return ErrEnqueueTimeout + errorCh <- ctx.Err() } + return errorCh } // Stop closes all task queues and waits for all workers to be shutdown. diff --git a/pkg/concurrency/pool_test.go b/internal/pkg/concurrency/pool_test.go similarity index 84% rename from pkg/concurrency/pool_test.go rename to internal/pkg/concurrency/pool_test.go index 8017c6a..4e51c5a 100644 --- a/pkg/concurrency/pool_test.go +++ b/internal/pkg/concurrency/pool_test.go @@ -16,16 +16,19 @@ import ( // go test -v -cover -run=^TestNewPool$ func TestNewPool(t *testing.T) { - ctx := context.Background() pool := NewPool(WithMaxWorkers(4), WithMaxWorkerTasks(16)).Start() numbers := [1000]int{} for i := 0; i < 1000; i++ { no := i - pool.Go(ctx, func(ctx context.Context) { + errorCh := pool.Go(ctx, func(ctx context.Context) error { numbers[no] = no + return nil }) + if err := <-errorCh; err != nil { + t.Error(err) + } } time.Sleep(time.Second) diff --git a/internal/postard/service/service.go b/internal/postard/service/service.go index 7fbb706..d564c87 100644 --- a/internal/postard/service/service.go +++ b/internal/postard/service/service.go @@ -27,6 +27,14 @@ type SendEmailOptions struct { Timeout time.Duration // The timeout of sending one email. } +// DefaultSendEmailOptions returns a default options for sending emails. +func DefaultSendEmailOptions() *SendEmailOptions { + return &SendEmailOptions{ + Async: false, + Timeout: 5 * time.Second, + } +} + // SmtpService is the service of smtp. type SmtpService interface { // SendEmail sends email with options and returns an error if something wrong happens. diff --git a/internal/postard/service/error.go b/internal/postard/service/service_test.go similarity index 59% rename from internal/postard/service/error.go rename to internal/postard/service/service_test.go index 8421697..c87815e 100644 --- a/internal/postard/service/error.go +++ b/internal/postard/service/service_test.go @@ -4,6 +4,13 @@ // // Author: FishGoddess // Email: fishgoddess@qq.com -// Created at 2021/09/17 01:38:30 +// Created at 2021/09/22 00:22:39 package service + +import "testing" + +// go test -v -cover -run=^TestSmtpService$ +func TestSmtpService(t *testing.T) { + // do nothing... +} diff --git a/internal/postard/service/smtp.go b/internal/postard/service/smtp.go index b87d20a..a553641 100644 --- a/internal/postard/service/smtp.go +++ b/internal/postard/service/smtp.go @@ -12,12 +12,13 @@ import ( "context" "time" - "github.com/avino-plan/postar/pkg/concurrency" + "github.com/avino-plan/postar/internal/pkg/concurrency" + "github.com/pkg/errors" "gopkg.in/gomail.v2" ) -// smtpService is the service of smtp. -type smtpService struct { +// SmtpServiceImpl is the service of smtp. +type SmtpServiceImpl struct { host string // The host of smtp server. port int // The port of smtp server. user string // The user of smtp server. @@ -27,7 +28,7 @@ type smtpService struct { // NewSmtpService returns a new SmtpServer. func NewSmtpService(host string, port int, user string, password string, pool *concurrency.Pool) SmtpService { - return &smtpService{ + return &SmtpServiceImpl{ host: host, port: port, user: user, @@ -37,7 +38,7 @@ func NewSmtpService(host string, port int, user string, password string, pool *c } // sendEmail sends email and returns an error if something wrong happens. -func (ss *smtpService) sendEmail(email *Email) error { +func (ss *SmtpServiceImpl) sendEmail(email *Email) error { msg := gomail.NewMessage() msg.SetHeader("From", ss.user) msg.SetHeader("To", email.To...) @@ -46,28 +47,25 @@ func (ss *smtpService) sendEmail(email *Email) error { return gomail.NewDialer(ss.host, ss.port, ss.user, ss.password).DialAndSend(msg) } -// SendEmail send -func (ss *smtpService) SendEmail(ctx context.Context, email *Email, options *SendEmailOptions) error { - - done := make(chan error, 1) - err := ss.pool.Go(ctx, func(ctx context.Context) { - done <- ss.sendEmail(email) - }) - if err != nil && concurrency.IsEnqueueTimeout(err) { - // TODO wraps err - return err +// SendEmail sends email to somewhere. +func (ss *SmtpServiceImpl) SendEmail(ctx context.Context, email *Email, options *SendEmailOptions) error { + if options == nil { + options = DefaultSendEmailOptions() } + errorCh := ss.pool.Go(ctx, func(ctx context.Context) error { + return ss.sendEmail(email) + }) if options.Async { return nil } timer := time.NewTimer(options.Timeout) select { - case err = <-done: - return err + case err := <-errorCh: + timer.Stop() + return errors.Wrap(err, "send email failed") case <-timer.C: - // TODO timeout error - return nil + return errors.New("send email timeout") } } diff --git a/internal/postard/service/smtp_test.go b/internal/postard/service/smtp_test.go new file mode 100644 index 0000000..16c5b6f --- /dev/null +++ b/internal/postard/service/smtp_test.go @@ -0,0 +1,49 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/22 00:24:19 + +package service + +import ( + "context" + "os" + "strconv" + "testing" + "time" + + "github.com/avino-plan/postar/internal/pkg/concurrency" +) + +// go test -v -cover -run=^TestNewSmtpService$ +func TestNewSmtpService(t *testing.T) { + host := os.Getenv("POSTAR_SMTP_HOST") + user := os.Getenv("POSTAR_SMTP_USER") + password := os.Getenv("POSTAR_SMTP_PASSWORD") + to := os.Getenv("POSTAR_SMTP_TO") + if host == "" || user == "" || password == "" || to == "" { + t.Skipf("smtp host %s or user %s or password %s or to %s is empty", host, user, password, to) + } + + port, err := strconv.ParseInt(os.Getenv("POSTAR_SMTP_PORT"), 10, 64) + if err != nil { + port = 587 + } + + pool := concurrency.NewPool().Start() + defer pool.Stop() + + smtpService := NewSmtpService(host, int(port), user, password, pool) + err = smtpService.SendEmail(context.Background(), &Email{ + To: []string{to}, + Subject: t.Name(), + BodyType: "text/html;charset=utf-8", + Body: t.Name() + time.Now().Format("20060102150405.000"), + }, DefaultSendEmailOptions()) + if err != nil { + t.Error(err) + } +} diff --git a/pkg/concurrency/error.go b/pkg/concurrency/error.go deleted file mode 100644 index 5282efd..0000000 --- a/pkg/concurrency/error.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/09/17 01:35:47 - -package concurrency - -import "errors" - -var ( - ErrEnqueueTimeout = errors.New("pool: enqueue a task to pool timeout") // An error: enqueue a task to pool timeout. -) - -// IsEnqueueTimeout returns if the err is ErrEnqueueTimeout. -func IsEnqueueTimeout(err error) bool { - return err == ErrEnqueueTimeout -} diff --git a/pkg/concurrency/error_test.go b/pkg/concurrency/error_test.go deleted file mode 100644 index 28ea0d6..0000000 --- a/pkg/concurrency/error_test.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/09/17 01:36:24 - -package concurrency - -import "testing" - -// go test -v -cover -run=^TestIsEnqueueTimeout$ -func TestIsEnqueueTimeout(t *testing.T) { - - err := ErrEnqueueTimeout - if !IsEnqueueTimeout(err) { - t.Error("err should be ErrEnqueueTimeout") - } - - err = nil - if IsEnqueueTimeout(err) { - t.Error("err should not be ErrEnqueueTimeout") - } -} From de2e0c0870418e9e46314da3036411e0144ffb4f Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Wed, 22 Sep 2021 01:48:26 +0800 Subject: [PATCH 05/27] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20proto=20=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/pkg/status.pb.go | 161 ++++++++++++ api/pkg/status.proto | 17 ++ api/postard/postard.pb.go | 407 ++++++++++++++++++++++++++++++ api/postard/postard.proto | 37 ++- api/postard/postard_grpc.pb.go | 101 ++++++++ go.mod | 4 +- go.sum | 122 ++++++++- internal/postard/server/server.go | 9 - 8 files changed, 840 insertions(+), 18 deletions(-) create mode 100644 api/pkg/status.pb.go create mode 100644 api/pkg/status.proto create mode 100644 api/postard/postard.pb.go create mode 100644 api/postard/postard_grpc.pb.go diff --git a/api/pkg/status.pb.go b/api/pkg/status.pb.go new file mode 100644 index 0000000..34f194e --- /dev/null +++ b/api/pkg/status.pb.go @@ -0,0 +1,161 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/22 01:18:01 + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.0 +// source: status.proto + +package pkg + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Status is the status of responses. +type Status struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Code int64 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` // Globally unique. + Msg string `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"` // The description of responses. +} + +func (x *Status) Reset() { + *x = Status{} + if protoimpl.UnsafeEnabled { + mi := &file_status_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Status) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Status) ProtoMessage() {} + +func (x *Status) ProtoReflect() protoreflect.Message { + mi := &file_status_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Status.ProtoReflect.Descriptor instead. +func (*Status) Descriptor() ([]byte, []int) { + return file_status_proto_rawDescGZIP(), []int{0} +} + +func (x *Status) GetCode() int64 { + if x != nil { + return x.Code + } + return 0 +} + +func (x *Status) GetMsg() string { + if x != nil { + return x.Msg + } + return "" +} + +var File_status_proto protoreflect.FileDescriptor + +var file_status_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, + 0x70, 0x6b, 0x67, 0x22, 0x2e, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, + 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x63, 0x6f, 0x64, + 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6d, 0x73, 0x67, 0x42, 0x26, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x2d, 0x70, 0x6c, 0x61, 0x6e, 0x2f, 0x70, 0x6f, 0x73, + 0x74, 0x61, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6b, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_status_proto_rawDescOnce sync.Once + file_status_proto_rawDescData = file_status_proto_rawDesc +) + +func file_status_proto_rawDescGZIP() []byte { + file_status_proto_rawDescOnce.Do(func() { + file_status_proto_rawDescData = protoimpl.X.CompressGZIP(file_status_proto_rawDescData) + }) + return file_status_proto_rawDescData +} + +var file_status_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_status_proto_goTypes = []interface{}{ + (*Status)(nil), // 0: pkg.Status +} +var file_status_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_status_proto_init() } +func file_status_proto_init() { + if File_status_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_status_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Status); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_status_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_status_proto_goTypes, + DependencyIndexes: file_status_proto_depIdxs, + MessageInfos: file_status_proto_msgTypes, + }.Build() + File_status_proto = out.File + file_status_proto_rawDesc = nil + file_status_proto_goTypes = nil + file_status_proto_depIdxs = nil +} diff --git a/api/pkg/status.proto b/api/pkg/status.proto new file mode 100644 index 0000000..4c03e09 --- /dev/null +++ b/api/pkg/status.proto @@ -0,0 +1,17 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/22 01:18:01 +syntax = "proto3"; +package pkg; + +option go_package = "github.com/avino-plan/postar/api/pkg"; + +// Status is the status of responses. +message Status { + int64 code = 1; // Globally unique. + string msg = 2; // The description of responses. +} \ No newline at end of file diff --git a/api/postard/postard.pb.go b/api/postard/postard.pb.go new file mode 100644 index 0000000..55339c0 --- /dev/null +++ b/api/postard/postard.pb.go @@ -0,0 +1,407 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/16 01:42:33 + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.0 +// source: postard.proto + +package postard + +import ( + pkg "github.com/avino-plan/postar/api/pkg" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Email wraps all information of using smtp service. +type Email struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + To []string `protobuf:"bytes,1,rep,name=to,proto3" json:"to,omitempty"` // The receivers of one email. + Subject string `protobuf:"bytes,2,opt,name=subject,proto3" json:"subject,omitempty"` // The subject of one email. + BodyType string `protobuf:"bytes,3,opt,name=BodyType,proto3" json:"BodyType,omitempty"` // The content type of body. + Body string `protobuf:"bytes,4,opt,name=Body,proto3" json:"Body,omitempty"` // The body of one email. +} + +func (x *Email) Reset() { + *x = Email{} + if protoimpl.UnsafeEnabled { + mi := &file_postard_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Email) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Email) ProtoMessage() {} + +func (x *Email) ProtoReflect() protoreflect.Message { + mi := &file_postard_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Email.ProtoReflect.Descriptor instead. +func (*Email) Descriptor() ([]byte, []int) { + return file_postard_proto_rawDescGZIP(), []int{0} +} + +func (x *Email) GetTo() []string { + if x != nil { + return x.To + } + return nil +} + +func (x *Email) GetSubject() string { + if x != nil { + return x.Subject + } + return "" +} + +func (x *Email) GetBodyType() string { + if x != nil { + return x.BodyType + } + return "" +} + +func (x *Email) GetBody() string { + if x != nil { + return x.Body + } + return "" +} + +// SendEmailOptions is the options of sending emails. +type SendEmailOptions struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Async bool `protobuf:"varint,1,opt,name=async,proto3" json:"async,omitempty"` // If need sending emails asynchronously. + Timeout int64 `protobuf:"varint,2,opt,name=timeout,proto3" json:"timeout,omitempty"` // Sending timeout. +} + +func (x *SendEmailOptions) Reset() { + *x = SendEmailOptions{} + if protoimpl.UnsafeEnabled { + mi := &file_postard_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SendEmailOptions) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendEmailOptions) ProtoMessage() {} + +func (x *SendEmailOptions) ProtoReflect() protoreflect.Message { + mi := &file_postard_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendEmailOptions.ProtoReflect.Descriptor instead. +func (*SendEmailOptions) Descriptor() ([]byte, []int) { + return file_postard_proto_rawDescGZIP(), []int{1} +} + +func (x *SendEmailOptions) GetAsync() bool { + if x != nil { + return x.Async + } + return false +} + +func (x *SendEmailOptions) GetTimeout() int64 { + if x != nil { + return x.Timeout + } + return 0 +} + +// SendEmailRequest is the request of SendEmail. +type SendEmailRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Email *Email `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` // Sending email. + Options *SendEmailOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` // Sending options. +} + +func (x *SendEmailRequest) Reset() { + *x = SendEmailRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_postard_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SendEmailRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendEmailRequest) ProtoMessage() {} + +func (x *SendEmailRequest) ProtoReflect() protoreflect.Message { + mi := &file_postard_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendEmailRequest.ProtoReflect.Descriptor instead. +func (*SendEmailRequest) Descriptor() ([]byte, []int) { + return file_postard_proto_rawDescGZIP(), []int{2} +} + +func (x *SendEmailRequest) GetEmail() *Email { + if x != nil { + return x.Email + } + return nil +} + +func (x *SendEmailRequest) GetOptions() *SendEmailOptions { + if x != nil { + return x.Options + } + return nil +} + +// SendEmailResponse is the response of SendEmail. +type SendEmailResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Status *pkg.Status `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` // The status of response. +} + +func (x *SendEmailResponse) Reset() { + *x = SendEmailResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_postard_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SendEmailResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendEmailResponse) ProtoMessage() {} + +func (x *SendEmailResponse) ProtoReflect() protoreflect.Message { + mi := &file_postard_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendEmailResponse.ProtoReflect.Descriptor instead. +func (*SendEmailResponse) Descriptor() ([]byte, []int) { + return file_postard_proto_rawDescGZIP(), []int{3} +} + +func (x *SendEmailResponse) GetStatus() *pkg.Status { + if x != nil { + return x.Status + } + return nil +} + +var File_postard_proto protoreflect.FileDescriptor + +var file_postard_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x07, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x1a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x61, 0x0a, 0x05, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, + 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, 0x12, + 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x42, 0x6f, 0x64, + 0x79, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x42, 0x6f, 0x64, + 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x42, 0x6f, 0x64, 0x79, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x42, 0x6f, 0x64, 0x79, 0x22, 0x42, 0x0a, 0x10, 0x53, 0x65, 0x6e, + 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x14, 0x0a, + 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x73, + 0x79, 0x6e, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, 0x6d, 0x0a, + 0x10, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x24, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x61, 0x69, 0x6c, + 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x33, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, + 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x38, 0x0a, 0x11, + 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x23, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x32, 0x4d, 0x0a, 0x07, 0x50, 0x6f, 0x73, 0x74, 0x61, 0x72, + 0x64, 0x12, 0x42, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x19, + 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, + 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x70, 0x6f, 0x73, 0x74, + 0x61, 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x2d, 0x70, 0x6c, 0x61, 0x6e, 0x2f, 0x70, + 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, + 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_postard_proto_rawDescOnce sync.Once + file_postard_proto_rawDescData = file_postard_proto_rawDesc +) + +func file_postard_proto_rawDescGZIP() []byte { + file_postard_proto_rawDescOnce.Do(func() { + file_postard_proto_rawDescData = protoimpl.X.CompressGZIP(file_postard_proto_rawDescData) + }) + return file_postard_proto_rawDescData +} + +var file_postard_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_postard_proto_goTypes = []interface{}{ + (*Email)(nil), // 0: postard.Email + (*SendEmailOptions)(nil), // 1: postard.SendEmailOptions + (*SendEmailRequest)(nil), // 2: postard.SendEmailRequest + (*SendEmailResponse)(nil), // 3: postard.SendEmailResponse + (*pkg.Status)(nil), // 4: pkg.Status +} +var file_postard_proto_depIdxs = []int32{ + 0, // 0: postard.SendEmailRequest.email:type_name -> postard.Email + 1, // 1: postard.SendEmailRequest.options:type_name -> postard.SendEmailOptions + 4, // 2: postard.SendEmailResponse.status:type_name -> pkg.Status + 2, // 3: postard.Postard.SendEmail:input_type -> postard.SendEmailRequest + 3, // 4: postard.Postard.SendEmail:output_type -> postard.SendEmailResponse + 4, // [4:5] is the sub-list for method output_type + 3, // [3:4] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_postard_proto_init() } +func file_postard_proto_init() { + if File_postard_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_postard_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Email); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_postard_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SendEmailOptions); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_postard_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SendEmailRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_postard_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SendEmailResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_postard_proto_rawDesc, + NumEnums: 0, + NumMessages: 4, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_postard_proto_goTypes, + DependencyIndexes: file_postard_proto_depIdxs, + MessageInfos: file_postard_proto_msgTypes, + }.Build() + File_postard_proto = out.File + file_postard_proto_rawDesc = nil + file_postard_proto_goTypes = nil + file_postard_proto_depIdxs = nil +} diff --git a/api/postard/postard.proto b/api/postard/postard.proto index d04834e..f54a976 100644 --- a/api/postard/postard.proto +++ b/api/postard/postard.proto @@ -5,4 +5,39 @@ // Author: FishGoddess // Email: fishgoddess@qq.com // Created at 2021/09/16 01:42:33 -syntax = "proto3"; \ No newline at end of file +syntax = "proto3"; +package postard; +import "status.proto"; + +// protoc --proto_path=../pkg --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative postard.proto +option go_package = "github.com/avino-plan/postar/api/postard"; + +// Email wraps all information of using smtp service. +message Email { + repeated string to = 1; // The receivers of one email. + string subject = 2; // The subject of one email. + string BodyType = 3; // The content type of body. + string Body = 4; // The body of one email. +} + +// SendEmailOptions is the options of sending emails. +message SendEmailOptions { + bool async = 1; // If need sending emails asynchronously. + int64 timeout = 2; // Sending timeout. +} + +// SendEmailRequest is the request of SendEmail. +message SendEmailRequest { + Email email = 1; // Sending email. + SendEmailOptions options = 2; // Sending options. +} + +// SendEmailResponse is the response of SendEmail. +message SendEmailResponse { + pkg.Status status = 1; // The status of response. +} + +// Postard is the core service of postar. +service Postard { + rpc SendEmail(SendEmailRequest) returns (SendEmailResponse); // For sending emails. +} \ No newline at end of file diff --git a/api/postard/postard_grpc.pb.go b/api/postard/postard_grpc.pb.go new file mode 100644 index 0000000..8e2b601 --- /dev/null +++ b/api/postard/postard_grpc.pb.go @@ -0,0 +1,101 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package postard + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// PostardClient is the client API for Postard service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type PostardClient interface { + SendEmail(ctx context.Context, in *SendEmailRequest, opts ...grpc.CallOption) (*SendEmailResponse, error) +} + +type postardClient struct { + cc grpc.ClientConnInterface +} + +func NewPostardClient(cc grpc.ClientConnInterface) PostardClient { + return &postardClient{cc} +} + +func (c *postardClient) SendEmail(ctx context.Context, in *SendEmailRequest, opts ...grpc.CallOption) (*SendEmailResponse, error) { + out := new(SendEmailResponse) + err := c.cc.Invoke(ctx, "/postard.Postard/SendEmail", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// PostardServer is the server API for Postard service. +// All implementations must embed UnimplementedPostardServer +// for forward compatibility +type PostardServer interface { + SendEmail(context.Context, *SendEmailRequest) (*SendEmailResponse, error) + mustEmbedUnimplementedPostardServer() +} + +// UnimplementedPostardServer must be embedded to have forward compatible implementations. +type UnimplementedPostardServer struct { +} + +func (UnimplementedPostardServer) SendEmail(context.Context, *SendEmailRequest) (*SendEmailResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SendEmail not implemented") +} +func (UnimplementedPostardServer) mustEmbedUnimplementedPostardServer() {} + +// UnsafePostardServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to PostardServer will +// result in compilation errors. +type UnsafePostardServer interface { + mustEmbedUnimplementedPostardServer() +} + +func RegisterPostardServer(s grpc.ServiceRegistrar, srv PostardServer) { + s.RegisterService(&Postard_ServiceDesc, srv) +} + +func _Postard_SendEmail_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SendEmailRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PostardServer).SendEmail(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/postard.Postard/SendEmail", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PostardServer).SendEmail(ctx, req.(*SendEmailRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Postard_ServiceDesc is the grpc.ServiceDesc for Postard service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Postard_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "postard.Postard", + HandlerType: (*PostardServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SendEmail", + Handler: _Postard_SendEmail_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "postard.proto", +} diff --git a/go.mod b/go.mod index cce3e64..a8cb922 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/avino-plan/postar go 1.15 require ( - github.com/FishGoddess/logit v0.4.4-alpha - github.com/go-ini/ini v1.62.0 github.com/pkg/errors v0.9.1 + google.golang.org/grpc v1.40.0 + google.golang.org/protobuf v1.27.1 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df ) diff --git a/go.sum b/go.sum index 1c63154..4baad21 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,122 @@ -github.com/FishGoddess/logit v0.3.3 h1:qnxPIvq8YXGwG9y59Sk0hjoArOzWukQQqIsPT3dh3Xw= -github.com/FishGoddess/logit v0.3.3/go.mod h1:a2dFSibwfZXNNZ/V1jMOQBImPoySaxdPFThbsm17ZdY= -github.com/FishGoddess/logit v0.4.4-alpha h1:eKe9XUslcU0QPVVhm+29N3OCnyXeii16Q5bYQ6OinSE= -github.com/FishGoddess/logit v0.4.4-alpha/go.mod h1:a2dFSibwfZXNNZ/V1jMOQBImPoySaxdPFThbsm17ZdY= -github.com/go-ini/ini v1.62.0 h1:7VJT/ZXjzqSrvtraFp4ONq80hTcRQth1c9ZnQ3uNQvU= -github.com/go-ini/ini v1.62.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/postard/server/server.go b/internal/postard/server/server.go index 83f937a..bd90671 100644 --- a/internal/postard/server/server.go +++ b/internal/postard/server/server.go @@ -7,12 +7,3 @@ // Created at 2021/09/17 00:15:34 package server - -import "github.com/avino-plan/postar/internal/postard/service" - -func DefaultSendOptions() *service.SendEmailOptions { - return &service.SendEmailOptions{ - Async: false, - Timeout: 10000, - } -} From cefc5b319abb991c3a565d1db53b287b71396557 Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Fri, 24 Sep 2021 00:04:39 +0800 Subject: [PATCH 06/27] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/pkg/status.pb.go | 6 ++- api/pkg/status.proto | 3 +- api/postard/postard.pb.go | 74 ++++++++++++++++----------- api/postard/postard.proto | 4 +- api/postard/postard_grpc.pb.go | 6 +-- internal/postard/server/server.go | 9 ---- internal/postard/service/smtp.go | 27 +++++----- internal/postard/service/smtp_test.go | 2 +- 8 files changed, 68 insertions(+), 63 deletions(-) delete mode 100644 internal/postard/server/server.go diff --git a/api/pkg/status.pb.go b/api/pkg/status.pb.go index 34f194e..9564874 100644 --- a/api/pkg/status.pb.go +++ b/api/pkg/status.pb.go @@ -87,7 +87,9 @@ func (x *Status) GetMsg() string { var File_status_proto protoreflect.FileDescriptor var file_status_proto_rawDesc = []byte{ - 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, + 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x23, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, + 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6b, 0x67, 0x22, 0x2e, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, @@ -111,7 +113,7 @@ func file_status_proto_rawDescGZIP() []byte { var file_status_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_status_proto_goTypes = []interface{}{ - (*Status)(nil), // 0: pkg.Status + (*Status)(nil), // 0: github.com.avinoplan.postar.api.pkg.Status } var file_status_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type diff --git a/api/pkg/status.proto b/api/pkg/status.proto index 4c03e09..479e805 100644 --- a/api/pkg/status.proto +++ b/api/pkg/status.proto @@ -6,8 +6,9 @@ // Email: fishgoddess@qq.com // Created at 2021/09/22 01:18:01 syntax = "proto3"; -package pkg; +package github.com.avinoplan.postar.api.pkg; +// protoc -I=. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative status.proto option go_package = "github.com/avino-plan/postar/api/pkg"; // Status is the status of responses. diff --git a/api/postard/postard.pb.go b/api/postard/postard.pb.go index 55339c0..45db3f5 100644 --- a/api/postard/postard.pb.go +++ b/api/postard/postard.pb.go @@ -265,7 +265,9 @@ var File_postard_proto protoreflect.FileDescriptor var file_postard_proto_rawDesc = []byte{ 0x0a, 0x0d, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, - 0x07, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x1a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, + 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x1a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x61, 0x0a, 0x05, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, @@ -276,26 +278,36 @@ var file_postard_proto_rawDesc = []byte{ 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, 0x6d, 0x0a, - 0x10, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x24, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x61, 0x69, 0x6c, - 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x33, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, - 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x38, 0x0a, 0x11, - 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x23, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x32, 0x4d, 0x0a, 0x07, 0x50, 0x6f, 0x73, 0x74, 0x61, 0x72, - 0x64, 0x12, 0x42, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x19, - 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, - 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x70, 0x6f, 0x73, 0x74, - 0x61, 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x2d, 0x70, 0x6c, 0x61, 0x6e, 0x2f, 0x70, - 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, - 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, 0xad, 0x01, + 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x44, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2e, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, + 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x61, 0x69, + 0x6c, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x53, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, + 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x73, 0x74, + 0x61, 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x58, 0x0a, + 0x11, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x43, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, + 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x32, 0x8e, 0x01, 0x0a, 0x07, 0x50, 0x6f, 0x73, 0x74, + 0x61, 0x72, 0x64, 0x12, 0x82, 0x01, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, + 0x6c, 0x12, 0x39, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, + 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, + 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, 0x2e, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, + 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, + 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x2d, 0x70, 0x6c, 0x61, + 0x6e, 0x2f, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6f, 0x73, + 0x74, 0x61, 0x72, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -312,18 +324,18 @@ func file_postard_proto_rawDescGZIP() []byte { var file_postard_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_postard_proto_goTypes = []interface{}{ - (*Email)(nil), // 0: postard.Email - (*SendEmailOptions)(nil), // 1: postard.SendEmailOptions - (*SendEmailRequest)(nil), // 2: postard.SendEmailRequest - (*SendEmailResponse)(nil), // 3: postard.SendEmailResponse - (*pkg.Status)(nil), // 4: pkg.Status + (*Email)(nil), // 0: github.com.avinoplan.postar.api.postard.Email + (*SendEmailOptions)(nil), // 1: github.com.avinoplan.postar.api.postard.SendEmailOptions + (*SendEmailRequest)(nil), // 2: github.com.avinoplan.postar.api.postard.SendEmailRequest + (*SendEmailResponse)(nil), // 3: github.com.avinoplan.postar.api.postard.SendEmailResponse + (*pkg.Status)(nil), // 4: github.com.avinoplan.postar.api.pkg.Status } var file_postard_proto_depIdxs = []int32{ - 0, // 0: postard.SendEmailRequest.email:type_name -> postard.Email - 1, // 1: postard.SendEmailRequest.options:type_name -> postard.SendEmailOptions - 4, // 2: postard.SendEmailResponse.status:type_name -> pkg.Status - 2, // 3: postard.Postard.SendEmail:input_type -> postard.SendEmailRequest - 3, // 4: postard.Postard.SendEmail:output_type -> postard.SendEmailResponse + 0, // 0: github.com.avinoplan.postar.api.postard.SendEmailRequest.email:type_name -> github.com.avinoplan.postar.api.postard.Email + 1, // 1: github.com.avinoplan.postar.api.postard.SendEmailRequest.options:type_name -> github.com.avinoplan.postar.api.postard.SendEmailOptions + 4, // 2: github.com.avinoplan.postar.api.postard.SendEmailResponse.status:type_name -> github.com.avinoplan.postar.api.pkg.Status + 2, // 3: github.com.avinoplan.postar.api.postard.Postard.SendEmail:input_type -> github.com.avinoplan.postar.api.postard.SendEmailRequest + 3, // 4: github.com.avinoplan.postar.api.postard.Postard.SendEmail:output_type -> github.com.avinoplan.postar.api.postard.SendEmailResponse 4, // [4:5] is the sub-list for method output_type 3, // [3:4] is the sub-list for method input_type 3, // [3:3] is the sub-list for extension type_name diff --git a/api/postard/postard.proto b/api/postard/postard.proto index f54a976..87f3d86 100644 --- a/api/postard/postard.proto +++ b/api/postard/postard.proto @@ -6,10 +6,10 @@ // Email: fishgoddess@qq.com // Created at 2021/09/16 01:42:33 syntax = "proto3"; -package postard; +package github.com.avinoplan.postar.api.postard; import "status.proto"; -// protoc --proto_path=../pkg --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative postard.proto +// protoc -I=../pkg -I=. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative postard.proto option go_package = "github.com/avino-plan/postar/api/postard"; // Email wraps all information of using smtp service. diff --git a/api/postard/postard_grpc.pb.go b/api/postard/postard_grpc.pb.go index 8e2b601..846c27b 100644 --- a/api/postard/postard_grpc.pb.go +++ b/api/postard/postard_grpc.pb.go @@ -31,7 +31,7 @@ func NewPostardClient(cc grpc.ClientConnInterface) PostardClient { func (c *postardClient) SendEmail(ctx context.Context, in *SendEmailRequest, opts ...grpc.CallOption) (*SendEmailResponse, error) { out := new(SendEmailResponse) - err := c.cc.Invoke(ctx, "/postard.Postard/SendEmail", in, out, opts...) + err := c.cc.Invoke(ctx, "/github.com.avinoplan.postar.api.postard.Postard/SendEmail", in, out, opts...) if err != nil { return nil, err } @@ -76,7 +76,7 @@ func _Postard_SendEmail_Handler(srv interface{}, ctx context.Context, dec func(i } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/postard.Postard/SendEmail", + FullMethod: "/github.com.avinoplan.postar.api.postard.Postard/SendEmail", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(PostardServer).SendEmail(ctx, req.(*SendEmailRequest)) @@ -88,7 +88,7 @@ func _Postard_SendEmail_Handler(srv interface{}, ctx context.Context, dec func(i // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var Postard_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "postard.Postard", + ServiceName: "github.com.avinoplan.postar.api.postard.Postard", HandlerType: (*PostardServer)(nil), Methods: []grpc.MethodDesc{ { diff --git a/internal/postard/server/server.go b/internal/postard/server/server.go deleted file mode 100644 index bd90671..0000000 --- a/internal/postard/server/server.go +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/09/17 00:15:34 - -package server diff --git a/internal/postard/service/smtp.go b/internal/postard/service/smtp.go index a553641..3f9464e 100644 --- a/internal/postard/service/smtp.go +++ b/internal/postard/service/smtp.go @@ -10,7 +10,6 @@ package service import ( "context" - "time" "github.com/avino-plan/postar/internal/pkg/concurrency" "github.com/pkg/errors" @@ -19,21 +18,21 @@ import ( // SmtpServiceImpl is the service of smtp. type SmtpServiceImpl struct { + pool *concurrency.Pool // The pool of workers. host string // The host of smtp server. port int // The port of smtp server. user string // The user of smtp server. password string // The password of smtp server. - pool *concurrency.Pool // The pool of workers. } // NewSmtpService returns a new SmtpServer. -func NewSmtpService(host string, port int, user string, password string, pool *concurrency.Pool) SmtpService { +func NewSmtpService(pool *concurrency.Pool, host string, port int, user string, password string) SmtpService { return &SmtpServiceImpl{ + pool: pool, host: host, port: port, user: user, password: password, - pool: pool, } } @@ -48,24 +47,24 @@ func (ss *SmtpServiceImpl) sendEmail(email *Email) error { } // SendEmail sends email to somewhere. -func (ss *SmtpServiceImpl) SendEmail(ctx context.Context, email *Email, options *SendEmailOptions) error { +func (ss *SmtpServiceImpl) SendEmail(pCtx context.Context, email *Email, options *SendEmailOptions) error { if options == nil { options = DefaultSendEmailOptions() } - errorCh := ss.pool.Go(ctx, func(ctx context.Context) error { - return ss.sendEmail(email) - }) + ctx, cancel := context.WithTimeout(pCtx, options.Timeout) + defer cancel() + + errorCh := ss.pool.Go(ctx, func(ctx context.Context) error { return ss.sendEmail(email) }) if options.Async { return nil } - timer := time.NewTimer(options.Timeout) + var err error select { - case err := <-errorCh: - timer.Stop() - return errors.Wrap(err, "send email failed") - case <-timer.C: - return errors.New("send email timeout") + case err = <-errorCh: + case <-ctx.Done(): + err = ctx.Err() } + return errors.Wrap(err, "send email failed") } diff --git a/internal/postard/service/smtp_test.go b/internal/postard/service/smtp_test.go index 16c5b6f..e4a2615 100644 --- a/internal/postard/service/smtp_test.go +++ b/internal/postard/service/smtp_test.go @@ -36,7 +36,7 @@ func TestNewSmtpService(t *testing.T) { pool := concurrency.NewPool().Start() defer pool.Stop() - smtpService := NewSmtpService(host, int(port), user, password, pool) + smtpService := NewSmtpService(pool, host, int(port), user, password) err = smtpService.SendEmail(context.Background(), &Email{ To: []string{to}, Subject: t.Name(), From 72a4a3d381bb559b2fc81893e00f6d2790efea10 Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Sat, 25 Sep 2021 23:48:30 +0800 Subject: [PATCH 07/27] =?UTF-8?q?=E5=AE=8C=E5=96=84=20trace?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/pkg/status.pb.go | 163 ------------------------- api/pkg/status.proto | 18 --- api/postard/postard.pb.go | 108 ++++++++-------- api/postard/postard.proto | 9 +- go.mod | 1 - internal/pkg/trace/encode.go | 57 +++++++++ internal/pkg/trace/encode_test.go | 36 ++++++ internal/pkg/trace/trace.go | 35 ++++++ internal/pkg/trace/trace_test.go | 41 +++++++ internal/postard/server/grpc.go | 33 +++++ internal/postard/service/error.go | 26 ++++ internal/postard/service/error_test.go | 50 ++++++++ internal/postard/service/smtp.go | 14 ++- 13 files changed, 342 insertions(+), 249 deletions(-) delete mode 100644 api/pkg/status.pb.go delete mode 100644 api/pkg/status.proto create mode 100644 internal/pkg/trace/encode.go create mode 100644 internal/pkg/trace/encode_test.go create mode 100644 internal/pkg/trace/trace.go create mode 100644 internal/pkg/trace/trace_test.go create mode 100644 internal/postard/service/error.go create mode 100644 internal/postard/service/error_test.go diff --git a/api/pkg/status.pb.go b/api/pkg/status.pb.go deleted file mode 100644 index 9564874..0000000 --- a/api/pkg/status.pb.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/09/22 01:18:01 - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.27.1 -// protoc v3.18.0 -// source: status.proto - -package pkg - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// Status is the status of responses. -type Status struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Code int64 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` // Globally unique. - Msg string `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"` // The description of responses. -} - -func (x *Status) Reset() { - *x = Status{} - if protoimpl.UnsafeEnabled { - mi := &file_status_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Status) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Status) ProtoMessage() {} - -func (x *Status) ProtoReflect() protoreflect.Message { - mi := &file_status_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Status.ProtoReflect.Descriptor instead. -func (*Status) Descriptor() ([]byte, []int) { - return file_status_proto_rawDescGZIP(), []int{0} -} - -func (x *Status) GetCode() int64 { - if x != nil { - return x.Code - } - return 0 -} - -func (x *Status) GetMsg() string { - if x != nil { - return x.Msg - } - return "" -} - -var File_status_proto protoreflect.FileDescriptor - -var file_status_proto_rawDesc = []byte{ - 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x23, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, - 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x70, 0x6b, 0x67, 0x22, 0x2e, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, - 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x63, 0x6f, 0x64, - 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6d, 0x73, 0x67, 0x42, 0x26, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x2d, 0x70, 0x6c, 0x61, 0x6e, 0x2f, 0x70, 0x6f, 0x73, - 0x74, 0x61, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6b, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, -} - -var ( - file_status_proto_rawDescOnce sync.Once - file_status_proto_rawDescData = file_status_proto_rawDesc -) - -func file_status_proto_rawDescGZIP() []byte { - file_status_proto_rawDescOnce.Do(func() { - file_status_proto_rawDescData = protoimpl.X.CompressGZIP(file_status_proto_rawDescData) - }) - return file_status_proto_rawDescData -} - -var file_status_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_status_proto_goTypes = []interface{}{ - (*Status)(nil), // 0: github.com.avinoplan.postar.api.pkg.Status -} -var file_status_proto_depIdxs = []int32{ - 0, // [0:0] is the sub-list for method output_type - 0, // [0:0] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_status_proto_init() } -func file_status_proto_init() { - if File_status_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_status_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Status); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_status_proto_rawDesc, - NumEnums: 0, - NumMessages: 1, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_status_proto_goTypes, - DependencyIndexes: file_status_proto_depIdxs, - MessageInfos: file_status_proto_msgTypes, - }.Build() - File_status_proto = out.File - file_status_proto_rawDesc = nil - file_status_proto_goTypes = nil - file_status_proto_depIdxs = nil -} diff --git a/api/pkg/status.proto b/api/pkg/status.proto deleted file mode 100644 index 479e805..0000000 --- a/api/pkg/status.proto +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/09/22 01:18:01 -syntax = "proto3"; -package github.com.avinoplan.postar.api.pkg; - -// protoc -I=. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative status.proto -option go_package = "github.com/avino-plan/postar/api/pkg"; - -// Status is the status of responses. -message Status { - int64 code = 1; // Globally unique. - string msg = 2; // The description of responses. -} \ No newline at end of file diff --git a/api/postard/postard.pb.go b/api/postard/postard.pb.go index 45db3f5..a7cfefa 100644 --- a/api/postard/postard.pb.go +++ b/api/postard/postard.pb.go @@ -15,7 +15,6 @@ package postard import ( - pkg "github.com/avino-plan/postar/api/pkg" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -37,8 +36,8 @@ type Email struct { To []string `protobuf:"bytes,1,rep,name=to,proto3" json:"to,omitempty"` // The receivers of one email. Subject string `protobuf:"bytes,2,opt,name=subject,proto3" json:"subject,omitempty"` // The subject of one email. - BodyType string `protobuf:"bytes,3,opt,name=BodyType,proto3" json:"BodyType,omitempty"` // The content type of body. - Body string `protobuf:"bytes,4,opt,name=Body,proto3" json:"Body,omitempty"` // The body of one email. + BodyType string `protobuf:"bytes,3,opt,name=bodyType,proto3" json:"bodyType,omitempty"` // The content type of body. + Body string `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"` // The body of one email. } func (x *Email) Reset() { @@ -219,7 +218,7 @@ type SendEmailResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Status *pkg.Status `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` // The status of response. + TraceId string `protobuf:"bytes,1,opt,name=traceId,proto3" json:"traceId,omitempty"` // For tracing. } func (x *SendEmailResponse) Reset() { @@ -254,11 +253,11 @@ func (*SendEmailResponse) Descriptor() ([]byte, []int) { return file_postard_proto_rawDescGZIP(), []int{3} } -func (x *SendEmailResponse) GetStatus() *pkg.Status { +func (x *SendEmailResponse) GetTraceId() string { if x != nil { - return x.Status + return x.TraceId } - return nil + return "" } var File_postard_proto protoreflect.FileDescriptor @@ -267,47 +266,44 @@ var file_postard_proto_rawDesc = []byte{ 0x0a, 0x0d, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x1a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x61, 0x0a, 0x05, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, - 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, 0x12, - 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x42, 0x6f, 0x64, - 0x79, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x42, 0x6f, 0x64, - 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x42, 0x6f, 0x64, 0x79, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x42, 0x6f, 0x64, 0x79, 0x22, 0x42, 0x0a, 0x10, 0x53, 0x65, 0x6e, - 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x14, 0x0a, - 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x73, - 0x79, 0x6e, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, 0xad, 0x01, - 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x44, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x2e, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, - 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x61, 0x69, - 0x6c, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x53, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, - 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x73, 0x74, - 0x61, 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x58, 0x0a, - 0x11, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x43, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, - 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x32, 0x8e, 0x01, 0x0a, 0x07, 0x50, 0x6f, 0x73, 0x74, - 0x61, 0x72, 0x64, 0x12, 0x82, 0x01, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, - 0x6c, 0x12, 0x39, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, - 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, - 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, 0x2e, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, - 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, - 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x2d, 0x70, 0x6c, 0x61, - 0x6e, 0x2f, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6f, 0x73, - 0x74, 0x61, 0x72, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x22, 0x61, 0x0a, 0x05, 0x45, 0x6d, 0x61, 0x69, + 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x02, 0x74, + 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x62, + 0x6f, 0x64, 0x79, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, + 0x6f, 0x64, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x42, 0x0a, 0x10, 0x53, + 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x14, 0x0a, 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, + 0x61, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, + 0xad, 0x01, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x45, 0x6d, + 0x61, 0x69, 0x6c, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x53, 0x0a, 0x07, 0x6f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, + 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, + 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, + 0x2d, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x32, 0x8e, + 0x01, 0x0a, 0x07, 0x50, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x12, 0x82, 0x01, 0x0a, 0x09, 0x53, + 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x39, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, + 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, + 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x3a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x53, 0x65, + 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, + 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, + 0x69, 0x6e, 0x6f, 0x2d, 0x70, 0x6c, 0x61, 0x6e, 0x2f, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -328,19 +324,17 @@ var file_postard_proto_goTypes = []interface{}{ (*SendEmailOptions)(nil), // 1: github.com.avinoplan.postar.api.postard.SendEmailOptions (*SendEmailRequest)(nil), // 2: github.com.avinoplan.postar.api.postard.SendEmailRequest (*SendEmailResponse)(nil), // 3: github.com.avinoplan.postar.api.postard.SendEmailResponse - (*pkg.Status)(nil), // 4: github.com.avinoplan.postar.api.pkg.Status } var file_postard_proto_depIdxs = []int32{ 0, // 0: github.com.avinoplan.postar.api.postard.SendEmailRequest.email:type_name -> github.com.avinoplan.postar.api.postard.Email 1, // 1: github.com.avinoplan.postar.api.postard.SendEmailRequest.options:type_name -> github.com.avinoplan.postar.api.postard.SendEmailOptions - 4, // 2: github.com.avinoplan.postar.api.postard.SendEmailResponse.status:type_name -> github.com.avinoplan.postar.api.pkg.Status - 2, // 3: github.com.avinoplan.postar.api.postard.Postard.SendEmail:input_type -> github.com.avinoplan.postar.api.postard.SendEmailRequest - 3, // 4: github.com.avinoplan.postar.api.postard.Postard.SendEmail:output_type -> github.com.avinoplan.postar.api.postard.SendEmailResponse - 4, // [4:5] is the sub-list for method output_type - 3, // [3:4] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name + 2, // 2: github.com.avinoplan.postar.api.postard.Postard.SendEmail:input_type -> github.com.avinoplan.postar.api.postard.SendEmailRequest + 3, // 3: github.com.avinoplan.postar.api.postard.Postard.SendEmail:output_type -> github.com.avinoplan.postar.api.postard.SendEmailResponse + 3, // [3:4] is the sub-list for method output_type + 2, // [2:3] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_postard_proto_init() } diff --git a/api/postard/postard.proto b/api/postard/postard.proto index 87f3d86..adcbe8a 100644 --- a/api/postard/postard.proto +++ b/api/postard/postard.proto @@ -7,17 +7,16 @@ // Created at 2021/09/16 01:42:33 syntax = "proto3"; package github.com.avinoplan.postar.api.postard; -import "status.proto"; -// protoc -I=../pkg -I=. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative postard.proto +// protoc -I=. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative postard.proto option go_package = "github.com/avino-plan/postar/api/postard"; // Email wraps all information of using smtp service. message Email { repeated string to = 1; // The receivers of one email. string subject = 2; // The subject of one email. - string BodyType = 3; // The content type of body. - string Body = 4; // The body of one email. + string bodyType = 3; // The content type of body. + string body = 4; // The body of one email. } // SendEmailOptions is the options of sending emails. @@ -34,7 +33,7 @@ message SendEmailRequest { // SendEmailResponse is the response of SendEmail. message SendEmailResponse { - pkg.Status status = 1; // The status of response. + string traceId = 1; // For tracing. } // Postard is the core service of postar. diff --git a/go.mod b/go.mod index a8cb922..b02f1cb 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/avino-plan/postar go 1.15 require ( - github.com/pkg/errors v0.9.1 google.golang.org/grpc v1.40.0 google.golang.org/protobuf v1.27.1 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect diff --git a/internal/pkg/trace/encode.go b/internal/pkg/trace/encode.go new file mode 100644 index 0000000..8c14333 --- /dev/null +++ b/internal/pkg/trace/encode.go @@ -0,0 +1,57 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/25 22:39:17 + +package trace + +import ( + "encoding/binary" + "fmt" + "math/rand" + "os" + "time" +) + +var ( + // letters includes 0-9 a-z A-Z. + letters = [62]byte{ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + } + + // random 用于生成随机数 + random = rand.New(rand.NewSource(time.Now().Unix())) + + // pid 是程序的 pid + pid = uint64(os.Getpid()) +) + +// TimeHex returns now time in hex. +func TimeHex() string { + b := make([]byte, 8) + binary.BigEndian.PutUint64(b, uint64(time.Now().Unix())) + return fmt.Sprintf("%x", b[4:]) +} + +// PidHex returns pid in hex. +func PidHex() string { + b := make([]byte, 8) + binary.BigEndian.PutUint64(b, pid) + return fmt.Sprintf("%x", b[4:]) +} + +// RandomString returns a string including 0-9/a-z/A-Z not longer than length. +func RandomString(length int) string { + b := make([]byte, length) + for i := 0; i < length; i++ { + b[i] = letters[random.Intn(62)] + } + return string(b) +} diff --git a/internal/pkg/trace/encode_test.go b/internal/pkg/trace/encode_test.go new file mode 100644 index 0000000..6cebf36 --- /dev/null +++ b/internal/pkg/trace/encode_test.go @@ -0,0 +1,36 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/25 22:58:39 + +package trace + +import "testing" + +// go test -v -cover -run=^TestTimeHex$ +func TestTimeHex(t *testing.T) { + timeHex := TimeHex() + if len(timeHex) != 8 { + t.Errorf("length of TimeHex is wrong with %s, %d", timeHex, len(timeHex)) + } +} + +// go test -v -cover -run=^TestPidHex$ +func TestPidHex(t *testing.T) { + pidHex := PidHex() + if len(pidHex) != 8 { + t.Errorf("length of PidHex is wrong with %s, %d", pidHex, len(pidHex)) + } +} + +// go test -v -cover -run=^TestRandomString$ +func TestRandomString(t *testing.T) { + length := 16 + str := RandomString(length) + if len(str) != length { + t.Errorf("length of RandomString is wrong with %d", len(str)) + } +} diff --git a/internal/pkg/trace/trace.go b/internal/pkg/trace/trace.go new file mode 100644 index 0000000..1f82838 --- /dev/null +++ b/internal/pkg/trace/trace.go @@ -0,0 +1,35 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/25 22:44:59 + +package trace + +import "context" + +var ( + traceIdKey struct{} // The key of trace id in context. +) + +// WithContext returns a context with a trace id inside. +func WithContext(ctx context.Context) context.Context { + traceId := RandomString(4) + TimeHex() + PidHex() + RandomString(4) + return context.WithValue(ctx, traceIdKey, traceId) +} + +// FromContext gets the trace id from context. +func FromContext(ctx context.Context) string { + value := ctx.Value(traceIdKey) + if value == nil { + return "" + } + + traceId, ok := value.(string) + if !ok { + return "" + } + return traceId +} diff --git a/internal/pkg/trace/trace_test.go b/internal/pkg/trace/trace_test.go new file mode 100644 index 0000000..2675c6a --- /dev/null +++ b/internal/pkg/trace/trace_test.go @@ -0,0 +1,41 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/25 23:07:54 + +package trace + +import ( + "context" + "testing" +) + +// go test -v -cover -run=^TestWithContext$ +func TestWithContext(t *testing.T) { + ctx := WithContext(context.Background()) + + value := ctx.Value(traceIdKey) + if value == nil { + t.Error("ctx.Value returns nil") + } + + traceId, ok := value.(string) + if !ok { + t.Errorf("value %+v isn't string", value) + } + t.Log("traceId:", traceId) +} + +// go test -v -cover -run=^TestFromContext$ +func TestFromContext(t *testing.T) { + traceId := RandomString(4) + TimeHex() + PidHex() + RandomString(4) + ctx := context.WithValue(context.Background(), traceIdKey, traceId) + + traceIdInCtx := FromContext(ctx) + if traceIdInCtx != traceId { + t.Errorf("traceIdInCtx %s != traceId %s", traceIdInCtx, traceId) + } +} diff --git a/internal/postard/server/grpc.go b/internal/postard/server/grpc.go index 217f6b5..969878f 100644 --- a/internal/postard/server/grpc.go +++ b/internal/postard/server/grpc.go @@ -7,3 +7,36 @@ // Created at 2021/09/16 02:05:02 package server + +import ( + "context" + + "github.com/avino-plan/postar/api/postard" + "github.com/avino-plan/postar/internal/pkg/trace" + "github.com/avino-plan/postar/internal/postard/service" +) + +type PostardServerGrpcImpl struct { + postard.UnimplementedPostardServer + service service.SmtpService +} + +func NewPostardServerGrpcImpl(service service.SmtpService) *PostardServerGrpcImpl { + return &PostardServerGrpcImpl{ + service: service, + } +} + +func (psgi *PostardServerGrpcImpl) SendEmail(pCtx context.Context, request *postard.SendEmailRequest) (*postard.SendEmailResponse, error) { + ctx := trace.WithContext(pCtx) + + err := psgi.service.SendEmail(ctx, nil, nil) + if service.IsSendTimeout(err) { + + } + + if err != nil { + + } + return &postard.SendEmailResponse{TraceId: trace.FromContext(ctx)}, nil +} diff --git a/internal/postard/service/error.go b/internal/postard/service/error.go new file mode 100644 index 0000000..ee1d22f --- /dev/null +++ b/internal/postard/service/error.go @@ -0,0 +1,26 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/25 21:58:02 + +package service + +import "errors" + +var ( + errSendTimeout = errors.New("postard/service: send timeout") // Send timeout + errSendEmailFailed = errors.New("postard/service: send email failed") // Send email failed +) + +// IsSendTimeout returns if err equals to errSendTimeout. +func IsSendTimeout(err error) bool { + return errors.Is(err, errSendTimeout) +} + +// IsSendEmailFailed returns if err equals to errSendEmailFailed. +func IsSendEmailFailed(err error) bool { + return errors.Is(err, errSendEmailFailed) +} diff --git a/internal/postard/service/error_test.go b/internal/postard/service/error_test.go new file mode 100644 index 0000000..ddcfbe9 --- /dev/null +++ b/internal/postard/service/error_test.go @@ -0,0 +1,50 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/25 22:06:36 + +package service + +import ( + "errors" + "testing" +) + +// go test -v -cover -run=^TestIsSendTimeout$ +func TestIsSendTimeout(t *testing.T) { + testCases := []struct { + err error + result bool + }{ + {errSendTimeout, true}, + {errSendEmailFailed, false}, + {errors.New("unknown error"), false}, + } + + for i, testCase := range testCases { + if IsSendTimeout(testCase.err) != testCase.result { + t.Errorf("testCase %d failed with err %+v, result %+v", i, testCase.err, testCase.result) + } + } +} + +// go test -v -cover -run=^TestIsSendEmailFailed$ +func TestIsSendEmailFailed(t *testing.T) { + testCases := []struct { + err error + result bool + }{ + {errSendTimeout, false}, + {errSendEmailFailed, true}, + {errors.New("unknown error"), false}, + } + + for i, testCase := range testCases { + if IsSendEmailFailed(testCase.err) != testCase.result { + t.Errorf("testCase %d failed with err %+v, result %+v", i, testCase.err, testCase.result) + } + } +} diff --git a/internal/postard/service/smtp.go b/internal/postard/service/smtp.go index 3f9464e..63b12cb 100644 --- a/internal/postard/service/smtp.go +++ b/internal/postard/service/smtp.go @@ -12,7 +12,6 @@ import ( "context" "github.com/avino-plan/postar/internal/pkg/concurrency" - "github.com/pkg/errors" "gopkg.in/gomail.v2" ) @@ -60,11 +59,16 @@ func (ss *SmtpServiceImpl) SendEmail(pCtx context.Context, email *Email, options return nil } - var err error select { - case err = <-errorCh: + case e := <-errorCh: + if e != nil { + return errSendEmailFailed + } case <-ctx.Done(): - err = ctx.Err() + e := ctx.Err() + if e != nil { + return errSendTimeout + } } - return errors.Wrap(err, "send email failed") + return nil } From 2c794fc046d22c311f422d728800cec8938276f5 Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Tue, 28 Sep 2021 00:01:01 +0800 Subject: [PATCH 08/27] =?UTF-8?q?=E5=AE=8C=E5=96=84=20GRPC=20=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/postard/postard.pb.go | 321 +++++++++++++++-------- api/postard/postard.proto | 24 +- api/postard/postard_grpc.pb.go | 10 +- cmd/postard/main.go | 25 ++ go.mod | 1 + go.sum | 2 + internal/postard/server/grpc.go | 55 +++- internal/postard/service/context.go | 32 +++ internal/postard/service/context_test.go | 35 +++ internal/postard/service/service.go | 18 +- internal/postard/service/service_test.go | 5 + internal/postard/service/smtp.go | 20 +- 12 files changed, 400 insertions(+), 148 deletions(-) create mode 100644 internal/postard/service/context.go create mode 100644 internal/postard/service/context_test.go diff --git a/api/postard/postard.pb.go b/api/postard/postard.pb.go index a7cfefa..d6ce25b 100644 --- a/api/postard/postard.pb.go +++ b/api/postard/postard.pb.go @@ -17,6 +17,7 @@ package postard import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" ) @@ -28,6 +29,128 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// ResponseCodes is all codes of response. +type ResponseCodes int32 + +const ( + ResponseCodes_OK ResponseCodes = 0 + ResponseCodes_InternalServerError ResponseCodes = 50000 + ResponseCodes_TimeoutError ResponseCodes = 50001 +) + +// Enum value maps for ResponseCodes. +var ( + ResponseCodes_name = map[int32]string{ + 0: "OK", + 50000: "InternalServerError", + 50001: "TimeoutError", + } + ResponseCodes_value = map[string]int32{ + "OK": 0, + "InternalServerError": 50000, + "TimeoutError": 50001, + } +) + +func (x ResponseCodes) Enum() *ResponseCodes { + p := new(ResponseCodes) + *p = x + return p +} + +func (x ResponseCodes) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ResponseCodes) Descriptor() protoreflect.EnumDescriptor { + return file_postard_proto_enumTypes[0].Descriptor() +} + +func (ResponseCodes) Type() protoreflect.EnumType { + return &file_postard_proto_enumTypes[0] +} + +func (x ResponseCodes) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ResponseCodes.Descriptor instead. +func (ResponseCodes) EnumDescriptor() ([]byte, []int) { + return file_postard_proto_rawDescGZIP(), []int{0} +} + +// PostardResponse is the response of Postard. +type PostardResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Code ResponseCodes `protobuf:"varint,1,opt,name=code,proto3,enum=github.com.avinoplan.postar.api.postard.ResponseCodes" json:"code,omitempty"` // 0 is ok. + Msg string `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"` // For messaging. + TraceId string `protobuf:"bytes,3,opt,name=traceId,proto3" json:"traceId,omitempty"` // For tracing. + Data *anypb.Any `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` // Any data. +} + +func (x *PostardResponse) Reset() { + *x = PostardResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_postard_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PostardResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PostardResponse) ProtoMessage() {} + +func (x *PostardResponse) ProtoReflect() protoreflect.Message { + mi := &file_postard_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PostardResponse.ProtoReflect.Descriptor instead. +func (*PostardResponse) Descriptor() ([]byte, []int) { + return file_postard_proto_rawDescGZIP(), []int{0} +} + +func (x *PostardResponse) GetCode() ResponseCodes { + if x != nil { + return x.Code + } + return ResponseCodes_OK +} + +func (x *PostardResponse) GetMsg() string { + if x != nil { + return x.Msg + } + return "" +} + +func (x *PostardResponse) GetTraceId() string { + if x != nil { + return x.TraceId + } + return "" +} + +func (x *PostardResponse) GetData() *anypb.Any { + if x != nil { + return x.Data + } + return nil +} + // Email wraps all information of using smtp service. type Email struct { state protoimpl.MessageState @@ -43,7 +166,7 @@ type Email struct { func (x *Email) Reset() { *x = Email{} if protoimpl.UnsafeEnabled { - mi := &file_postard_proto_msgTypes[0] + mi := &file_postard_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -56,7 +179,7 @@ func (x *Email) String() string { func (*Email) ProtoMessage() {} func (x *Email) ProtoReflect() protoreflect.Message { - mi := &file_postard_proto_msgTypes[0] + mi := &file_postard_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -69,7 +192,7 @@ func (x *Email) ProtoReflect() protoreflect.Message { // Deprecated: Use Email.ProtoReflect.Descriptor instead. func (*Email) Descriptor() ([]byte, []int) { - return file_postard_proto_rawDescGZIP(), []int{0} + return file_postard_proto_rawDescGZIP(), []int{1} } func (x *Email) GetTo() []string { @@ -113,7 +236,7 @@ type SendEmailOptions struct { func (x *SendEmailOptions) Reset() { *x = SendEmailOptions{} if protoimpl.UnsafeEnabled { - mi := &file_postard_proto_msgTypes[1] + mi := &file_postard_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -126,7 +249,7 @@ func (x *SendEmailOptions) String() string { func (*SendEmailOptions) ProtoMessage() {} func (x *SendEmailOptions) ProtoReflect() protoreflect.Message { - mi := &file_postard_proto_msgTypes[1] + mi := &file_postard_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -139,7 +262,7 @@ func (x *SendEmailOptions) ProtoReflect() protoreflect.Message { // Deprecated: Use SendEmailOptions.ProtoReflect.Descriptor instead. func (*SendEmailOptions) Descriptor() ([]byte, []int) { - return file_postard_proto_rawDescGZIP(), []int{1} + return file_postard_proto_rawDescGZIP(), []int{2} } func (x *SendEmailOptions) GetAsync() bool { @@ -169,7 +292,7 @@ type SendEmailRequest struct { func (x *SendEmailRequest) Reset() { *x = SendEmailRequest{} if protoimpl.UnsafeEnabled { - mi := &file_postard_proto_msgTypes[2] + mi := &file_postard_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -182,7 +305,7 @@ func (x *SendEmailRequest) String() string { func (*SendEmailRequest) ProtoMessage() {} func (x *SendEmailRequest) ProtoReflect() protoreflect.Message { - mi := &file_postard_proto_msgTypes[2] + mi := &file_postard_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -195,7 +318,7 @@ func (x *SendEmailRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SendEmailRequest.ProtoReflect.Descriptor instead. func (*SendEmailRequest) Descriptor() ([]byte, []int) { - return file_postard_proto_rawDescGZIP(), []int{2} + return file_postard_proto_rawDescGZIP(), []int{3} } func (x *SendEmailRequest) GetEmail() *Email { @@ -212,98 +335,64 @@ func (x *SendEmailRequest) GetOptions() *SendEmailOptions { return nil } -// SendEmailResponse is the response of SendEmail. -type SendEmailResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - TraceId string `protobuf:"bytes,1,opt,name=traceId,proto3" json:"traceId,omitempty"` // For tracing. -} - -func (x *SendEmailResponse) Reset() { - *x = SendEmailResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_postard_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SendEmailResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SendEmailResponse) ProtoMessage() {} - -func (x *SendEmailResponse) ProtoReflect() protoreflect.Message { - mi := &file_postard_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SendEmailResponse.ProtoReflect.Descriptor instead. -func (*SendEmailResponse) Descriptor() ([]byte, []int) { - return file_postard_proto_rawDescGZIP(), []int{3} -} - -func (x *SendEmailResponse) GetTraceId() string { - if x != nil { - return x.TraceId - } - return "" -} - var File_postard_proto protoreflect.FileDescriptor var file_postard_proto_rawDesc = []byte{ 0x0a, 0x0d, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x22, 0x61, 0x0a, 0x05, 0x45, 0x6d, 0x61, 0x69, - 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x02, 0x74, - 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x62, - 0x6f, 0x64, 0x79, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, - 0x6f, 0x64, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x42, 0x0a, 0x10, 0x53, - 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, - 0x14, 0x0a, 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, - 0x61, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, - 0xad, 0x01, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, - 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x45, 0x6d, - 0x61, 0x69, 0x6c, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x53, 0x0a, 0x07, 0x6f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, - 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, - 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, - 0x2d, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x32, 0x8e, - 0x01, 0x0a, 0x07, 0x50, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x12, 0x82, 0x01, 0x0a, 0x09, 0x53, - 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x39, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, - 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, - 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x3a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, - 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x53, 0x65, - 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, - 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, - 0x69, 0x6e, 0x6f, 0x2d, 0x70, 0x6c, 0x61, 0x6e, 0x2f, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x22, 0xb3, 0x01, 0x0a, 0x0f, 0x50, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x36, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, + 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x52, 0x04, 0x63, + 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, + 0x28, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x41, 0x6e, 0x79, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x61, 0x0a, 0x05, 0x45, 0x6d, 0x61, + 0x69, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x02, + 0x74, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x62, 0x6f, 0x64, 0x79, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x62, 0x6f, 0x64, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x42, 0x0a, 0x10, + 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x22, 0xad, 0x01, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, + 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x45, + 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x53, 0x0a, 0x07, 0x6f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, + 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, + 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, + 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x2a, 0x46, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x64, 0x65, + 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x13, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x10, 0xd0, 0x86, 0x03, 0x12, 0x12, 0x0a, 0x0c, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x45, + 0x72, 0x72, 0x6f, 0x72, 0x10, 0xd1, 0x86, 0x03, 0x32, 0x8c, 0x01, 0x0a, 0x07, 0x50, 0x6f, 0x73, + 0x74, 0x61, 0x72, 0x64, 0x12, 0x80, 0x01, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, + 0x69, 0x6c, 0x12, 0x39, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, + 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, + 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, + 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x2d, 0x70, 0x6c, 0x61, 0x6e, + 0x2f, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6f, 0x73, 0x74, + 0x61, 0x72, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -318,23 +407,28 @@ func file_postard_proto_rawDescGZIP() []byte { return file_postard_proto_rawDescData } +var file_postard_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_postard_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_postard_proto_goTypes = []interface{}{ - (*Email)(nil), // 0: github.com.avinoplan.postar.api.postard.Email - (*SendEmailOptions)(nil), // 1: github.com.avinoplan.postar.api.postard.SendEmailOptions - (*SendEmailRequest)(nil), // 2: github.com.avinoplan.postar.api.postard.SendEmailRequest - (*SendEmailResponse)(nil), // 3: github.com.avinoplan.postar.api.postard.SendEmailResponse + (ResponseCodes)(0), // 0: github.com.avinoplan.postar.api.postard.ResponseCodes + (*PostardResponse)(nil), // 1: github.com.avinoplan.postar.api.postard.PostardResponse + (*Email)(nil), // 2: github.com.avinoplan.postar.api.postard.Email + (*SendEmailOptions)(nil), // 3: github.com.avinoplan.postar.api.postard.SendEmailOptions + (*SendEmailRequest)(nil), // 4: github.com.avinoplan.postar.api.postard.SendEmailRequest + (*anypb.Any)(nil), // 5: google.protobuf.Any } var file_postard_proto_depIdxs = []int32{ - 0, // 0: github.com.avinoplan.postar.api.postard.SendEmailRequest.email:type_name -> github.com.avinoplan.postar.api.postard.Email - 1, // 1: github.com.avinoplan.postar.api.postard.SendEmailRequest.options:type_name -> github.com.avinoplan.postar.api.postard.SendEmailOptions - 2, // 2: github.com.avinoplan.postar.api.postard.Postard.SendEmail:input_type -> github.com.avinoplan.postar.api.postard.SendEmailRequest - 3, // 3: github.com.avinoplan.postar.api.postard.Postard.SendEmail:output_type -> github.com.avinoplan.postar.api.postard.SendEmailResponse - 3, // [3:4] is the sub-list for method output_type - 2, // [2:3] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 0, // 0: github.com.avinoplan.postar.api.postard.PostardResponse.code:type_name -> github.com.avinoplan.postar.api.postard.ResponseCodes + 5, // 1: github.com.avinoplan.postar.api.postard.PostardResponse.data:type_name -> google.protobuf.Any + 2, // 2: github.com.avinoplan.postar.api.postard.SendEmailRequest.email:type_name -> github.com.avinoplan.postar.api.postard.Email + 3, // 3: github.com.avinoplan.postar.api.postard.SendEmailRequest.options:type_name -> github.com.avinoplan.postar.api.postard.SendEmailOptions + 4, // 4: github.com.avinoplan.postar.api.postard.Postard.SendEmail:input_type -> github.com.avinoplan.postar.api.postard.SendEmailRequest + 1, // 5: github.com.avinoplan.postar.api.postard.Postard.SendEmail:output_type -> github.com.avinoplan.postar.api.postard.PostardResponse + 5, // [5:6] is the sub-list for method output_type + 4, // [4:5] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name } func init() { file_postard_proto_init() } @@ -344,7 +438,7 @@ func file_postard_proto_init() { } if !protoimpl.UnsafeEnabled { file_postard_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Email); i { + switch v := v.(*PostardResponse); i { case 0: return &v.state case 1: @@ -356,7 +450,7 @@ func file_postard_proto_init() { } } file_postard_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SendEmailOptions); i { + switch v := v.(*Email); i { case 0: return &v.state case 1: @@ -368,7 +462,7 @@ func file_postard_proto_init() { } } file_postard_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SendEmailRequest); i { + switch v := v.(*SendEmailOptions); i { case 0: return &v.state case 1: @@ -380,7 +474,7 @@ func file_postard_proto_init() { } } file_postard_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SendEmailResponse); i { + switch v := v.(*SendEmailRequest); i { case 0: return &v.state case 1: @@ -397,13 +491,14 @@ func file_postard_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_postard_proto_rawDesc, - NumEnums: 0, + NumEnums: 1, NumMessages: 4, NumExtensions: 0, NumServices: 1, }, GoTypes: file_postard_proto_goTypes, DependencyIndexes: file_postard_proto_depIdxs, + EnumInfos: file_postard_proto_enumTypes, MessageInfos: file_postard_proto_msgTypes, }.Build() File_postard_proto = out.File diff --git a/api/postard/postard.proto b/api/postard/postard.proto index adcbe8a..7159e48 100644 --- a/api/postard/postard.proto +++ b/api/postard/postard.proto @@ -11,6 +11,23 @@ package github.com.avinoplan.postar.api.postard; // protoc -I=. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative postard.proto option go_package = "github.com/avino-plan/postar/api/postard"; +import "google/protobuf/any.proto"; + +// ResponseCodes is all codes of response. +enum ResponseCodes { + OK = 0; + InternalServerError = 50000; + TimeoutError = 50001; +} + +// PostardResponse is the response of Postard. +message PostardResponse { + ResponseCodes code = 1; // 0 is ok. + string msg = 2; // For messaging. + string traceId = 3; // For tracing. + google.protobuf.Any data = 4; // Any data. +} + // Email wraps all information of using smtp service. message Email { repeated string to = 1; // The receivers of one email. @@ -31,12 +48,7 @@ message SendEmailRequest { SendEmailOptions options = 2; // Sending options. } -// SendEmailResponse is the response of SendEmail. -message SendEmailResponse { - string traceId = 1; // For tracing. -} - // Postard is the core service of postar. service Postard { - rpc SendEmail(SendEmailRequest) returns (SendEmailResponse); // For sending emails. + rpc SendEmail(SendEmailRequest) returns (PostardResponse); // For sending emails. } \ No newline at end of file diff --git a/api/postard/postard_grpc.pb.go b/api/postard/postard_grpc.pb.go index 846c27b..235c9ae 100644 --- a/api/postard/postard_grpc.pb.go +++ b/api/postard/postard_grpc.pb.go @@ -18,7 +18,7 @@ const _ = grpc.SupportPackageIsVersion7 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type PostardClient interface { - SendEmail(ctx context.Context, in *SendEmailRequest, opts ...grpc.CallOption) (*SendEmailResponse, error) + SendEmail(ctx context.Context, in *SendEmailRequest, opts ...grpc.CallOption) (*PostardResponse, error) } type postardClient struct { @@ -29,8 +29,8 @@ func NewPostardClient(cc grpc.ClientConnInterface) PostardClient { return &postardClient{cc} } -func (c *postardClient) SendEmail(ctx context.Context, in *SendEmailRequest, opts ...grpc.CallOption) (*SendEmailResponse, error) { - out := new(SendEmailResponse) +func (c *postardClient) SendEmail(ctx context.Context, in *SendEmailRequest, opts ...grpc.CallOption) (*PostardResponse, error) { + out := new(PostardResponse) err := c.cc.Invoke(ctx, "/github.com.avinoplan.postar.api.postard.Postard/SendEmail", in, out, opts...) if err != nil { return nil, err @@ -42,7 +42,7 @@ func (c *postardClient) SendEmail(ctx context.Context, in *SendEmailRequest, opt // All implementations must embed UnimplementedPostardServer // for forward compatibility type PostardServer interface { - SendEmail(context.Context, *SendEmailRequest) (*SendEmailResponse, error) + SendEmail(context.Context, *SendEmailRequest) (*PostardResponse, error) mustEmbedUnimplementedPostardServer() } @@ -50,7 +50,7 @@ type PostardServer interface { type UnimplementedPostardServer struct { } -func (UnimplementedPostardServer) SendEmail(context.Context, *SendEmailRequest) (*SendEmailResponse, error) { +func (UnimplementedPostardServer) SendEmail(context.Context, *SendEmailRequest) (*PostardResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SendEmail not implemented") } func (UnimplementedPostardServer) mustEmbedUnimplementedPostardServer() {} diff --git a/cmd/postard/main.go b/cmd/postard/main.go index 5688188..2155d67 100644 --- a/cmd/postard/main.go +++ b/cmd/postard/main.go @@ -8,6 +8,31 @@ package main +import ( + "net" + + "github.com/FishGoddess/logit" + "github.com/avino-plan/postar/internal/pkg/concurrency" + "github.com/avino-plan/postar/internal/postard/server" + "github.com/avino-plan/postar/internal/postard/service" +) + func main() { + logger := logit.NewLogger() + contextService := service.NewContextService(logger) + + pool := concurrency.NewPool() + smtpService := service.NewSmtpService(pool, "", 0, "", "") + + svr := server.NewPostardGrpcServer(contextService, smtpService) + + listener, err := net.Listen("tcp", ":5897") + if err != nil { + panic(err) + } + err = svr.Run(listener) + if err != nil { + panic(err) + } } diff --git a/go.mod b/go.mod index b02f1cb..4fd2fcf 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/avino-plan/postar go 1.15 require ( + github.com/FishGoddess/logit v0.4.7-alpha google.golang.org/grpc v1.40.0 google.golang.org/protobuf v1.27.1 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect diff --git a/go.sum b/go.sum index 4baad21..3bcf639 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/FishGoddess/logit v0.4.7-alpha h1:6pYVA+Qp+KFr/vB7Z4ad9JIhq7Nm9QMWBMcES9QYtvU= +github.com/FishGoddess/logit v0.4.7-alpha/go.mod h1:a2dFSibwfZXNNZ/V1jMOQBImPoySaxdPFThbsm17ZdY= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/internal/postard/server/grpc.go b/internal/postard/server/grpc.go index 969878f..409f0e8 100644 --- a/internal/postard/server/grpc.go +++ b/internal/postard/server/grpc.go @@ -10,33 +10,66 @@ package server import ( "context" + "net" "github.com/avino-plan/postar/api/postard" "github.com/avino-plan/postar/internal/pkg/trace" "github.com/avino-plan/postar/internal/postard/service" + "google.golang.org/grpc" ) -type PostardServerGrpcImpl struct { +// PostardGrpcServer is a grpc implement of PostardServer. +type PostardGrpcServer struct { postard.UnimplementedPostardServer - service service.SmtpService + server *grpc.Server + contextService service.ContextService + smtpService service.SmtpService } -func NewPostardServerGrpcImpl(service service.SmtpService) *PostardServerGrpcImpl { - return &PostardServerGrpcImpl{ - service: service, +// NewPostardGrpcServer returns a new PostardGrpcServer. +func NewPostardGrpcServer(contextService service.ContextService, smtpService service.SmtpService) *PostardGrpcServer { + return &PostardGrpcServer{ + contextService: contextService, + smtpService: smtpService, } } -func (psgi *PostardServerGrpcImpl) SendEmail(pCtx context.Context, request *postard.SendEmailRequest) (*postard.SendEmailResponse, error) { - ctx := trace.WithContext(pCtx) +// SendEmail sends emails. +func (pgs *PostardGrpcServer) SendEmail(ctx context.Context, request *postard.SendEmailRequest) (*postard.PostardResponse, error) { + ctx = pgs.contextService.WrapContext(ctx) + traceId := trace.FromContext(ctx) - err := psgi.service.SendEmail(ctx, nil, nil) + err := pgs.smtpService.SendEmail(ctx, nil, nil) if service.IsSendTimeout(err) { - + return &postard.PostardResponse{ + Code: postard.ResponseCodes_TimeoutError, + Msg: "send email timeout", + TraceId: traceId, + }, nil } if err != nil { - + return &postard.PostardResponse{ + Code: postard.ResponseCodes_InternalServerError, + Msg: "send email failed", + TraceId: traceId, + }, nil } - return &postard.SendEmailResponse{TraceId: trace.FromContext(ctx)}, nil + + return &postard.PostardResponse{ + Code: postard.ResponseCodes_OK, + TraceId: traceId, + }, nil +} + +// Run runs PostardGrpcServer with listener. +func (pgs *PostardGrpcServer) Run(listener net.Listener) error { + pgs.server = grpc.NewServer() + postard.RegisterPostardServer(pgs.server, pgs) + return pgs.server.Serve(listener) +} + +// Shutdown shutdowns PostardGrpcServer gracefully. +func (pgs *PostardGrpcServer) Shutdown() { + pgs.server.GracefulStop() } diff --git a/internal/postard/service/context.go b/internal/postard/service/context.go new file mode 100644 index 0000000..1b85ac6 --- /dev/null +++ b/internal/postard/service/context.go @@ -0,0 +1,32 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/27 22:40:58 + +package service + +import ( + "context" + + "github.com/FishGoddess/logit" + "github.com/avino-plan/postar/internal/pkg/trace" +) + +// contextServiceImpl is the service of context. +type contextServiceImpl struct { + logger *logit.Logger +} + +// NewContextService returns a new ContextService. +func NewContextService(logger *logit.Logger) ContextService { + return &contextServiceImpl{logger: logger} +} + +// WrapContext wraps context with something and returns a new context. +func (csi *contextServiceImpl) WrapContext(ctx context.Context) context.Context { + ctx = logit.NewContext(ctx, csi.logger) + return trace.WithContext(ctx) +} diff --git a/internal/postard/service/context_test.go b/internal/postard/service/context_test.go new file mode 100644 index 0000000..1067e6b --- /dev/null +++ b/internal/postard/service/context_test.go @@ -0,0 +1,35 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/27 22:50:56 + +package service + +import ( + "context" + "testing" + + "github.com/FishGoddess/logit" + "github.com/avino-plan/postar/internal/pkg/trace" +) + +// go test -v -cover -run=^TestNewContextService$ +func TestNewContextService(t *testing.T) { + logger := logit.NewLogger() + + service := NewContextService(logger) + ctx := service.WrapContext(context.Background()) + + contextLogger := logit.FromContext(ctx) + if contextLogger != logger { + t.Errorf("contextLogger %+v != logger %+v", contextLogger, logger) + } + + traceId := trace.FromContext(ctx) + if traceId == "" { + t.Error("traceId == ''") + } +} diff --git a/internal/postard/service/service.go b/internal/postard/service/service.go index d564c87..63bf45f 100644 --- a/internal/postard/service/service.go +++ b/internal/postard/service/service.go @@ -27,12 +27,10 @@ type SendEmailOptions struct { Timeout time.Duration // The timeout of sending one email. } -// DefaultSendEmailOptions returns a default options for sending emails. -func DefaultSendEmailOptions() *SendEmailOptions { - return &SendEmailOptions{ - Async: false, - Timeout: 5 * time.Second, - } +// ContextService is the service of context +type ContextService interface { + // WrapContext wraps context with something and returns a new context. + WrapContext(ctx context.Context) context.Context } // SmtpService is the service of smtp. @@ -40,3 +38,11 @@ type SmtpService interface { // SendEmail sends email with options and returns an error if something wrong happens. SendEmail(ctx context.Context, email *Email, options *SendEmailOptions) error } + +// DefaultSendEmailOptions returns a default options for sending emails. +func DefaultSendEmailOptions() *SendEmailOptions { + return &SendEmailOptions{ + Async: false, + Timeout: 5 * time.Second, + } +} diff --git a/internal/postard/service/service_test.go b/internal/postard/service/service_test.go index c87815e..a5dab9f 100644 --- a/internal/postard/service/service_test.go +++ b/internal/postard/service/service_test.go @@ -10,6 +10,11 @@ package service import "testing" +// go test -v -cover -run=^TestContextService$ +func TestContextService(t *testing.T) { + // do nothing... +} + // go test -v -cover -run=^TestSmtpService$ func TestSmtpService(t *testing.T) { // do nothing... diff --git a/internal/postard/service/smtp.go b/internal/postard/service/smtp.go index 63b12cb..f72f7f6 100644 --- a/internal/postard/service/smtp.go +++ b/internal/postard/service/smtp.go @@ -11,12 +11,13 @@ package service import ( "context" + "github.com/FishGoddess/logit" "github.com/avino-plan/postar/internal/pkg/concurrency" "gopkg.in/gomail.v2" ) -// SmtpServiceImpl is the service of smtp. -type SmtpServiceImpl struct { +// smtpServiceImpl is the service of smtp. +type smtpServiceImpl struct { pool *concurrency.Pool // The pool of workers. host string // The host of smtp server. port int // The port of smtp server. @@ -24,9 +25,9 @@ type SmtpServiceImpl struct { password string // The password of smtp server. } -// NewSmtpService returns a new SmtpServer. +// NewSmtpService returns a new SmtpService. func NewSmtpService(pool *concurrency.Pool, host string, port int, user string, password string) SmtpService { - return &SmtpServiceImpl{ + return &smtpServiceImpl{ pool: pool, host: host, port: port, @@ -36,7 +37,7 @@ func NewSmtpService(pool *concurrency.Pool, host string, port int, user string, } // sendEmail sends email and returns an error if something wrong happens. -func (ss *SmtpServiceImpl) sendEmail(email *Email) error { +func (ss *smtpServiceImpl) sendEmail(email *Email) error { msg := gomail.NewMessage() msg.SetHeader("From", ss.user) msg.SetHeader("To", email.To...) @@ -46,12 +47,15 @@ func (ss *SmtpServiceImpl) sendEmail(email *Email) error { } // SendEmail sends email to somewhere. -func (ss *SmtpServiceImpl) SendEmail(pCtx context.Context, email *Email, options *SendEmailOptions) error { +func (ss *smtpServiceImpl) SendEmail(ctx context.Context, email *Email, options *SendEmailOptions) error { + logger := logit.FromContext(ctx) + if options == nil { options = DefaultSendEmailOptions() + logger.Debug("options is nil, using DefaultSendEmailOptions()").Any("options", options).End() } - ctx, cancel := context.WithTimeout(pCtx, options.Timeout) + ctx, cancel := context.WithTimeout(ctx, options.Timeout) defer cancel() errorCh := ss.pool.Go(ctx, func(ctx context.Context) error { return ss.sendEmail(email) }) @@ -62,11 +66,13 @@ func (ss *SmtpServiceImpl) SendEmail(pCtx context.Context, email *Email, options select { case e := <-errorCh: if e != nil { + logger.Error("send email failed").Error("e", e).End() return errSendEmailFailed } case <-ctx.Done(): e := ctx.Err() if e != nil { + logger.Error("send email timeout").Error("e", e).End() return errSendTimeout } } From 913e6313d8dcb98ba99a19ca31a5d70ce33ad6e5 Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Mon, 17 Jan 2022 01:12:14 +0800 Subject: [PATCH 09/27] =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/{postard => }/postard.pb.go | 91 +++++++------- api/{postard => }/postard.proto | 4 +- api/{postard => }/postard_grpc.pb.go | 16 +-- cmd/postard/main.go | 12 +- internal/pkg/trace/trace.go | 35 ------ internal/pkg/trace/trace_test.go | 41 ------- internal/postard/{service => biz}/smtp.go | 32 ++--- .../postard/{service => biz}/smtp_test.go | 13 +- .../{service/service.go => model/smtp.go} | 19 +-- internal/postard/server/grpc.go | 75 ++++++------ internal/postard/server/http.go | 2 + internal/postard/server/udp.go | 11 ++ internal/postard/server/vex.go | 11 ++ internal/postard/service/context.go | 32 ----- internal/postard/service/error.go | 26 ---- internal/postard/service/service_test.go | 21 ---- {internal/pkg => pkg}/concurrency/pool.go | 0 .../pkg => pkg}/concurrency/pool_test.go | 0 pkg/context/context.go | 29 +++++ .../service => pkg/context}/context_test.go | 17 +-- {internal/pkg/trace => pkg/encode}/encode.go | 43 ++++--- .../pkg/trace => pkg/encode}/encode_test.go | 12 +- pkg/errors/errors.go | 114 ++++++++++++++++++ .../errors/errors_test.go | 2 +- pkg/trace/trace.go | 39 ++++++ pkg/trace/trace_test.go | 57 +++++++++ 26 files changed, 430 insertions(+), 324 deletions(-) rename api/{postard => }/postard.pb.go (79%) rename api/{postard => }/postard.proto (93%) rename api/{postard => }/postard_grpc.pb.go (86%) delete mode 100644 internal/pkg/trace/trace.go delete mode 100644 internal/pkg/trace/trace_test.go rename internal/postard/{service => biz}/smtp.go (63%) rename internal/postard/{service => biz}/smtp_test.go (78%) rename internal/postard/{service/service.go => model/smtp.go} (64%) create mode 100644 internal/postard/server/udp.go create mode 100644 internal/postard/server/vex.go delete mode 100644 internal/postard/service/context.go delete mode 100644 internal/postard/service/error.go delete mode 100644 internal/postard/service/service_test.go rename {internal/pkg => pkg}/concurrency/pool.go (100%) rename {internal/pkg => pkg}/concurrency/pool_test.go (100%) create mode 100644 pkg/context/context.go rename {internal/postard/service => pkg/context}/context_test.go (64%) rename {internal/pkg/trace => pkg/encode}/encode.go (60%) rename {internal/pkg/trace => pkg/encode}/encode_test.go (77%) create mode 100644 pkg/errors/errors.go rename internal/postard/service/error_test.go => pkg/errors/errors_test.go (98%) create mode 100644 pkg/trace/trace.go create mode 100644 pkg/trace/trace_test.go diff --git a/api/postard/postard.pb.go b/api/postard.pb.go similarity index 79% rename from api/postard/postard.pb.go rename to api/postard.pb.go index d6ce25b..5bb79f1 100644 --- a/api/postard/postard.pb.go +++ b/api/postard.pb.go @@ -12,7 +12,7 @@ // protoc v3.18.0 // source: postard.proto -package postard +package api import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" @@ -85,10 +85,10 @@ type PostardResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Code ResponseCodes `protobuf:"varint,1,opt,name=code,proto3,enum=github.com.avinoplan.postar.api.postard.ResponseCodes" json:"code,omitempty"` // 0 is ok. - Msg string `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"` // For messaging. - TraceId string `protobuf:"bytes,3,opt,name=traceId,proto3" json:"traceId,omitempty"` // For tracing. - Data *anypb.Any `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` // Any data. + Code ResponseCodes `protobuf:"varint,1,opt,name=code,proto3,enum=github.com.avinoplan.postar.api.ResponseCodes" json:"code,omitempty"` // 0 is ok. + Msg string `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"` // For messaging. + TraceId string `protobuf:"bytes,3,opt,name=traceId,proto3" json:"traceId,omitempty"` // For tracing. + Data *anypb.Any `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` // Any data. } func (x *PostardResponse) Reset() { @@ -151,7 +151,7 @@ func (x *PostardResponse) GetData() *anypb.Any { return nil } -// Email wraps all information of using smtp service. +// Email wraps all information of using smtp biz. type Email struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -339,15 +339,14 @@ var File_postard_proto protoreflect.FileDescriptor var file_postard_proto_rawDesc = []byte{ 0x0a, 0x0d, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, - 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, + 0x1f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0xb3, 0x01, 0x0a, 0x0f, 0x50, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x36, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, - 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, + 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xab, 0x01, 0x0a, 0x0f, + 0x50, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x42, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2e, 0x2e, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, + 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, @@ -365,34 +364,32 @@ var file_postard_proto_rawDesc = []byte{ 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, - 0x22, 0xad, 0x01, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x22, 0x9d, 0x01, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, + 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x05, 0x65, 0x6d, + 0x61, 0x69, 0x6c, 0x12, 0x4b, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, - 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x45, - 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x53, 0x0a, 0x07, 0x6f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, - 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, - 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, + 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2a, 0x46, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x13, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0xd0, 0x86, 0x03, 0x12, 0x12, 0x0a, 0x0c, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x45, - 0x72, 0x72, 0x6f, 0x72, 0x10, 0xd1, 0x86, 0x03, 0x32, 0x8c, 0x01, 0x0a, 0x07, 0x50, 0x6f, 0x73, - 0x74, 0x61, 0x72, 0x64, 0x12, 0x80, 0x01, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, - 0x69, 0x6c, 0x12, 0x39, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, - 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x53, 0x65, 0x6e, - 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, - 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x2d, 0x70, 0x6c, 0x61, 0x6e, - 0x2f, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6f, 0x73, 0x74, - 0x61, 0x72, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x72, 0x6f, 0x72, 0x10, 0xd1, 0x86, 0x03, 0x32, 0x7b, 0x0a, 0x07, 0x50, 0x6f, 0x73, 0x74, + 0x61, 0x72, 0x64, 0x12, 0x70, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, + 0x12, 0x31, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, + 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x22, 0x5a, 0x20, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x2d, 0x70, 0x6c, 0x61, 0x6e, 0x2f, 0x70, + 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -410,20 +407,20 @@ func file_postard_proto_rawDescGZIP() []byte { var file_postard_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_postard_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_postard_proto_goTypes = []interface{}{ - (ResponseCodes)(0), // 0: github.com.avinoplan.postar.api.postard.ResponseCodes - (*PostardResponse)(nil), // 1: github.com.avinoplan.postar.api.postard.PostardResponse - (*Email)(nil), // 2: github.com.avinoplan.postar.api.postard.Email - (*SendEmailOptions)(nil), // 3: github.com.avinoplan.postar.api.postard.SendEmailOptions - (*SendEmailRequest)(nil), // 4: github.com.avinoplan.postar.api.postard.SendEmailRequest + (ResponseCodes)(0), // 0: github.com.avinoplan.postar.api.ResponseCodes + (*PostardResponse)(nil), // 1: github.com.avinoplan.postar.api.PostardResponse + (*Email)(nil), // 2: github.com.avinoplan.postar.api.Email + (*SendEmailOptions)(nil), // 3: github.com.avinoplan.postar.api.SendEmailOptions + (*SendEmailRequest)(nil), // 4: github.com.avinoplan.postar.api.SendEmailRequest (*anypb.Any)(nil), // 5: google.protobuf.Any } var file_postard_proto_depIdxs = []int32{ - 0, // 0: github.com.avinoplan.postar.api.postard.PostardResponse.code:type_name -> github.com.avinoplan.postar.api.postard.ResponseCodes - 5, // 1: github.com.avinoplan.postar.api.postard.PostardResponse.data:type_name -> google.protobuf.Any - 2, // 2: github.com.avinoplan.postar.api.postard.SendEmailRequest.email:type_name -> github.com.avinoplan.postar.api.postard.Email - 3, // 3: github.com.avinoplan.postar.api.postard.SendEmailRequest.options:type_name -> github.com.avinoplan.postar.api.postard.SendEmailOptions - 4, // 4: github.com.avinoplan.postar.api.postard.Postard.SendEmail:input_type -> github.com.avinoplan.postar.api.postard.SendEmailRequest - 1, // 5: github.com.avinoplan.postar.api.postard.Postard.SendEmail:output_type -> github.com.avinoplan.postar.api.postard.PostardResponse + 0, // 0: github.com.avinoplan.postar.api.PostardResponse.code:type_name -> github.com.avinoplan.postar.api.ResponseCodes + 5, // 1: github.com.avinoplan.postar.api.PostardResponse.data:type_name -> google.protobuf.Any + 2, // 2: github.com.avinoplan.postar.api.SendEmailRequest.email:type_name -> github.com.avinoplan.postar.api.Email + 3, // 3: github.com.avinoplan.postar.api.SendEmailRequest.options:type_name -> github.com.avinoplan.postar.api.SendEmailOptions + 4, // 4: github.com.avinoplan.postar.api.Postard.SendEmail:input_type -> github.com.avinoplan.postar.api.SendEmailRequest + 1, // 5: github.com.avinoplan.postar.api.Postard.SendEmail:output_type -> github.com.avinoplan.postar.api.PostardResponse 5, // [5:6] is the sub-list for method output_type 4, // [4:5] is the sub-list for method input_type 4, // [4:4] is the sub-list for extension type_name diff --git a/api/postard/postard.proto b/api/postard.proto similarity index 93% rename from api/postard/postard.proto rename to api/postard.proto index 7159e48..dce3bae 100644 --- a/api/postard/postard.proto +++ b/api/postard.proto @@ -6,10 +6,10 @@ // Email: fishgoddess@qq.com // Created at 2021/09/16 01:42:33 syntax = "proto3"; -package github.com.avinoplan.postar.api.postard; +package github.com.avinoplan.postar.api; // protoc -I=. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative postard.proto -option go_package = "github.com/avino-plan/postar/api/postard"; +option go_package = "github.com/avino-plan/postar/api"; import "google/protobuf/any.proto"; diff --git a/api/postard/postard_grpc.pb.go b/api/postard_grpc.pb.go similarity index 86% rename from api/postard/postard_grpc.pb.go rename to api/postard_grpc.pb.go index 235c9ae..05b575f 100644 --- a/api/postard/postard_grpc.pb.go +++ b/api/postard_grpc.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. -package postard +package api import ( context "context" @@ -14,7 +14,7 @@ import ( // Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 -// PostardClient is the client API for Postard service. +// PostardClient is the client API for Postard biz. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type PostardClient interface { @@ -31,14 +31,14 @@ func NewPostardClient(cc grpc.ClientConnInterface) PostardClient { func (c *postardClient) SendEmail(ctx context.Context, in *SendEmailRequest, opts ...grpc.CallOption) (*PostardResponse, error) { out := new(PostardResponse) - err := c.cc.Invoke(ctx, "/github.com.avinoplan.postar.api.postard.Postard/SendEmail", in, out, opts...) + err := c.cc.Invoke(ctx, "/github.com.avinoplan.postar.api.Postard/SendEmail", in, out, opts...) if err != nil { return nil, err } return out, nil } -// PostardServer is the server API for Postard service. +// PostardServer is the server API for Postard biz. // All implementations must embed UnimplementedPostardServer // for forward compatibility type PostardServer interface { @@ -55,7 +55,7 @@ func (UnimplementedPostardServer) SendEmail(context.Context, *SendEmailRequest) } func (UnimplementedPostardServer) mustEmbedUnimplementedPostardServer() {} -// UnsafePostardServer may be embedded to opt out of forward compatibility for this service. +// UnsafePostardServer may be embedded to opt out of forward compatibility for this biz. // Use of this interface is not recommended, as added methods to PostardServer will // result in compilation errors. type UnsafePostardServer interface { @@ -76,7 +76,7 @@ func _Postard_SendEmail_Handler(srv interface{}, ctx context.Context, dec func(i } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/github.com.avinoplan.postar.api.postard.Postard/SendEmail", + FullMethod: "/github.com.avinoplan.postar.api.Postard/SendEmail", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(PostardServer).SendEmail(ctx, req.(*SendEmailRequest)) @@ -84,11 +84,11 @@ func _Postard_SendEmail_Handler(srv interface{}, ctx context.Context, dec func(i return interceptor(ctx, in, info, handler) } -// Postard_ServiceDesc is the grpc.ServiceDesc for Postard service. +// Postard_ServiceDesc is the grpc.ServiceDesc for Postard biz. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var Postard_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "github.com.avinoplan.postar.api.postard.Postard", + ServiceName: "github.com.avinoplan.postar.api.Postard", HandlerType: (*PostardServer)(nil), Methods: []grpc.MethodDesc{ { diff --git a/cmd/postard/main.go b/cmd/postard/main.go index 2155d67..c915b42 100644 --- a/cmd/postard/main.go +++ b/cmd/postard/main.go @@ -12,19 +12,17 @@ import ( "net" "github.com/FishGoddess/logit" - "github.com/avino-plan/postar/internal/pkg/concurrency" + "github.com/avino-plan/postar/internal/postard/biz" "github.com/avino-plan/postar/internal/postard/server" - "github.com/avino-plan/postar/internal/postard/service" + "github.com/avino-plan/postar/pkg/concurrency" ) func main() { logger := logit.NewLogger() - contextService := service.NewContextService(logger) - pool := concurrency.NewPool() - smtpService := service.NewSmtpService(pool, "", 0, "", "") - - svr := server.NewPostardGrpcServer(contextService, smtpService) + + smtpBiz := biz.NewSmtpBiz(pool, "", 0, "", "") + svr := server.NewGrpcServer(logger, smtpBiz) listener, err := net.Listen("tcp", ":5897") if err != nil { diff --git a/internal/pkg/trace/trace.go b/internal/pkg/trace/trace.go deleted file mode 100644 index 1f82838..0000000 --- a/internal/pkg/trace/trace.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/09/25 22:44:59 - -package trace - -import "context" - -var ( - traceIdKey struct{} // The key of trace id in context. -) - -// WithContext returns a context with a trace id inside. -func WithContext(ctx context.Context) context.Context { - traceId := RandomString(4) + TimeHex() + PidHex() + RandomString(4) - return context.WithValue(ctx, traceIdKey, traceId) -} - -// FromContext gets the trace id from context. -func FromContext(ctx context.Context) string { - value := ctx.Value(traceIdKey) - if value == nil { - return "" - } - - traceId, ok := value.(string) - if !ok { - return "" - } - return traceId -} diff --git a/internal/pkg/trace/trace_test.go b/internal/pkg/trace/trace_test.go deleted file mode 100644 index 2675c6a..0000000 --- a/internal/pkg/trace/trace_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/09/25 23:07:54 - -package trace - -import ( - "context" - "testing" -) - -// go test -v -cover -run=^TestWithContext$ -func TestWithContext(t *testing.T) { - ctx := WithContext(context.Background()) - - value := ctx.Value(traceIdKey) - if value == nil { - t.Error("ctx.Value returns nil") - } - - traceId, ok := value.(string) - if !ok { - t.Errorf("value %+v isn't string", value) - } - t.Log("traceId:", traceId) -} - -// go test -v -cover -run=^TestFromContext$ -func TestFromContext(t *testing.T) { - traceId := RandomString(4) + TimeHex() + PidHex() + RandomString(4) - ctx := context.WithValue(context.Background(), traceIdKey, traceId) - - traceIdInCtx := FromContext(ctx) - if traceIdInCtx != traceId { - t.Errorf("traceIdInCtx %s != traceId %s", traceIdInCtx, traceId) - } -} diff --git a/internal/postard/service/smtp.go b/internal/postard/biz/smtp.go similarity index 63% rename from internal/postard/service/smtp.go rename to internal/postard/biz/smtp.go index f72f7f6..6a35130 100644 --- a/internal/postard/service/smtp.go +++ b/internal/postard/biz/smtp.go @@ -6,18 +6,20 @@ // Email: fishgoddess@qq.com // Created at 2021/09/16 02:05:37 -package service +package biz import ( "context" "github.com/FishGoddess/logit" - "github.com/avino-plan/postar/internal/pkg/concurrency" + "github.com/avino-plan/postar/internal/postard/model" + "github.com/avino-plan/postar/pkg/concurrency" + "github.com/avino-plan/postar/pkg/errors" "gopkg.in/gomail.v2" ) -// smtpServiceImpl is the service of smtp. -type smtpServiceImpl struct { +// SmtpBiz is the biz of smtp. +type SmtpBiz struct { pool *concurrency.Pool // The pool of workers. host string // The host of smtp server. port int // The port of smtp server. @@ -25,9 +27,9 @@ type smtpServiceImpl struct { password string // The password of smtp server. } -// NewSmtpService returns a new SmtpService. -func NewSmtpService(pool *concurrency.Pool, host string, port int, user string, password string) SmtpService { - return &smtpServiceImpl{ +// NewSmtpBiz returns a new SmtpBiz. +func NewSmtpBiz(pool *concurrency.Pool, host string, port int, user string, password string) *SmtpBiz { + return &SmtpBiz{ pool: pool, host: host, port: port, @@ -37,28 +39,28 @@ func NewSmtpService(pool *concurrency.Pool, host string, port int, user string, } // sendEmail sends email and returns an error if something wrong happens. -func (ss *smtpServiceImpl) sendEmail(email *Email) error { +func (sb *SmtpBiz) sendEmail(email *model.Email) error { msg := gomail.NewMessage() - msg.SetHeader("From", ss.user) + msg.SetHeader("From", sb.user) msg.SetHeader("To", email.To...) msg.SetHeader("Subject", email.Subject) msg.SetBody(email.BodyType, email.Body) - return gomail.NewDialer(ss.host, ss.port, ss.user, ss.password).DialAndSend(msg) + return gomail.NewDialer(sb.host, sb.port, sb.user, sb.password).DialAndSend(msg) } // SendEmail sends email to somewhere. -func (ss *smtpServiceImpl) SendEmail(ctx context.Context, email *Email, options *SendEmailOptions) error { +func (sb *SmtpBiz) SendEmail(ctx context.Context, email *model.Email, options *model.SendEmailOptions) error { logger := logit.FromContext(ctx) if options == nil { - options = DefaultSendEmailOptions() + options = model.DefaultSendEmailOptions() logger.Debug("options is nil, using DefaultSendEmailOptions()").Any("options", options).End() } ctx, cancel := context.WithTimeout(ctx, options.Timeout) defer cancel() - errorCh := ss.pool.Go(ctx, func(ctx context.Context) error { return ss.sendEmail(email) }) + errorCh := sb.pool.Go(ctx, func(ctx context.Context) error { return sb.sendEmail(email) }) if options.Async { return nil } @@ -67,13 +69,13 @@ func (ss *smtpServiceImpl) SendEmail(ctx context.Context, email *Email, options case e := <-errorCh: if e != nil { logger.Error("send email failed").Error("e", e).End() - return errSendEmailFailed + return errors.SendEmailFailedErr(e) } case <-ctx.Done(): e := ctx.Err() if e != nil { logger.Error("send email timeout").Error("e", e).End() - return errSendTimeout + return errors.SendTimeoutErr(e) } } return nil diff --git a/internal/postard/service/smtp_test.go b/internal/postard/biz/smtp_test.go similarity index 78% rename from internal/postard/service/smtp_test.go rename to internal/postard/biz/smtp_test.go index e4a2615..d4c7677 100644 --- a/internal/postard/service/smtp_test.go +++ b/internal/postard/biz/smtp_test.go @@ -6,7 +6,7 @@ // Email: fishgoddess@qq.com // Created at 2021/09/22 00:24:19 -package service +package biz import ( "context" @@ -15,11 +15,12 @@ import ( "testing" "time" - "github.com/avino-plan/postar/internal/pkg/concurrency" + "github.com/avino-plan/postar/internal/postard/model" + "github.com/avino-plan/postar/pkg/concurrency" ) -// go test -v -cover -run=^TestNewSmtpService$ -func TestNewSmtpService(t *testing.T) { +// go test -v -cover -run=^TestSmtpBiz$ +func TestSmtpBiz(t *testing.T) { host := os.Getenv("POSTAR_SMTP_HOST") user := os.Getenv("POSTAR_SMTP_USER") password := os.Getenv("POSTAR_SMTP_PASSWORD") @@ -37,12 +38,12 @@ func TestNewSmtpService(t *testing.T) { defer pool.Stop() smtpService := NewSmtpService(pool, host, int(port), user, password) - err = smtpService.SendEmail(context.Background(), &Email{ + err = smtpService.SendEmail(context.Background(), &model.Email{ To: []string{to}, Subject: t.Name(), BodyType: "text/html;charset=utf-8", Body: t.Name() + time.Now().Format("20060102150405.000"), - }, DefaultSendEmailOptions()) + }, model.DefaultSendEmailOptions()) if err != nil { t.Error(err) } diff --git a/internal/postard/service/service.go b/internal/postard/model/smtp.go similarity index 64% rename from internal/postard/service/service.go rename to internal/postard/model/smtp.go index 63bf45f..d783b32 100644 --- a/internal/postard/service/service.go +++ b/internal/postard/model/smtp.go @@ -6,12 +6,9 @@ // Email: fishgoddess@qq.com // Created at 2021/09/17 00:06:09 -package service +package model -import ( - "context" - "time" -) +import "time" // Email is an email. type Email struct { @@ -27,18 +24,6 @@ type SendEmailOptions struct { Timeout time.Duration // The timeout of sending one email. } -// ContextService is the service of context -type ContextService interface { - // WrapContext wraps context with something and returns a new context. - WrapContext(ctx context.Context) context.Context -} - -// SmtpService is the service of smtp. -type SmtpService interface { - // SendEmail sends email with options and returns an error if something wrong happens. - SendEmail(ctx context.Context, email *Email, options *SendEmailOptions) error -} - // DefaultSendEmailOptions returns a default options for sending emails. func DefaultSendEmailOptions() *SendEmailOptions { return &SendEmailOptions{ diff --git a/internal/postard/server/grpc.go b/internal/postard/server/grpc.go index 409f0e8..4204179 100644 --- a/internal/postard/server/grpc.go +++ b/internal/postard/server/grpc.go @@ -12,64 +12,67 @@ import ( "context" "net" - "github.com/avino-plan/postar/api/postard" - "github.com/avino-plan/postar/internal/pkg/trace" - "github.com/avino-plan/postar/internal/postard/service" + "github.com/FishGoddess/logit" + "github.com/avino-plan/postar/api" + "github.com/avino-plan/postar/internal/postard/biz" + "github.com/avino-plan/postar/pkg/errors" + "github.com/avino-plan/postar/pkg/trace" "google.golang.org/grpc" ) -// PostardGrpcServer is a grpc implement of PostardServer. -type PostardGrpcServer struct { - postard.UnimplementedPostardServer - server *grpc.Server - contextService service.ContextService - smtpService service.SmtpService +// GRPCServer is a grpc implement of PostardServer. +type GRPCServer struct { + api.UnimplementedPostardServer + server *grpc.Server + logger *logit.Logger + smtpBiz *biz.SmtpBiz } -// NewPostardGrpcServer returns a new PostardGrpcServer. -func NewPostardGrpcServer(contextService service.ContextService, smtpService service.SmtpService) *PostardGrpcServer { - return &PostardGrpcServer{ - contextService: contextService, - smtpService: smtpService, +// NewGrpcServer returns a new GRPCServer. +func NewGrpcServer(logger *logit.Logger, smtpBiz *biz.SmtpBiz) *GRPCServer { + return &GRPCServer{ + logger: logger, + smtpBiz: smtpBiz, } } // SendEmail sends emails. -func (pgs *PostardGrpcServer) SendEmail(ctx context.Context, request *postard.SendEmailRequest) (*postard.PostardResponse, error) { - ctx = pgs.contextService.WrapContext(ctx) - traceId := trace.FromContext(ctx) +func (gs *GRPCServer) SendEmail(ctx context.Context, request *api.SendEmailRequest) (*api.PostardResponse, error) { + traceID := trace.NewTraceID() + ctx = trace.NewContext(ctx, traceID) + ctx = logit.NewContext(ctx, gs.logger) - err := pgs.smtpService.SendEmail(ctx, nil, nil) - if service.IsSendTimeout(err) { - return &postard.PostardResponse{ - Code: postard.ResponseCodes_TimeoutError, + err := gs.smtpBiz.SendEmail(ctx, nil, nil) + if errors.IsSendTimeout(err) { + return &api.PostardResponse{ + Code: api.ResponseCodes_TimeoutError, Msg: "send email timeout", - TraceId: traceId, + TraceId: traceID, }, nil } if err != nil { - return &postard.PostardResponse{ - Code: postard.ResponseCodes_InternalServerError, + return &api.PostardResponse{ + Code: api.ResponseCodes_InternalServerError, Msg: "send email failed", - TraceId: traceId, + TraceId: traceID, }, nil } - return &postard.PostardResponse{ - Code: postard.ResponseCodes_OK, - TraceId: traceId, + return &api.PostardResponse{ + Code: api.ResponseCodes_OK, + TraceId: traceID, }, nil } -// Run runs PostardGrpcServer with listener. -func (pgs *PostardGrpcServer) Run(listener net.Listener) error { - pgs.server = grpc.NewServer() - postard.RegisterPostardServer(pgs.server, pgs) - return pgs.server.Serve(listener) +// Run runs GRPCServer with listener. +func (gs *GRPCServer) Run(listener net.Listener) error { + gs.server = grpc.NewServer() + api.RegisterPostardServer(gs.server, gs) + return gs.server.Serve(listener) } -// Shutdown shutdowns PostardGrpcServer gracefully. -func (pgs *PostardGrpcServer) Shutdown() { - pgs.server.GracefulStop() +// Shutdown shutdowns GRPCServer gracefully. +func (gs *GRPCServer) Shutdown() { + gs.server.GracefulStop() } diff --git a/internal/postard/server/http.go b/internal/postard/server/http.go index 3665780..ebe1a1a 100644 --- a/internal/postard/server/http.go +++ b/internal/postard/server/http.go @@ -7,3 +7,5 @@ // Created at 2021/09/16 02:04:54 package server + +// TODO http server diff --git a/internal/postard/server/udp.go b/internal/postard/server/udp.go new file mode 100644 index 0000000..a58f00c --- /dev/null +++ b/internal/postard/server/udp.go @@ -0,0 +1,11 @@ +// Copyright 2022 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2022/01/17 00:59:20 + +package server + +// TODO udp server diff --git a/internal/postard/server/vex.go b/internal/postard/server/vex.go new file mode 100644 index 0000000..9a4e973 --- /dev/null +++ b/internal/postard/server/vex.go @@ -0,0 +1,11 @@ +// Copyright 2022 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2022/01/17 00:56:29 + +package server + +// TODO vex server diff --git a/internal/postard/service/context.go b/internal/postard/service/context.go deleted file mode 100644 index 1b85ac6..0000000 --- a/internal/postard/service/context.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/09/27 22:40:58 - -package service - -import ( - "context" - - "github.com/FishGoddess/logit" - "github.com/avino-plan/postar/internal/pkg/trace" -) - -// contextServiceImpl is the service of context. -type contextServiceImpl struct { - logger *logit.Logger -} - -// NewContextService returns a new ContextService. -func NewContextService(logger *logit.Logger) ContextService { - return &contextServiceImpl{logger: logger} -} - -// WrapContext wraps context with something and returns a new context. -func (csi *contextServiceImpl) WrapContext(ctx context.Context) context.Context { - ctx = logit.NewContext(ctx, csi.logger) - return trace.WithContext(ctx) -} diff --git a/internal/postard/service/error.go b/internal/postard/service/error.go deleted file mode 100644 index ee1d22f..0000000 --- a/internal/postard/service/error.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/09/25 21:58:02 - -package service - -import "errors" - -var ( - errSendTimeout = errors.New("postard/service: send timeout") // Send timeout - errSendEmailFailed = errors.New("postard/service: send email failed") // Send email failed -) - -// IsSendTimeout returns if err equals to errSendTimeout. -func IsSendTimeout(err error) bool { - return errors.Is(err, errSendTimeout) -} - -// IsSendEmailFailed returns if err equals to errSendEmailFailed. -func IsSendEmailFailed(err error) bool { - return errors.Is(err, errSendEmailFailed) -} diff --git a/internal/postard/service/service_test.go b/internal/postard/service/service_test.go deleted file mode 100644 index a5dab9f..0000000 --- a/internal/postard/service/service_test.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/09/22 00:22:39 - -package service - -import "testing" - -// go test -v -cover -run=^TestContextService$ -func TestContextService(t *testing.T) { - // do nothing... -} - -// go test -v -cover -run=^TestSmtpService$ -func TestSmtpService(t *testing.T) { - // do nothing... -} diff --git a/internal/pkg/concurrency/pool.go b/pkg/concurrency/pool.go similarity index 100% rename from internal/pkg/concurrency/pool.go rename to pkg/concurrency/pool.go diff --git a/internal/pkg/concurrency/pool_test.go b/pkg/concurrency/pool_test.go similarity index 100% rename from internal/pkg/concurrency/pool_test.go rename to pkg/concurrency/pool_test.go diff --git a/pkg/context/context.go b/pkg/context/context.go new file mode 100644 index 0000000..b0aa994 --- /dev/null +++ b/pkg/context/context.go @@ -0,0 +1,29 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/27 22:40:58 + +package context + +import ( + "context" + + "github.com/FishGoddess/logit" + "github.com/avino-plan/postar/pkg/trace" +) + +// WithLogger wraps ctx with logger. +func WithLogger(ctx context.Context, logger *logit.Logger) context.Context { + return logit.NewContext(ctx, logger) +} + +// WithTraceID wraps ctx with traceID. +func WithTraceID(ctx context.Context, traceID string) context.Context { + if traceID == "" { + traceID = trace.NewTraceID() + } + return trace.NewContext(ctx, traceID) +} diff --git a/internal/postard/service/context_test.go b/pkg/context/context_test.go similarity index 64% rename from internal/postard/service/context_test.go rename to pkg/context/context_test.go index 1067e6b..cad2cdc 100644 --- a/internal/postard/service/context_test.go +++ b/pkg/context/context_test.go @@ -6,28 +6,29 @@ // Email: fishgoddess@qq.com // Created at 2021/09/27 22:50:56 -package service +package context import ( "context" "testing" "github.com/FishGoddess/logit" - "github.com/avino-plan/postar/internal/pkg/trace" + "github.com/avino-plan/postar/pkg/trace" ) -// go test -v -cover -run=^TestNewContextService$ -func TestNewContextService(t *testing.T) { +// go test -v -cover -run=^TestWithLogger$ +func TestWithLogger(t *testing.T) { logger := logit.NewLogger() - - service := NewContextService(logger) - ctx := service.WrapContext(context.Background()) - + ctx := WithLogger(context.Background(), logger) contextLogger := logit.FromContext(ctx) if contextLogger != logger { t.Errorf("contextLogger %+v != logger %+v", contextLogger, logger) } +} +// go test -v -cover -run=^TestWithTraceID$ +func TestWithTraceID(t *testing.T) { + ctx := WithTraceID(context.Background()) traceId := trace.FromContext(ctx) if traceId == "" { t.Error("traceId == ''") diff --git a/internal/pkg/trace/encode.go b/pkg/encode/encode.go similarity index 60% rename from internal/pkg/trace/encode.go rename to pkg/encode/encode.go index 8c14333..f6e8609 100644 --- a/internal/pkg/trace/encode.go +++ b/pkg/encode/encode.go @@ -6,18 +6,19 @@ // Email: fishgoddess@qq.com // Created at 2021/09/25 22:39:17 -package trace +package encode import ( "encoding/binary" "fmt" "math/rand" "os" + "strconv" "time" ) var ( - // letters includes 0-9 a-z A-Z. + // letters includes 0-9, a-z, A-Z. letters = [62]byte{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', @@ -26,25 +27,35 @@ var ( 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', } - // random 用于生成随机数 + pid = strconv.Itoa(os.Getpid()) random = rand.New(rand.NewSource(time.Now().Unix())) - - // pid 是程序的 pid - pid = uint64(os.Getpid()) ) -// TimeHex returns now time in hex. -func TimeHex() string { - b := make([]byte, 8) - binary.BigEndian.PutUint64(b, uint64(time.Now().Unix())) - return fmt.Sprintf("%x", b[4:]) +// numberHex returns num in hex string. +// The hex string will be cut with start and end. +func numberHex(num uint64, start int, end int) string { + size := 8 + if start < 0 || start > size { + start = 0 + } + + if end <= 0 || end > size { + end = size + } + + b := make([]byte, size) + binary.BigEndian.PutUint64(b, num) + return fmt.Sprintf("%x", b[start:end]) +} + +// Now returns in current time in hex string. +func Now() string { + return numberHex(uint64(time.Now().Unix()), 4, 0) } -// PidHex returns pid in hex. -func PidHex() string { - b := make([]byte, 8) - binary.BigEndian.PutUint64(b, pid) - return fmt.Sprintf("%x", b[4:]) +// PID returns pid in string. +func PID() string { + return pid } // RandomString returns a string including 0-9/a-z/A-Z not longer than length. diff --git a/internal/pkg/trace/encode_test.go b/pkg/encode/encode_test.go similarity index 77% rename from internal/pkg/trace/encode_test.go rename to pkg/encode/encode_test.go index 6cebf36..9d23ef0 100644 --- a/internal/pkg/trace/encode_test.go +++ b/pkg/encode/encode_test.go @@ -6,13 +6,13 @@ // Email: fishgoddess@qq.com // Created at 2021/09/25 22:58:39 -package trace +package encode import "testing" -// go test -v -cover -run=^TestTimeHex$ -func TestTimeHex(t *testing.T) { - timeHex := TimeHex() +// go test -v -cover -run=^NowTimeHex$ +func TestNowTimeHex(t *testing.T) { + timeHex := Now() if len(timeHex) != 8 { t.Errorf("length of TimeHex is wrong with %s, %d", timeHex, len(timeHex)) } @@ -20,9 +20,9 @@ func TestTimeHex(t *testing.T) { // go test -v -cover -run=^TestPidHex$ func TestPidHex(t *testing.T) { - pidHex := PidHex() + pidHex := PID() if len(pidHex) != 8 { - t.Errorf("length of PidHex is wrong with %s, %d", pidHex, len(pidHex)) + t.Errorf("length of PID is wrong with %s, %d", pidHex, len(pidHex)) } } diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go new file mode 100644 index 0000000..3986fac --- /dev/null +++ b/pkg/errors/errors.go @@ -0,0 +1,114 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/25 21:58:02 + +package errors + +import ( + "errors" + "fmt" + "net/http" +) + +const ( + codeBadRequest = http.StatusBadRequest + codeSendTimeout = 1100 + codeSendEmailFailed = 11000 +) + +// Error is error with code. +type Error struct { + err error + code int32 +} + +// Error returns error message. +func (e *Error) Error() string { + if e == nil || e.err == nil { + return "" + } + return fmt.Sprintf("%d (%s)", e.code, e.err.Error()) +} + +// Is returns if e is the target's type. +func (e *Error) Is(target error) bool { + if e == nil { + return e == target + } + + err, ok := target.(*Error) + if !ok { + return e.err == target + } + + return e.code == err.code +} + +// Unwrap unwraps e. +func (e *Error) Unwrap() error { + if e == nil { + return nil + } + return e.err +} + +// WithCode wraps err with code. +func WithCode(err error, code int32) error { + if err == nil { + return nil + } + return &Error{err: err, code: code} +} + +// Is returns if err has a code and its code equals to code. +func Is(err error, code int32) bool { + for { + if err == nil { + return false + } + + e, ok := err.(*Error) + if !ok { + return false + } + + if e.code == code { + return true + } + err = errors.Unwrap(err) + } +} + +// BadRequest returns a bad request error. +func BadRequest(err error) error { + return WithCode(err, codeBadRequest) +} + +// IsBadRequest returns if err is bad request. +func IsBadRequest(err error) bool { + return Is(err, codeBadRequest) +} + +// SendTimeoutErr returns a send timeout error. +func SendTimeoutErr(err error) error { + return WithCode(err, codeSendTimeout) +} + +// IsSendTimeout returns if err is send timeout. +func IsSendTimeout(err error) bool { + return Is(err, codeSendTimeout) +} + +// SendEmailFailedErr returns a send email failed error. +func SendEmailFailedErr(err error) error { + return WithCode(err, codeSendEmailFailed) +} + +// IsSendEmailFailed returns if err is send email failed. +func IsSendEmailFailed(err error) bool { + return Is(err, codeSendEmailFailed) +} diff --git a/internal/postard/service/error_test.go b/pkg/errors/errors_test.go similarity index 98% rename from internal/postard/service/error_test.go rename to pkg/errors/errors_test.go index ddcfbe9..e435dc0 100644 --- a/internal/postard/service/error_test.go +++ b/pkg/errors/errors_test.go @@ -6,7 +6,7 @@ // Email: fishgoddess@qq.com // Created at 2021/09/25 22:06:36 -package service +package errors import ( "errors" diff --git a/pkg/trace/trace.go b/pkg/trace/trace.go new file mode 100644 index 0000000..525c84c --- /dev/null +++ b/pkg/trace/trace.go @@ -0,0 +1,39 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/25 22:44:59 + +package trace + +import ( + "context" + + "github.com/avino-plan/postar/pkg/encode" +) + +var ( + traceIDKey struct{} // The context key of trace id. +) + +// NewTraceID returns a new trace id. +func NewTraceID() string { + salt := encode.RandomString(6) + return encode.Now() + salt[:3] + encode.PID() + salt[3:] +} + +// NewContext wraps ctx with a trace id. +func NewContext(ctx context.Context, traceID string) context.Context { + return context.WithValue(ctx, traceIDKey, traceID) +} + +// FromContext gets the trace id from context. +func FromContext(ctx context.Context) string { + traceID, ok := ctx.Value(traceIDKey).(string) + if !ok && traceID == "" { + return NewTraceID() + } + return traceID +} diff --git a/pkg/trace/trace_test.go b/pkg/trace/trace_test.go new file mode 100644 index 0000000..06d193d --- /dev/null +++ b/pkg/trace/trace_test.go @@ -0,0 +1,57 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/25 23:07:54 + +package trace + +import ( + "context" + "testing" +) + +// go test -v -cover -run=^TestNewTraceID$ +func TestNewTraceID(t *testing.T) { + traceID := NewTraceID() + if traceID == "" { + t.Error("traceID == ''") + } + + t.Log("traceID:", traceID) +} + +// go test -v -cover -run=^TestWithContext$ +func TestWithContext(t *testing.T) { + ctx := NewContext(context.Background(), NewTraceID()) + + value := ctx.Value(traceIDKey) + if value == nil { + t.Error("ctx.Value returns nil") + } + + traceID, ok := value.(string) + if !ok { + t.Errorf("value %+v isn't string", value) + } + t.Log("traceID:", traceID) +} + +// go test -v -cover -run=^TestFromContext$ +func TestFromContext(t *testing.T) { + ctx := context.Background() + traceIDInCtx := FromContext(ctx) + if traceIDInCtx == "" { + t.Error("traceIDInCtx == ''") + } + + traceID := NewTraceID() + ctx = context.WithValue(ctx, traceIDKey, traceID) + + traceIDInCtx = FromContext(ctx) + if traceIDInCtx != traceID { + t.Errorf("traceIDInCtx %s != traceId %s", traceIDInCtx, traceID) + } +} From 0d24c88868648ad9c34861c6bd6a3259b2e8f415 Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Sun, 23 Jan 2022 00:52:38 +0800 Subject: [PATCH 10/27] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=A4=A7=E9=87=8F?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FUTURE.md | 22 +- README.en.md | 6 +- README.md | 4 +- api/api.pb.go | 143 ++++++++++ api/api.proto | 20 ++ api/postard.pb.go | 352 ++++++++++-------------- api/postard.proto | 45 ++- api/postard_grpc.pb.go | 20 +- cmd/postard/main.go | 14 +- go.mod | 6 +- go.sum | 13 +- internal/{postard => }/biz/smtp.go | 49 ++-- internal/{postard => }/biz/smtp_test.go | 10 +- internal/{postard => }/model/smtp.go | 8 +- internal/{postard => }/server/grpc.go | 24 +- internal/{postard => }/server/http.go | 0 internal/{postard => }/server/udp.go | 0 internal/{postard => }/server/vex.go | 0 pkg/concurrency/pool.go | 140 ---------- pkg/concurrency/pool_test.go | 42 --- pkg/context/context.go | 29 -- pkg/context/context_test.go | 36 --- pkg/encode/encode.go | 14 +- pkg/encode/encode_test.go | 2 +- pkg/errors/errors.go | 97 +------ pkg/errors/errors_test.go | 21 +- pkg/trace/trace.go | 4 +- 27 files changed, 450 insertions(+), 671 deletions(-) create mode 100644 api/api.pb.go create mode 100644 api/api.proto rename internal/{postard => }/biz/smtp.go (55%) rename internal/{postard => }/biz/smtp_test.go (82%) rename internal/{postard => }/model/smtp.go (79%) rename internal/{postard => }/server/grpc.go (75%) rename internal/{postard => }/server/http.go (100%) rename internal/{postard => }/server/udp.go (100%) rename internal/{postard => }/server/vex.go (100%) delete mode 100644 pkg/concurrency/pool.go delete mode 100644 pkg/concurrency/pool_test.go delete mode 100644 pkg/context/context.go delete mode 100644 pkg/context/context_test.go diff --git a/FUTURE.md b/FUTURE.md index 90d7a7c..3614cc7 100644 --- a/FUTURE.md +++ b/FUTURE.md @@ -2,10 +2,18 @@ ### Future versions -* 支持邮件中携带附件 (P0) -* dialer 对象池或连接池优化 (P1) -* 邮件发送信息监控平台 (P2) -* 增加 Vex 远程调用接口 (P3) -* 增加 JsonRPC 远程调用接口 (P3) -* 增加 UDP 远程调用接口 (P3) -* 增加 WebSocket 远程调用接口 (P3) +* [ ] dialer 对象池或连接池优化 (P0) +* [ ] 增加 net/rpc 远程调用接口 (P1) + +### v0.2.x + +* [ ] 支持邮件中携带附件 (P0) +* [ ] 接入监控平台 (P0) +* [ ] 增加 http 远程调用接口 (P0) +* [x] 增加 grpc 远程调用接口 (P0) +* [ ] 增加 vex 远程调用接口 (P1) +* [ ] 增加 udp 远程调用接口 (P2) + +### v0.1.x + +* [x] 邮件发送功能 diff --git a/README.en.md b/README.en.md index c5914e5..f190ee8 100644 --- a/README.en.md +++ b/README.en.md @@ -43,6 +43,6 @@ If you find that something is not working as expected please open an _**issue**_ ### 📦 Projects postar used -| Project | Author | Description | link | -| -----------|--------|-------------|------------------| -| logit | FishGoddess | A high-performance and easy-to-use logging foundation | [Gitee](https://gitee.com/FishGoddess/logit) / [GitHub](https://github.com/FishGoddess/logit) | +| Project | Author | Description | link | +|---------|-------------|-------------------------------------------------------|-----------------------------------------------------------------------------------------------| +| logit | FishGoddess | A high-performance and easy-to-use logging foundation | [Gitee](https://gitee.com/FishGoddess/logit) / [GitHub](https://github.com/FishGoddess/logit) | diff --git a/README.md b/README.md index 377032d..112cd74 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ _注意:默认的配置文件路径是 `/opt/postar/conf/postar.ini`,默认 ### 📦 postar 使用的技术 -| 项目 | 作者 | 描述 | 链接 | -| -----------|--------|-------------|-------------------| +| 项目 | 作者 | 描述 | 链接 | +|-------|-------------|---------------------|--------------------------------------------------------------------------------------------| | logit | FishGoddess | 一个高性能、功能强大且极易上手的日志库 | [码云](https://gitee.com/FishGoddess/logit) / [GitHub](https://github.com/FishGoddess/logit) | diff --git a/api/api.pb.go b/api/api.pb.go new file mode 100644 index 0000000..9de5b13 --- /dev/null +++ b/api/api.pb.go @@ -0,0 +1,143 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/16 01:42:33 + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.0 +// source: api.proto + +package api + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// ResponseCodes is all codes of response. +type ServerCode int32 + +const ( + ServerCode_OK ServerCode = 0 + ServerCode_TIMEOUT ServerCode = 429 + ServerCode_SEND_EMAIL_FAILED ServerCode = 11000 +) + +// Enum value maps for ServerCode. +var ( + ServerCode_name = map[int32]string{ + 0: "OK", + 429: "TIMEOUT", + 11000: "SEND_EMAIL_FAILED", + } + ServerCode_value = map[string]int32{ + "OK": 0, + "TIMEOUT": 429, + "SEND_EMAIL_FAILED": 11000, + } +) + +func (x ServerCode) Enum() *ServerCode { + p := new(ServerCode) + *p = x + return p +} + +func (x ServerCode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ServerCode) Descriptor() protoreflect.EnumDescriptor { + return file_api_proto_enumTypes[0].Descriptor() +} + +func (ServerCode) Type() protoreflect.EnumType { + return &file_api_proto_enumTypes[0] +} + +func (x ServerCode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ServerCode.Descriptor instead. +func (ServerCode) EnumDescriptor() ([]byte, []int) { + return file_api_proto_rawDescGZIP(), []int{0} +} + +var File_api_proto protoreflect.FileDescriptor + +var file_api_proto_rawDesc = []byte{ + 0x0a, 0x09, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, + 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2a, 0x3a, 0x0a, 0x0a, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, + 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0xad, 0x03, + 0x12, 0x16, 0x0a, 0x11, 0x53, 0x45, 0x4e, 0x44, 0x5f, 0x45, 0x4d, 0x41, 0x49, 0x4c, 0x5f, 0x46, + 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0xf8, 0x55, 0x42, 0x21, 0x5a, 0x1f, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, + 0x2f, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_api_proto_rawDescOnce sync.Once + file_api_proto_rawDescData = file_api_proto_rawDesc +) + +func file_api_proto_rawDescGZIP() []byte { + file_api_proto_rawDescOnce.Do(func() { + file_api_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_proto_rawDescData) + }) + return file_api_proto_rawDescData +} + +var file_api_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_api_proto_goTypes = []interface{}{ + (ServerCode)(0), // 0: github.com.avinoplan.postar.api.ServerCode +} +var file_api_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_api_proto_init() } +func file_api_proto_init() { + if File_api_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_api_proto_rawDesc, + NumEnums: 1, + NumMessages: 0, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_api_proto_goTypes, + DependencyIndexes: file_api_proto_depIdxs, + EnumInfos: file_api_proto_enumTypes, + }.Build() + File_api_proto = out.File + file_api_proto_rawDesc = nil + file_api_proto_goTypes = nil + file_api_proto_depIdxs = nil +} diff --git a/api/api.proto b/api/api.proto new file mode 100644 index 0000000..be567c5 --- /dev/null +++ b/api/api.proto @@ -0,0 +1,20 @@ +// Copyright 2021 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2021/09/16 01:42:33 + +syntax = "proto3"; +package github.com.avinoplan.postar.api; + +// protoc -I=. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative api.proto +option go_package = "github.com/avinoplan/postar/api"; + +// ResponseCodes is all codes of response. +enum ServerCode { + OK = 0; + TIMEOUT = 429; + SEND_EMAIL_FAILED = 11000; +} diff --git a/api/postard.pb.go b/api/postard.pb.go index 5bb79f1..86d17ff 100644 --- a/api/postard.pb.go +++ b/api/postard.pb.go @@ -17,7 +17,6 @@ package api import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" ) @@ -29,70 +28,20 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -// ResponseCodes is all codes of response. -type ResponseCodes int32 - -const ( - ResponseCodes_OK ResponseCodes = 0 - ResponseCodes_InternalServerError ResponseCodes = 50000 - ResponseCodes_TimeoutError ResponseCodes = 50001 -) - -// Enum value maps for ResponseCodes. -var ( - ResponseCodes_name = map[int32]string{ - 0: "OK", - 50000: "InternalServerError", - 50001: "TimeoutError", - } - ResponseCodes_value = map[string]int32{ - "OK": 0, - "InternalServerError": 50000, - "TimeoutError": 50001, - } -) - -func (x ResponseCodes) Enum() *ResponseCodes { - p := new(ResponseCodes) - *p = x - return p -} - -func (x ResponseCodes) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ResponseCodes) Descriptor() protoreflect.EnumDescriptor { - return file_postard_proto_enumTypes[0].Descriptor() -} - -func (ResponseCodes) Type() protoreflect.EnumType { - return &file_postard_proto_enumTypes[0] -} - -func (x ResponseCodes) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ResponseCodes.Descriptor instead. -func (ResponseCodes) EnumDescriptor() ([]byte, []int) { - return file_postard_proto_rawDescGZIP(), []int{0} -} - -// PostardResponse is the response of Postard. -type PostardResponse struct { +// Email wraps all information of using smtp service. +type Email struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Code ResponseCodes `protobuf:"varint,1,opt,name=code,proto3,enum=github.com.avinoplan.postar.api.ResponseCodes" json:"code,omitempty"` // 0 is ok. - Msg string `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"` // For messaging. - TraceId string `protobuf:"bytes,3,opt,name=traceId,proto3" json:"traceId,omitempty"` // For tracing. - Data *anypb.Any `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` // Any data. + Receivers []string `protobuf:"bytes,1,rep,name=receivers,proto3" json:"receivers,omitempty"` // Receivers. + Subject string `protobuf:"bytes,2,opt,name=subject,proto3" json:"subject,omitempty"` // Subject. + BodyType string `protobuf:"bytes,3,opt,name=body_type,json=bodyType,proto3" json:"body_type,omitempty"` // Body type. + Body string `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"` // Body. } -func (x *PostardResponse) Reset() { - *x = PostardResponse{} +func (x *Email) Reset() { + *x = Email{} if protoimpl.UnsafeEnabled { mi := &file_postard_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -100,13 +49,13 @@ func (x *PostardResponse) Reset() { } } -func (x *PostardResponse) String() string { +func (x *Email) String() string { return protoimpl.X.MessageStringOf(x) } -func (*PostardResponse) ProtoMessage() {} +func (*Email) ProtoMessage() {} -func (x *PostardResponse) ProtoReflect() protoreflect.Message { +func (x *Email) ProtoReflect() protoreflect.Message { mi := &file_postard_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -118,53 +67,51 @@ func (x *PostardResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use PostardResponse.ProtoReflect.Descriptor instead. -func (*PostardResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use Email.ProtoReflect.Descriptor instead. +func (*Email) Descriptor() ([]byte, []int) { return file_postard_proto_rawDescGZIP(), []int{0} } -func (x *PostardResponse) GetCode() ResponseCodes { +func (x *Email) GetReceivers() []string { if x != nil { - return x.Code + return x.Receivers } - return ResponseCodes_OK + return nil } -func (x *PostardResponse) GetMsg() string { +func (x *Email) GetSubject() string { if x != nil { - return x.Msg + return x.Subject } return "" } -func (x *PostardResponse) GetTraceId() string { +func (x *Email) GetBodyType() string { if x != nil { - return x.TraceId + return x.BodyType } return "" } -func (x *PostardResponse) GetData() *anypb.Any { +func (x *Email) GetBody() string { if x != nil { - return x.Data + return x.Body } - return nil + return "" } -// Email wraps all information of using smtp biz. -type Email struct { +// SendEmailOptions is the options of sending emails. +type SendEmailOptions struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - To []string `protobuf:"bytes,1,rep,name=to,proto3" json:"to,omitempty"` // The receivers of one email. - Subject string `protobuf:"bytes,2,opt,name=subject,proto3" json:"subject,omitempty"` // The subject of one email. - BodyType string `protobuf:"bytes,3,opt,name=bodyType,proto3" json:"bodyType,omitempty"` // The content type of body. - Body string `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"` // The body of one email. + Async bool `protobuf:"varint,1,opt,name=async,proto3" json:"async,omitempty"` // Sending this email asynchronously. + Timeout int32 `protobuf:"varint,2,opt,name=timeout,proto3" json:"timeout,omitempty"` // Sending timeout in seconds. } -func (x *Email) Reset() { - *x = Email{} +func (x *SendEmailOptions) Reset() { + *x = SendEmailOptions{} if protoimpl.UnsafeEnabled { mi := &file_postard_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -172,13 +119,13 @@ func (x *Email) Reset() { } } -func (x *Email) String() string { +func (x *SendEmailOptions) String() string { return protoimpl.X.MessageStringOf(x) } -func (*Email) ProtoMessage() {} +func (*SendEmailOptions) ProtoMessage() {} -func (x *Email) ProtoReflect() protoreflect.Message { +func (x *SendEmailOptions) ProtoReflect() protoreflect.Message { mi := &file_postard_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -190,51 +137,37 @@ func (x *Email) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use Email.ProtoReflect.Descriptor instead. -func (*Email) Descriptor() ([]byte, []int) { +// Deprecated: Use SendEmailOptions.ProtoReflect.Descriptor instead. +func (*SendEmailOptions) Descriptor() ([]byte, []int) { return file_postard_proto_rawDescGZIP(), []int{1} } -func (x *Email) GetTo() []string { - if x != nil { - return x.To - } - return nil -} - -func (x *Email) GetSubject() string { - if x != nil { - return x.Subject - } - return "" -} - -func (x *Email) GetBodyType() string { +func (x *SendEmailOptions) GetAsync() bool { if x != nil { - return x.BodyType + return x.Async } - return "" + return false } -func (x *Email) GetBody() string { +func (x *SendEmailOptions) GetTimeout() int32 { if x != nil { - return x.Body + return x.Timeout } - return "" + return 0 } -// SendEmailOptions is the options of sending emails. -type SendEmailOptions struct { +// SendEmailRequest is the request of SendEmail. +type SendEmailRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Async bool `protobuf:"varint,1,opt,name=async,proto3" json:"async,omitempty"` // If need sending emails asynchronously. - Timeout int64 `protobuf:"varint,2,opt,name=timeout,proto3" json:"timeout,omitempty"` // Sending timeout. + Email *Email `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` // Sending email. + Options *SendEmailOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` // Sending options. } -func (x *SendEmailOptions) Reset() { - *x = SendEmailOptions{} +func (x *SendEmailRequest) Reset() { + *x = SendEmailRequest{} if protoimpl.UnsafeEnabled { mi := &file_postard_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -242,13 +175,13 @@ func (x *SendEmailOptions) Reset() { } } -func (x *SendEmailOptions) String() string { +func (x *SendEmailRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SendEmailOptions) ProtoMessage() {} +func (*SendEmailRequest) ProtoMessage() {} -func (x *SendEmailOptions) ProtoReflect() protoreflect.Message { +func (x *SendEmailRequest) ProtoReflect() protoreflect.Message { mi := &file_postard_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -260,37 +193,38 @@ func (x *SendEmailOptions) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SendEmailOptions.ProtoReflect.Descriptor instead. -func (*SendEmailOptions) Descriptor() ([]byte, []int) { +// Deprecated: Use SendEmailRequest.ProtoReflect.Descriptor instead. +func (*SendEmailRequest) Descriptor() ([]byte, []int) { return file_postard_proto_rawDescGZIP(), []int{2} } -func (x *SendEmailOptions) GetAsync() bool { +func (x *SendEmailRequest) GetEmail() *Email { if x != nil { - return x.Async + return x.Email } - return false + return nil } -func (x *SendEmailOptions) GetTimeout() int64 { +func (x *SendEmailRequest) GetOptions() *SendEmailOptions { if x != nil { - return x.Timeout + return x.Options } - return 0 + return nil } -// SendEmailRequest is the request of SendEmail. -type SendEmailRequest struct { +// SendEmailResponse is the response of SendEmail. +type SendEmailResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Email *Email `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` // Sending email. - Options *SendEmailOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` // Sending options. + Code ServerCode `protobuf:"varint,1,opt,name=code,proto3,enum=github.com.avinoplan.postar.api.ServerCode" json:"code,omitempty"` // 0 is ok. + Msg string `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"` // For messaging. + TraceId string `protobuf:"bytes,3,opt,name=trace_id,json=traceId,proto3" json:"trace_id,omitempty"` // For tracing. } -func (x *SendEmailRequest) Reset() { - *x = SendEmailRequest{} +func (x *SendEmailResponse) Reset() { + *x = SendEmailResponse{} if protoimpl.UnsafeEnabled { mi := &file_postard_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -298,13 +232,13 @@ func (x *SendEmailRequest) Reset() { } } -func (x *SendEmailRequest) String() string { +func (x *SendEmailResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SendEmailRequest) ProtoMessage() {} +func (*SendEmailResponse) ProtoMessage() {} -func (x *SendEmailRequest) ProtoReflect() protoreflect.Message { +func (x *SendEmailResponse) ProtoReflect() protoreflect.Message { mi := &file_postard_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -316,23 +250,30 @@ func (x *SendEmailRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SendEmailRequest.ProtoReflect.Descriptor instead. -func (*SendEmailRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use SendEmailResponse.ProtoReflect.Descriptor instead. +func (*SendEmailResponse) Descriptor() ([]byte, []int) { return file_postard_proto_rawDescGZIP(), []int{3} } -func (x *SendEmailRequest) GetEmail() *Email { +func (x *SendEmailResponse) GetCode() ServerCode { if x != nil { - return x.Email + return x.Code } - return nil + return ServerCode_OK } -func (x *SendEmailRequest) GetOptions() *SendEmailOptions { +func (x *SendEmailResponse) GetMsg() string { if x != nil { - return x.Options + return x.Msg } - return nil + return "" +} + +func (x *SendEmailResponse) GetTraceId() string { + if x != nil { + return x.TraceId + } + return "" } var File_postard_proto protoreflect.FileDescriptor @@ -341,55 +282,47 @@ var file_postard_proto_rawDesc = []byte{ 0x0a, 0x0d, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xab, 0x01, 0x0a, 0x0f, - 0x50, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x42, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2e, 0x2e, + 0x1a, 0x09, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x70, 0x0a, 0x05, 0x45, + 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, + 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1b, 0x0a, 0x09, + 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x62, 0x6f, 0x64, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, + 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x42, 0x0a, + 0x10, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x22, 0x9d, 0x01, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, + 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x05, 0x65, + 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x4b, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, + 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, + 0x6c, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x22, 0x81, 0x01, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, + 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, + 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, + 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x72, + 0x61, 0x63, 0x65, 0x49, 0x64, 0x32, 0x7d, 0x0a, 0x07, 0x50, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, + 0x12, 0x72, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x31, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x52, 0x04, 0x63, - 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, - 0x28, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x41, 0x6e, 0x79, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x61, 0x0a, 0x05, 0x45, 0x6d, 0x61, - 0x69, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x02, - 0x74, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1a, 0x0a, 0x08, - 0x62, 0x6f, 0x64, 0x79, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x62, 0x6f, 0x64, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x42, 0x0a, 0x10, - 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, - 0x22, 0x9d, 0x01, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, - 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x05, 0x65, 0x6d, - 0x61, 0x69, 0x6c, 0x12, 0x4b, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, - 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, - 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x2a, 0x46, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x64, 0x65, - 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x13, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x72, 0x72, 0x6f, 0x72, - 0x10, 0xd0, 0x86, 0x03, 0x12, 0x12, 0x0a, 0x0c, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x45, - 0x72, 0x72, 0x6f, 0x72, 0x10, 0xd1, 0x86, 0x03, 0x32, 0x7b, 0x0a, 0x07, 0x50, 0x6f, 0x73, 0x74, - 0x61, 0x72, 0x64, 0x12, 0x70, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, - 0x12, 0x31, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, + 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x32, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, - 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x22, 0x5a, 0x20, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x2d, 0x70, 0x6c, 0x61, 0x6e, 0x2f, 0x70, - 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x70, 0x69, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x21, 0x5a, 0x1f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2f, 0x70, 0x6f, 0x73, + 0x74, 0x61, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -404,28 +337,25 @@ func file_postard_proto_rawDescGZIP() []byte { return file_postard_proto_rawDescData } -var file_postard_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_postard_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_postard_proto_goTypes = []interface{}{ - (ResponseCodes)(0), // 0: github.com.avinoplan.postar.api.ResponseCodes - (*PostardResponse)(nil), // 1: github.com.avinoplan.postar.api.PostardResponse - (*Email)(nil), // 2: github.com.avinoplan.postar.api.Email - (*SendEmailOptions)(nil), // 3: github.com.avinoplan.postar.api.SendEmailOptions - (*SendEmailRequest)(nil), // 4: github.com.avinoplan.postar.api.SendEmailRequest - (*anypb.Any)(nil), // 5: google.protobuf.Any + (*Email)(nil), // 0: github.com.avinoplan.postar.api.Email + (*SendEmailOptions)(nil), // 1: github.com.avinoplan.postar.api.SendEmailOptions + (*SendEmailRequest)(nil), // 2: github.com.avinoplan.postar.api.SendEmailRequest + (*SendEmailResponse)(nil), // 3: github.com.avinoplan.postar.api.SendEmailResponse + (ServerCode)(0), // 4: github.com.avinoplan.postar.api.ServerCode } var file_postard_proto_depIdxs = []int32{ - 0, // 0: github.com.avinoplan.postar.api.PostardResponse.code:type_name -> github.com.avinoplan.postar.api.ResponseCodes - 5, // 1: github.com.avinoplan.postar.api.PostardResponse.data:type_name -> google.protobuf.Any - 2, // 2: github.com.avinoplan.postar.api.SendEmailRequest.email:type_name -> github.com.avinoplan.postar.api.Email - 3, // 3: github.com.avinoplan.postar.api.SendEmailRequest.options:type_name -> github.com.avinoplan.postar.api.SendEmailOptions - 4, // 4: github.com.avinoplan.postar.api.Postard.SendEmail:input_type -> github.com.avinoplan.postar.api.SendEmailRequest - 1, // 5: github.com.avinoplan.postar.api.Postard.SendEmail:output_type -> github.com.avinoplan.postar.api.PostardResponse - 5, // [5:6] is the sub-list for method output_type - 4, // [4:5] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 0, // 0: github.com.avinoplan.postar.api.SendEmailRequest.email:type_name -> github.com.avinoplan.postar.api.Email + 1, // 1: github.com.avinoplan.postar.api.SendEmailRequest.options:type_name -> github.com.avinoplan.postar.api.SendEmailOptions + 4, // 2: github.com.avinoplan.postar.api.SendEmailResponse.code:type_name -> github.com.avinoplan.postar.api.ServerCode + 2, // 3: github.com.avinoplan.postar.api.Postard.SendEmail:input_type -> github.com.avinoplan.postar.api.SendEmailRequest + 3, // 4: github.com.avinoplan.postar.api.Postard.SendEmail:output_type -> github.com.avinoplan.postar.api.SendEmailResponse + 4, // [4:5] is the sub-list for method output_type + 3, // [3:4] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_postard_proto_init() } @@ -433,9 +363,10 @@ func file_postard_proto_init() { if File_postard_proto != nil { return } + file_api_proto_init() if !protoimpl.UnsafeEnabled { file_postard_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PostardResponse); i { + switch v := v.(*Email); i { case 0: return &v.state case 1: @@ -447,7 +378,7 @@ func file_postard_proto_init() { } } file_postard_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Email); i { + switch v := v.(*SendEmailOptions); i { case 0: return &v.state case 1: @@ -459,7 +390,7 @@ func file_postard_proto_init() { } } file_postard_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SendEmailOptions); i { + switch v := v.(*SendEmailRequest); i { case 0: return &v.state case 1: @@ -471,7 +402,7 @@ func file_postard_proto_init() { } } file_postard_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SendEmailRequest); i { + switch v := v.(*SendEmailResponse); i { case 0: return &v.state case 1: @@ -488,14 +419,13 @@ func file_postard_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_postard_proto_rawDesc, - NumEnums: 1, + NumEnums: 0, NumMessages: 4, NumExtensions: 0, NumServices: 1, }, GoTypes: file_postard_proto_goTypes, DependencyIndexes: file_postard_proto_depIdxs, - EnumInfos: file_postard_proto_enumTypes, MessageInfos: file_postard_proto_msgTypes, }.Build() File_postard_proto = out.File diff --git a/api/postard.proto b/api/postard.proto index dce3bae..3a153f3 100644 --- a/api/postard.proto +++ b/api/postard.proto @@ -5,41 +5,28 @@ // Author: FishGoddess // Email: fishgoddess@qq.com // Created at 2021/09/16 01:42:33 -syntax = "proto3"; -package github.com.avinoplan.postar.api; -// protoc -I=. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative postard.proto -option go_package = "github.com/avino-plan/postar/api"; +syntax = "proto3"; -import "google/protobuf/any.proto"; +package github.com.avinoplan.postar.api; -// ResponseCodes is all codes of response. -enum ResponseCodes { - OK = 0; - InternalServerError = 50000; - TimeoutError = 50001; -} +import "api.proto"; -// PostardResponse is the response of Postard. -message PostardResponse { - ResponseCodes code = 1; // 0 is ok. - string msg = 2; // For messaging. - string traceId = 3; // For tracing. - google.protobuf.Any data = 4; // Any data. -} +// protoc -I=. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative postard.proto +option go_package = "github.com/avinoplan/postar/api"; // Email wraps all information of using smtp service. message Email { - repeated string to = 1; // The receivers of one email. - string subject = 2; // The subject of one email. - string bodyType = 3; // The content type of body. - string body = 4; // The body of one email. + repeated string receivers = 1; // Receivers. + string subject = 2; // Subject. + string body_type = 3; // Body type. + string body = 4; // Body. } // SendEmailOptions is the options of sending emails. message SendEmailOptions { - bool async = 1; // If need sending emails asynchronously. - int64 timeout = 2; // Sending timeout. + bool async = 1; // Sending this email asynchronously. + int32 timeout = 2; // Sending timeout in seconds. } // SendEmailRequest is the request of SendEmail. @@ -48,7 +35,15 @@ message SendEmailRequest { SendEmailOptions options = 2; // Sending options. } +// SendEmailResponse is the response of SendEmail. +message SendEmailResponse { + ServerCode code = 1; // 0 is ok. + string msg = 2; // For messaging. + string trace_id = 3; // For tracing. +} + // Postard is the core service of postar. service Postard { - rpc SendEmail(SendEmailRequest) returns (PostardResponse); // For sending emails. + // SendEmail send one email. + rpc SendEmail(SendEmailRequest) returns (SendEmailResponse); } \ No newline at end of file diff --git a/api/postard_grpc.pb.go b/api/postard_grpc.pb.go index 05b575f..7a9da98 100644 --- a/api/postard_grpc.pb.go +++ b/api/postard_grpc.pb.go @@ -14,11 +14,12 @@ import ( // Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 -// PostardClient is the client API for Postard biz. +// PostardClient is the client API for Postard service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type PostardClient interface { - SendEmail(ctx context.Context, in *SendEmailRequest, opts ...grpc.CallOption) (*PostardResponse, error) + // SendEmail send one email. + SendEmail(ctx context.Context, in *SendEmailRequest, opts ...grpc.CallOption) (*SendEmailResponse, error) } type postardClient struct { @@ -29,8 +30,8 @@ func NewPostardClient(cc grpc.ClientConnInterface) PostardClient { return &postardClient{cc} } -func (c *postardClient) SendEmail(ctx context.Context, in *SendEmailRequest, opts ...grpc.CallOption) (*PostardResponse, error) { - out := new(PostardResponse) +func (c *postardClient) SendEmail(ctx context.Context, in *SendEmailRequest, opts ...grpc.CallOption) (*SendEmailResponse, error) { + out := new(SendEmailResponse) err := c.cc.Invoke(ctx, "/github.com.avinoplan.postar.api.Postard/SendEmail", in, out, opts...) if err != nil { return nil, err @@ -38,11 +39,12 @@ func (c *postardClient) SendEmail(ctx context.Context, in *SendEmailRequest, opt return out, nil } -// PostardServer is the server API for Postard biz. +// PostardServer is the server API for Postard service. // All implementations must embed UnimplementedPostardServer // for forward compatibility type PostardServer interface { - SendEmail(context.Context, *SendEmailRequest) (*PostardResponse, error) + // SendEmail send one email. + SendEmail(context.Context, *SendEmailRequest) (*SendEmailResponse, error) mustEmbedUnimplementedPostardServer() } @@ -50,12 +52,12 @@ type PostardServer interface { type UnimplementedPostardServer struct { } -func (UnimplementedPostardServer) SendEmail(context.Context, *SendEmailRequest) (*PostardResponse, error) { +func (UnimplementedPostardServer) SendEmail(context.Context, *SendEmailRequest) (*SendEmailResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SendEmail not implemented") } func (UnimplementedPostardServer) mustEmbedUnimplementedPostardServer() {} -// UnsafePostardServer may be embedded to opt out of forward compatibility for this biz. +// UnsafePostardServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to PostardServer will // result in compilation errors. type UnsafePostardServer interface { @@ -84,7 +86,7 @@ func _Postard_SendEmail_Handler(srv interface{}, ctx context.Context, dec func(i return interceptor(ctx, in, info, handler) } -// Postard_ServiceDesc is the grpc.ServiceDesc for Postard biz. +// Postard_ServiceDesc is the grpc.ServiceDesc for Postard service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var Postard_ServiceDesc = grpc.ServiceDesc{ diff --git a/cmd/postard/main.go b/cmd/postard/main.go index c915b42..9ff1bf7 100644 --- a/cmd/postard/main.go +++ b/cmd/postard/main.go @@ -12,15 +12,19 @@ import ( "net" "github.com/FishGoddess/logit" - "github.com/avino-plan/postar/internal/postard/biz" - "github.com/avino-plan/postar/internal/postard/server" - "github.com/avino-plan/postar/pkg/concurrency" + "github.com/avinoplan/postar/internal/biz" + "github.com/avinoplan/postar/internal/server" + "github.com/panjf2000/ants/v2" ) func main() { logger := logit.NewLogger() - pool := concurrency.NewPool() - + pool, err := ants.NewPool(64) + if err != nil { + panic(err) + } + defer pool.Release() + smtpBiz := biz.NewSmtpBiz(pool, "", 0, "", "") svr := server.NewGrpcServer(logger, smtpBiz) diff --git a/go.mod b/go.mod index 4fd2fcf..aa6fdc3 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,11 @@ -module github.com/avino-plan/postar +module github.com/avinoplan/postar go 1.15 require ( - github.com/FishGoddess/logit v0.4.7-alpha + github.com/FishGoddess/errors v0.0.2 + github.com/FishGoddess/logit v0.4.11 + github.com/panjf2000/ants/v2 v2.4.7 google.golang.org/grpc v1.40.0 google.golang.org/protobuf v1.27.1 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect diff --git a/go.sum b/go.sum index 3bcf639..8077208 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,15 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/FishGoddess/errors v0.0.2 h1:g1S1HDrnjSuMS0sl92lyE+tXsfTn7Dp/lKlOvAPN5DA= +github.com/FishGoddess/errors v0.0.2/go.mod h1:2YWw8ijAMQIfs4FyOFjvahUPQWhgS6lFTkBG++n61+Y= github.com/FishGoddess/logit v0.4.7-alpha h1:6pYVA+Qp+KFr/vB7Z4ad9JIhq7Nm9QMWBMcES9QYtvU= github.com/FishGoddess/logit v0.4.7-alpha/go.mod h1:a2dFSibwfZXNNZ/V1jMOQBImPoySaxdPFThbsm17ZdY= +github.com/FishGoddess/logit v0.4.11 h1:d/KQf4eONmuTg1EVGJ+yQaK+Q/oANsE0G+BEI6hzZC4= +github.com/FishGoddess/logit v0.4.11/go.mod h1:a2dFSibwfZXNNZ/V1jMOQBImPoySaxdPFThbsm17ZdY= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/avino-plan/postar v0.2.3-alpha h1:4q/OiQl6qJBBfkQbo8S2b2aLtEKAikJ6+rILCt9DQ+s= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -12,6 +17,7 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -43,13 +49,15 @@ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/panjf2000/ants v1.3.0 h1:8pQ+8leaLc9lys2viEEr8md0U4RN6uOSUCE9bOYjQ9M= +github.com/panjf2000/ants/v2 v2.4.7 h1:MZnw2JRyTJxFwtaMtUJcwE618wKD04POWk2gwwP4E2M= +github.com/panjf2000/ants/v2 v2.4.7/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -120,5 +128,6 @@ gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AW gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/postard/biz/smtp.go b/internal/biz/smtp.go similarity index 55% rename from internal/postard/biz/smtp.go rename to internal/biz/smtp.go index 6a35130..c977261 100644 --- a/internal/postard/biz/smtp.go +++ b/internal/biz/smtp.go @@ -12,23 +12,23 @@ import ( "context" "github.com/FishGoddess/logit" - "github.com/avino-plan/postar/internal/postard/model" - "github.com/avino-plan/postar/pkg/concurrency" - "github.com/avino-plan/postar/pkg/errors" + "github.com/avinoplan/postar/internal/model" + "github.com/avinoplan/postar/pkg/errors" + "github.com/panjf2000/ants/v2" "gopkg.in/gomail.v2" ) // SmtpBiz is the biz of smtp. type SmtpBiz struct { - pool *concurrency.Pool // The pool of workers. - host string // The host of smtp server. - port int // The port of smtp server. - user string // The user of smtp server. - password string // The password of smtp server. + pool *ants.Pool // The pool of workers. + host string // The host of smtp server. + port int // The port of smtp server. + user string // The user of smtp server. + password string // The password of smtp server. } // NewSmtpBiz returns a new SmtpBiz. -func NewSmtpBiz(pool *concurrency.Pool, host string, port int, user string, password string) *SmtpBiz { +func NewSmtpBiz(pool *ants.Pool, host string, port int, user string, password string) *SmtpBiz { return &SmtpBiz{ pool: pool, host: host, @@ -54,29 +54,40 @@ func (sb *SmtpBiz) SendEmail(ctx context.Context, email *model.Email, options *m if options == nil { options = model.DefaultSendEmailOptions() - logger.Debug("options is nil, using DefaultSendEmailOptions()").Any("options", options).End() + logger.Debug("options is nil, using default options").Any("options", options).End() } ctx, cancel := context.WithTimeout(ctx, options.Timeout) defer cancel() - errorCh := sb.pool.Go(ctx, func(ctx context.Context) error { return sb.sendEmail(email) }) + errorCh := make(chan error, 1) + err := sb.pool.Submit(func() { + defer close(errorCh) + errorCh <- sb.sendEmail(email) + }) + + if err != nil { + logger.Error("submit email sending task to pool failed").Error("err", err).End() + return errors.SendEmailFailedErr(err) + } + if options.Async { return nil } select { - case e := <-errorCh: - if e != nil { - logger.Error("send email failed").Error("e", e).End() - return errors.SendEmailFailedErr(e) + case err = <-errorCh: + if err != nil { + logger.Error("send email failed").Error("err", err).End() + return errors.SendEmailFailedErr(err) } case <-ctx.Done(): - e := ctx.Err() - if e != nil { - logger.Error("send email timeout").Error("e", e).End() - return errors.SendTimeoutErr(e) + err = ctx.Err() + if err != nil { + logger.Error("send email timeout").Error("err", err).End() + return errors.TimeoutErr(err) } } + return nil } diff --git a/internal/postard/biz/smtp_test.go b/internal/biz/smtp_test.go similarity index 82% rename from internal/postard/biz/smtp_test.go rename to internal/biz/smtp_test.go index d4c7677..2b57558 100644 --- a/internal/postard/biz/smtp_test.go +++ b/internal/biz/smtp_test.go @@ -15,8 +15,8 @@ import ( "testing" "time" - "github.com/avino-plan/postar/internal/postard/model" - "github.com/avino-plan/postar/pkg/concurrency" + "github.com/avinoplan/postar/internal/model" + "github.com/panjf2000/ants/v2" ) // go test -v -cover -run=^TestSmtpBiz$ @@ -34,10 +34,10 @@ func TestSmtpBiz(t *testing.T) { port = 587 } - pool := concurrency.NewPool().Start() - defer pool.Stop() + pool, _ := ants.NewPool(64) + defer pool.Release() - smtpService := NewSmtpService(pool, host, int(port), user, password) + smtpService := NewSmtpBiz(pool, host, int(port), user, password) err = smtpService.SendEmail(context.Background(), &model.Email{ To: []string{to}, Subject: t.Name(), diff --git a/internal/postard/model/smtp.go b/internal/model/smtp.go similarity index 79% rename from internal/postard/model/smtp.go rename to internal/model/smtp.go index d783b32..46d40f6 100644 --- a/internal/postard/model/smtp.go +++ b/internal/model/smtp.go @@ -12,10 +12,10 @@ import "time" // Email is an email. type Email struct { - To []string // The receivers of one email. - Subject string // The subject of one email. - BodyType string // The content type of body. - Body string // The body of one email. + To []string // Receivers. + Subject string // Subject. + BodyType string // Body type. + Body string // Body. } // SendEmailOptions is the options of sending one email. diff --git a/internal/postard/server/grpc.go b/internal/server/grpc.go similarity index 75% rename from internal/postard/server/grpc.go rename to internal/server/grpc.go index 4204179..c67f742 100644 --- a/internal/postard/server/grpc.go +++ b/internal/server/grpc.go @@ -13,10 +13,10 @@ import ( "net" "github.com/FishGoddess/logit" - "github.com/avino-plan/postar/api" - "github.com/avino-plan/postar/internal/postard/biz" - "github.com/avino-plan/postar/pkg/errors" - "github.com/avino-plan/postar/pkg/trace" + "github.com/avinoplan/postar/api" + "github.com/avinoplan/postar/internal/biz" + "github.com/avinoplan/postar/pkg/errors" + "github.com/avinoplan/postar/pkg/trace" "google.golang.org/grpc" ) @@ -37,30 +37,30 @@ func NewGrpcServer(logger *logit.Logger, smtpBiz *biz.SmtpBiz) *GRPCServer { } // SendEmail sends emails. -func (gs *GRPCServer) SendEmail(ctx context.Context, request *api.SendEmailRequest) (*api.PostardResponse, error) { +func (gs *GRPCServer) SendEmail(ctx context.Context, request *api.SendEmailRequest) (*api.SendEmailResponse, error) { traceID := trace.NewTraceID() ctx = trace.NewContext(ctx, traceID) ctx = logit.NewContext(ctx, gs.logger) err := gs.smtpBiz.SendEmail(ctx, nil, nil) - if errors.IsSendTimeout(err) { - return &api.PostardResponse{ - Code: api.ResponseCodes_TimeoutError, + if errors.IsTimeout(err) { + return &api.SendEmailResponse{ + Code: api.ServerCode_TIMEOUT, Msg: "send email timeout", TraceId: traceID, }, nil } if err != nil { - return &api.PostardResponse{ - Code: api.ResponseCodes_InternalServerError, + return &api.SendEmailResponse{ + Code: api.ServerCode_SEND_EMAIL_FAILED, Msg: "send email failed", TraceId: traceID, }, nil } - return &api.PostardResponse{ - Code: api.ResponseCodes_OK, + return &api.SendEmailResponse{ + Code: api.ServerCode_OK, TraceId: traceID, }, nil } diff --git a/internal/postard/server/http.go b/internal/server/http.go similarity index 100% rename from internal/postard/server/http.go rename to internal/server/http.go diff --git a/internal/postard/server/udp.go b/internal/server/udp.go similarity index 100% rename from internal/postard/server/udp.go rename to internal/server/udp.go diff --git a/internal/postard/server/vex.go b/internal/server/vex.go similarity index 100% rename from internal/postard/server/vex.go rename to internal/server/vex.go diff --git a/pkg/concurrency/pool.go b/pkg/concurrency/pool.go deleted file mode 100644 index 7603f22..0000000 --- a/pkg/concurrency/pool.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/09/16 22:05:23 - -package concurrency - -import ( - "context" - "sync" - "sync/atomic" -) - -// PoolOptions is the options of Pool. -type PoolOptions func(pool *Pool) - -// WithMaxWorkers sets maxWorkers of pool. -func WithMaxWorkers(maxWorkers int) PoolOptions { - return func(pool *Pool) { - pool.maxWorkers = maxWorkers - pool.taskQueues = make([]chan func(), maxWorkers) - } -} - -// WithMaxWorkerTasks sets maxWorkerTasks of pool. -func WithMaxWorkerTasks(maxWorkerTasks int) PoolOptions { - return func(pool *Pool) { - pool.maxWorkerTasks = maxWorkerTasks - } -} - -// Pool is a set of goroutines. -// This is for controlling the number of goroutines and managing their lifecycles. -type Pool struct { - // maxWorkers is the max number of workers. - maxWorkers int - - // maxWorkerTasks is the max number of one task queue. - // The Go() method will block until the task queue has enough capacity. - maxWorkerTasks int - - // currentQueue records the current sequence of task queues. - currentQueue int64 - - // taskQueues stores all task queues. - // Every worker has its own task queue. - taskQueues []chan func() - - // onRecover is a function which will call in defer after finishing task. - onRecover func(cause interface{}) - - // wg is for managing all goroutines. - wg *sync.WaitGroup -} - -// NewPool returns a new Pool holder. -func NewPool(options ...PoolOptions) *Pool { - pool := &Pool{ - maxWorkers: 64, - maxWorkerTasks: 1024, - taskQueues: make([]chan func(), 64), - currentQueue: -1, - wg: &sync.WaitGroup{}, - } - - for _, option := range options { - option(pool) - } - return pool -} - -// OnRecover sets onRecover to p. -func (p *Pool) OnRecover(onRecover func(cause interface{})) { - p.onRecover = onRecover -} - -// Start starts all workers in pool and starts receiving tasks. -func (p *Pool) Start() *Pool { - for i := 0; i < p.maxWorkers; i++ { - taskQueue := make(chan func(), p.maxWorkerTasks) - p.taskQueues[i] = taskQueue - - p.wg.Add(1) - go func() { - defer p.wg.Done() - - for task := range taskQueue { - task() - } - }() - } - return p -} - -// nextTaskQueue returns next task queue. -func (p *Pool) nextTaskQueue() chan<- func() { - currentQueue := atomic.AddInt64(&p.currentQueue, 1) - if currentQueue >= int64(p.maxWorkers)-1 { - atomic.StoreInt64(&p.currentQueue, -1) - } - return p.taskQueues[currentQueue] -} - -// wrapTask wraps task to a complete task of pool. -func (p *Pool) wrapTask(ctx context.Context, fn func(ctx context.Context) error, errorCh chan<- error) func() { - return func() { - var err error - defer func() { - errorCh <- err - if cause := recover(); p.onRecover != nil { - p.onRecover(cause) // Notice: Just do some simple records which don't panic... - } - }() - err = fn(ctx) - } -} - -// Go sends the task to task queue and waits for executing. -func (p *Pool) Go(ctx context.Context, fn func(ctx context.Context) error) <-chan error { - errorCh := make(chan error, 1) - - taskQueue := p.nextTaskQueue() - select { - case taskQueue <- p.wrapTask(ctx, fn, errorCh): - case <-ctx.Done(): - errorCh <- ctx.Err() - } - return errorCh -} - -// Stop closes all task queues and waits for all workers to be shutdown. -func (p *Pool) Stop() { - for _, taskQueue := range p.taskQueues { - close(taskQueue) - } - p.wg.Wait() -} diff --git a/pkg/concurrency/pool_test.go b/pkg/concurrency/pool_test.go deleted file mode 100644 index 4e51c5a..0000000 --- a/pkg/concurrency/pool_test.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/09/16 23:07:51 - -package concurrency - -import ( - "context" - "testing" - "time" -) - -// go test -v -cover -run=^TestNewPool$ -func TestNewPool(t *testing.T) { - ctx := context.Background() - pool := NewPool(WithMaxWorkers(4), WithMaxWorkerTasks(16)).Start() - - numbers := [1000]int{} - for i := 0; i < 1000; i++ { - no := i - errorCh := pool.Go(ctx, func(ctx context.Context) error { - numbers[no] = no - return nil - }) - if err := <-errorCh; err != nil { - t.Error(err) - } - } - - time.Sleep(time.Second) - pool.Stop() - - for i, num := range numbers { - if i != num { - t.Errorf("i %d != num %d", i, num) - } - } -} diff --git a/pkg/context/context.go b/pkg/context/context.go deleted file mode 100644 index b0aa994..0000000 --- a/pkg/context/context.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/09/27 22:40:58 - -package context - -import ( - "context" - - "github.com/FishGoddess/logit" - "github.com/avino-plan/postar/pkg/trace" -) - -// WithLogger wraps ctx with logger. -func WithLogger(ctx context.Context, logger *logit.Logger) context.Context { - return logit.NewContext(ctx, logger) -} - -// WithTraceID wraps ctx with traceID. -func WithTraceID(ctx context.Context, traceID string) context.Context { - if traceID == "" { - traceID = trace.NewTraceID() - } - return trace.NewContext(ctx, traceID) -} diff --git a/pkg/context/context_test.go b/pkg/context/context_test.go deleted file mode 100644 index cad2cdc..0000000 --- a/pkg/context/context_test.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2021 Ye Zi Jie. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. -// -// Author: FishGoddess -// Email: fishgoddess@qq.com -// Created at 2021/09/27 22:50:56 - -package context - -import ( - "context" - "testing" - - "github.com/FishGoddess/logit" - "github.com/avino-plan/postar/pkg/trace" -) - -// go test -v -cover -run=^TestWithLogger$ -func TestWithLogger(t *testing.T) { - logger := logit.NewLogger() - ctx := WithLogger(context.Background(), logger) - contextLogger := logit.FromContext(ctx) - if contextLogger != logger { - t.Errorf("contextLogger %+v != logger %+v", contextLogger, logger) - } -} - -// go test -v -cover -run=^TestWithTraceID$ -func TestWithTraceID(t *testing.T) { - ctx := WithTraceID(context.Background()) - traceId := trace.FromContext(ctx) - if traceId == "" { - t.Error("traceId == ''") - } -} diff --git a/pkg/encode/encode.go b/pkg/encode/encode.go index f6e8609..851d529 100644 --- a/pkg/encode/encode.go +++ b/pkg/encode/encode.go @@ -31,6 +31,11 @@ var ( random = rand.New(rand.NewSource(time.Now().Unix())) ) +// PID returns pid in string. +func PID() string { + return pid +} + // numberHex returns num in hex string. // The hex string will be cut with start and end. func numberHex(num uint64, start int, end int) string { @@ -48,16 +53,11 @@ func numberHex(num uint64, start int, end int) string { return fmt.Sprintf("%x", b[start:end]) } -// Now returns in current time in hex string. -func Now() string { +// NowHex returns in current time in hex string. +func NowHex() string { return numberHex(uint64(time.Now().Unix()), 4, 0) } -// PID returns pid in string. -func PID() string { - return pid -} - // RandomString returns a string including 0-9/a-z/A-Z not longer than length. func RandomString(length int) string { b := make([]byte, length) diff --git a/pkg/encode/encode_test.go b/pkg/encode/encode_test.go index 9d23ef0..554b9bd 100644 --- a/pkg/encode/encode_test.go +++ b/pkg/encode/encode_test.go @@ -12,7 +12,7 @@ import "testing" // go test -v -cover -run=^NowTimeHex$ func TestNowTimeHex(t *testing.T) { - timeHex := Now() + timeHex := NowHex() if len(timeHex) != 8 { t.Errorf("length of TimeHex is wrong with %s, %d", timeHex, len(timeHex)) } diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go index 3986fac..6cb9a55 100644 --- a/pkg/errors/errors.go +++ b/pkg/errors/errors.go @@ -8,107 +8,28 @@ package errors -import ( - "errors" - "fmt" - "net/http" -) +import "github.com/FishGoddess/errors" const ( - codeBadRequest = http.StatusBadRequest - codeSendTimeout = 1100 codeSendEmailFailed = 11000 ) -// Error is error with code. -type Error struct { - err error - code int32 -} - -// Error returns error message. -func (e *Error) Error() string { - if e == nil || e.err == nil { - return "" - } - return fmt.Sprintf("%d (%s)", e.code, e.err.Error()) -} - -// Is returns if e is the target's type. -func (e *Error) Is(target error) bool { - if e == nil { - return e == target - } - - err, ok := target.(*Error) - if !ok { - return e.err == target - } - - return e.code == err.code -} - -// Unwrap unwraps e. -func (e *Error) Unwrap() error { - if e == nil { - return nil - } - return e.err -} - -// WithCode wraps err with code. -func WithCode(err error, code int32) error { - if err == nil { - return nil - } - return &Error{err: err, code: code} -} - -// Is returns if err has a code and its code equals to code. -func Is(err error, code int32) bool { - for { - if err == nil { - return false - } - - e, ok := err.(*Error) - if !ok { - return false - } - - if e.code == code { - return true - } - err = errors.Unwrap(err) - } -} - -// BadRequest returns a bad request error. -func BadRequest(err error) error { - return WithCode(err, codeBadRequest) -} - -// IsBadRequest returns if err is bad request. -func IsBadRequest(err error) bool { - return Is(err, codeBadRequest) -} - -// SendTimeoutErr returns a send timeout error. -func SendTimeoutErr(err error) error { - return WithCode(err, codeSendTimeout) +// TimeoutErr returns a timeout error. +func TimeoutErr(err error) error { + return errors.Timeout(err) } -// IsSendTimeout returns if err is send timeout. -func IsSendTimeout(err error) bool { - return Is(err, codeSendTimeout) +// IsTimeout returns if err is timeout. +func IsTimeout(err error) bool { + return errors.IsTimeout(err) } // SendEmailFailedErr returns a send email failed error. func SendEmailFailedErr(err error) error { - return WithCode(err, codeSendEmailFailed) + return errors.Wrap(err, codeSendEmailFailed) } // IsSendEmailFailed returns if err is send email failed. func IsSendEmailFailed(err error) bool { - return Is(err, codeSendEmailFailed) + return errors.Is(err, codeSendEmailFailed) } diff --git a/pkg/errors/errors_test.go b/pkg/errors/errors_test.go index e435dc0..a46ec1f 100644 --- a/pkg/errors/errors_test.go +++ b/pkg/errors/errors_test.go @@ -13,32 +13,13 @@ import ( "testing" ) -// go test -v -cover -run=^TestIsSendTimeout$ -func TestIsSendTimeout(t *testing.T) { - testCases := []struct { - err error - result bool - }{ - {errSendTimeout, true}, - {errSendEmailFailed, false}, - {errors.New("unknown error"), false}, - } - - for i, testCase := range testCases { - if IsSendTimeout(testCase.err) != testCase.result { - t.Errorf("testCase %d failed with err %+v, result %+v", i, testCase.err, testCase.result) - } - } -} - // go test -v -cover -run=^TestIsSendEmailFailed$ func TestIsSendEmailFailed(t *testing.T) { testCases := []struct { err error result bool }{ - {errSendTimeout, false}, - {errSendEmailFailed, true}, + {SendEmailFailedErr(errors.New("send email failed")), true}, {errors.New("unknown error"), false}, } diff --git a/pkg/trace/trace.go b/pkg/trace/trace.go index 525c84c..69d1aa7 100644 --- a/pkg/trace/trace.go +++ b/pkg/trace/trace.go @@ -11,7 +11,7 @@ package trace import ( "context" - "github.com/avino-plan/postar/pkg/encode" + "github.com/avinoplan/postar/pkg/encode" ) var ( @@ -21,7 +21,7 @@ var ( // NewTraceID returns a new trace id. func NewTraceID() string { salt := encode.RandomString(6) - return encode.Now() + salt[:3] + encode.PID() + salt[3:] + return encode.NowHex() + salt[:3] + encode.PID() + salt[3:] } // NewContext wraps ctx with a trace id. From 29b524df32db343822b253bcbe2aa14c50a7743a Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Sun, 23 Jan 2022 02:21:56 +0800 Subject: [PATCH 11/27] =?UTF-8?q?=E5=A4=A7=E9=87=8F=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/{postard.pb.go => postar.pb.go} | 171 ++++++++++++++-------------- api/{postard.proto => postar.proto} | 6 +- api/postar_grpc.pb.go | 103 +++++++++++++++++ api/postard_grpc.pb.go | 103 ----------------- cmd/{postard => postar}/main.go | 31 +++-- configs/config.go | 41 +++++++ internal/biz/smtp.go | 37 +++--- internal/biz/smtp_test.go | 44 ++++--- internal/model/smtp.go | 8 +- internal/server/grpc.go | 27 +++-- internal/server/server.go | 26 +++++ 11 files changed, 346 insertions(+), 251 deletions(-) rename api/{postard.pb.go => postar.pb.go} (59%) rename api/{postard.proto => postar.proto} (91%) create mode 100644 api/postar_grpc.pb.go delete mode 100644 api/postard_grpc.pb.go rename cmd/{postard => postar}/main.go (55%) create mode 100644 configs/config.go create mode 100644 internal/server/server.go diff --git a/api/postard.pb.go b/api/postar.pb.go similarity index 59% rename from api/postard.pb.go rename to api/postar.pb.go index 86d17ff..ccd78fd 100644 --- a/api/postard.pb.go +++ b/api/postar.pb.go @@ -10,7 +10,7 @@ // versions: // protoc-gen-go v1.27.1 // protoc v3.18.0 -// source: postard.proto +// source: postar.proto package api @@ -43,7 +43,7 @@ type Email struct { func (x *Email) Reset() { *x = Email{} if protoimpl.UnsafeEnabled { - mi := &file_postard_proto_msgTypes[0] + mi := &file_postar_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -56,7 +56,7 @@ func (x *Email) String() string { func (*Email) ProtoMessage() {} func (x *Email) ProtoReflect() protoreflect.Message { - mi := &file_postard_proto_msgTypes[0] + mi := &file_postar_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -69,7 +69,7 @@ func (x *Email) ProtoReflect() protoreflect.Message { // Deprecated: Use Email.ProtoReflect.Descriptor instead. func (*Email) Descriptor() ([]byte, []int) { - return file_postard_proto_rawDescGZIP(), []int{0} + return file_postar_proto_rawDescGZIP(), []int{0} } func (x *Email) GetReceivers() []string { @@ -113,7 +113,7 @@ type SendEmailOptions struct { func (x *SendEmailOptions) Reset() { *x = SendEmailOptions{} if protoimpl.UnsafeEnabled { - mi := &file_postard_proto_msgTypes[1] + mi := &file_postar_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -126,7 +126,7 @@ func (x *SendEmailOptions) String() string { func (*SendEmailOptions) ProtoMessage() {} func (x *SendEmailOptions) ProtoReflect() protoreflect.Message { - mi := &file_postard_proto_msgTypes[1] + mi := &file_postar_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -139,7 +139,7 @@ func (x *SendEmailOptions) ProtoReflect() protoreflect.Message { // Deprecated: Use SendEmailOptions.ProtoReflect.Descriptor instead. func (*SendEmailOptions) Descriptor() ([]byte, []int) { - return file_postard_proto_rawDescGZIP(), []int{1} + return file_postar_proto_rawDescGZIP(), []int{1} } func (x *SendEmailOptions) GetAsync() bool { @@ -169,7 +169,7 @@ type SendEmailRequest struct { func (x *SendEmailRequest) Reset() { *x = SendEmailRequest{} if protoimpl.UnsafeEnabled { - mi := &file_postard_proto_msgTypes[2] + mi := &file_postar_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -182,7 +182,7 @@ func (x *SendEmailRequest) String() string { func (*SendEmailRequest) ProtoMessage() {} func (x *SendEmailRequest) ProtoReflect() protoreflect.Message { - mi := &file_postard_proto_msgTypes[2] + mi := &file_postar_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -195,7 +195,7 @@ func (x *SendEmailRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SendEmailRequest.ProtoReflect.Descriptor instead. func (*SendEmailRequest) Descriptor() ([]byte, []int) { - return file_postard_proto_rawDescGZIP(), []int{2} + return file_postar_proto_rawDescGZIP(), []int{2} } func (x *SendEmailRequest) GetEmail() *Email { @@ -226,7 +226,7 @@ type SendEmailResponse struct { func (x *SendEmailResponse) Reset() { *x = SendEmailResponse{} if protoimpl.UnsafeEnabled { - mi := &file_postard_proto_msgTypes[3] + mi := &file_postar_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -239,7 +239,7 @@ func (x *SendEmailResponse) String() string { func (*SendEmailResponse) ProtoMessage() {} func (x *SendEmailResponse) ProtoReflect() protoreflect.Message { - mi := &file_postard_proto_msgTypes[3] + mi := &file_postar_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -252,7 +252,7 @@ func (x *SendEmailResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SendEmailResponse.ProtoReflect.Descriptor instead. func (*SendEmailResponse) Descriptor() ([]byte, []int) { - return file_postard_proto_rawDescGZIP(), []int{3} + return file_postar_proto_rawDescGZIP(), []int{3} } func (x *SendEmailResponse) GetCode() ServerCode { @@ -276,81 +276,82 @@ func (x *SendEmailResponse) GetTraceId() string { return "" } -var File_postard_proto protoreflect.FileDescriptor - -var file_postard_proto_rawDesc = []byte{ - 0x0a, 0x0d, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, - 0x1f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, - 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x1a, 0x09, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x70, 0x0a, 0x05, 0x45, - 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, - 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1b, 0x0a, 0x09, - 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x62, 0x6f, 0x64, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, - 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x42, 0x0a, - 0x10, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, - 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, - 0x74, 0x22, 0x9d, 0x01, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, - 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x05, 0x65, - 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x4b, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, - 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, - 0x6c, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x22, 0x81, 0x01, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, - 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, - 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, - 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x72, - 0x61, 0x63, 0x65, 0x49, 0x64, 0x32, 0x7d, 0x0a, 0x07, 0x50, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x64, - 0x12, 0x72, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x31, 0x2e, +var File_postar_proto protoreflect.FileDescriptor + +var file_postar_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, - 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x32, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, - 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x21, 0x5a, 0x1f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2f, 0x70, 0x6f, 0x73, - 0x74, 0x61, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x1a, + 0x09, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x70, 0x0a, 0x05, 0x45, 0x6d, + 0x61, 0x69, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, + 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x62, + 0x6f, 0x64, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x62, 0x6f, 0x64, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x42, 0x0a, 0x10, + 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x22, 0x9d, 0x01, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, + 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x05, 0x65, 0x6d, + 0x61, 0x69, 0x6c, 0x12, 0x4b, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, + 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, + 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x22, 0x81, 0x01, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, + 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x64, + 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, 0x61, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x72, 0x61, + 0x63, 0x65, 0x49, 0x64, 0x32, 0x83, 0x01, 0x0a, 0x0d, 0x50, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x72, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, + 0x61, 0x69, 0x6c, 0x12, 0x31, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x6f, + 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x6d, 0x61, + 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x21, 0x5a, 0x1f, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, + 0x61, 0x6e, 0x2f, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( - file_postard_proto_rawDescOnce sync.Once - file_postard_proto_rawDescData = file_postard_proto_rawDesc + file_postar_proto_rawDescOnce sync.Once + file_postar_proto_rawDescData = file_postar_proto_rawDesc ) -func file_postard_proto_rawDescGZIP() []byte { - file_postard_proto_rawDescOnce.Do(func() { - file_postard_proto_rawDescData = protoimpl.X.CompressGZIP(file_postard_proto_rawDescData) +func file_postar_proto_rawDescGZIP() []byte { + file_postar_proto_rawDescOnce.Do(func() { + file_postar_proto_rawDescData = protoimpl.X.CompressGZIP(file_postar_proto_rawDescData) }) - return file_postard_proto_rawDescData + return file_postar_proto_rawDescData } -var file_postard_proto_msgTypes = make([]protoimpl.MessageInfo, 4) -var file_postard_proto_goTypes = []interface{}{ +var file_postar_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_postar_proto_goTypes = []interface{}{ (*Email)(nil), // 0: github.com.avinoplan.postar.api.Email (*SendEmailOptions)(nil), // 1: github.com.avinoplan.postar.api.SendEmailOptions (*SendEmailRequest)(nil), // 2: github.com.avinoplan.postar.api.SendEmailRequest (*SendEmailResponse)(nil), // 3: github.com.avinoplan.postar.api.SendEmailResponse (ServerCode)(0), // 4: github.com.avinoplan.postar.api.ServerCode } -var file_postard_proto_depIdxs = []int32{ +var file_postar_proto_depIdxs = []int32{ 0, // 0: github.com.avinoplan.postar.api.SendEmailRequest.email:type_name -> github.com.avinoplan.postar.api.Email 1, // 1: github.com.avinoplan.postar.api.SendEmailRequest.options:type_name -> github.com.avinoplan.postar.api.SendEmailOptions 4, // 2: github.com.avinoplan.postar.api.SendEmailResponse.code:type_name -> github.com.avinoplan.postar.api.ServerCode - 2, // 3: github.com.avinoplan.postar.api.Postard.SendEmail:input_type -> github.com.avinoplan.postar.api.SendEmailRequest - 3, // 4: github.com.avinoplan.postar.api.Postard.SendEmail:output_type -> github.com.avinoplan.postar.api.SendEmailResponse + 2, // 3: github.com.avinoplan.postar.api.PostarService.SendEmail:input_type -> github.com.avinoplan.postar.api.SendEmailRequest + 3, // 4: github.com.avinoplan.postar.api.PostarService.SendEmail:output_type -> github.com.avinoplan.postar.api.SendEmailResponse 4, // [4:5] is the sub-list for method output_type 3, // [3:4] is the sub-list for method input_type 3, // [3:3] is the sub-list for extension type_name @@ -358,14 +359,14 @@ var file_postard_proto_depIdxs = []int32{ 0, // [0:3] is the sub-list for field type_name } -func init() { file_postard_proto_init() } -func file_postard_proto_init() { - if File_postard_proto != nil { +func init() { file_postar_proto_init() } +func file_postar_proto_init() { + if File_postar_proto != nil { return } file_api_proto_init() if !protoimpl.UnsafeEnabled { - file_postard_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_postar_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Email); i { case 0: return &v.state @@ -377,7 +378,7 @@ func file_postard_proto_init() { return nil } } - file_postard_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_postar_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SendEmailOptions); i { case 0: return &v.state @@ -389,7 +390,7 @@ func file_postard_proto_init() { return nil } } - file_postard_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_postar_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SendEmailRequest); i { case 0: return &v.state @@ -401,7 +402,7 @@ func file_postard_proto_init() { return nil } } - file_postard_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_postar_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SendEmailResponse); i { case 0: return &v.state @@ -418,18 +419,18 @@ func file_postard_proto_init() { out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_postard_proto_rawDesc, + RawDescriptor: file_postar_proto_rawDesc, NumEnums: 0, NumMessages: 4, NumExtensions: 0, NumServices: 1, }, - GoTypes: file_postard_proto_goTypes, - DependencyIndexes: file_postard_proto_depIdxs, - MessageInfos: file_postard_proto_msgTypes, + GoTypes: file_postar_proto_goTypes, + DependencyIndexes: file_postar_proto_depIdxs, + MessageInfos: file_postar_proto_msgTypes, }.Build() - File_postard_proto = out.File - file_postard_proto_rawDesc = nil - file_postard_proto_goTypes = nil - file_postard_proto_depIdxs = nil + File_postar_proto = out.File + file_postar_proto_rawDesc = nil + file_postar_proto_goTypes = nil + file_postar_proto_depIdxs = nil } diff --git a/api/postard.proto b/api/postar.proto similarity index 91% rename from api/postard.proto rename to api/postar.proto index 3a153f3..7be89f8 100644 --- a/api/postard.proto +++ b/api/postar.proto @@ -12,7 +12,7 @@ package github.com.avinoplan.postar.api; import "api.proto"; -// protoc -I=. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative postard.proto +// protoc -I=. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative postar.proto option go_package = "github.com/avinoplan/postar/api"; // Email wraps all information of using smtp service. @@ -42,8 +42,8 @@ message SendEmailResponse { string trace_id = 3; // For tracing. } -// Postard is the core service of postar. -service Postard { +// PostarService is the service of postar. +service PostarService { // SendEmail send one email. rpc SendEmail(SendEmailRequest) returns (SendEmailResponse); } \ No newline at end of file diff --git a/api/postar_grpc.pb.go b/api/postar_grpc.pb.go new file mode 100644 index 0000000..5c3ede4 --- /dev/null +++ b/api/postar_grpc.pb.go @@ -0,0 +1,103 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package api + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// PostarServiceClient is the client API for PostarService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type PostarServiceClient interface { + // SendEmail send one email. + SendEmail(ctx context.Context, in *SendEmailRequest, opts ...grpc.CallOption) (*SendEmailResponse, error) +} + +type postarServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewPostarServiceClient(cc grpc.ClientConnInterface) PostarServiceClient { + return &postarServiceClient{cc} +} + +func (c *postarServiceClient) SendEmail(ctx context.Context, in *SendEmailRequest, opts ...grpc.CallOption) (*SendEmailResponse, error) { + out := new(SendEmailResponse) + err := c.cc.Invoke(ctx, "/github.com.avinoplan.postar.api.PostarService/SendEmail", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// PostarServiceServer is the server API for PostarService service. +// All implementations must embed UnimplementedPostarServiceServer +// for forward compatibility +type PostarServiceServer interface { + // SendEmail send one email. + SendEmail(context.Context, *SendEmailRequest) (*SendEmailResponse, error) + mustEmbedUnimplementedPostarServiceServer() +} + +// UnimplementedPostarServiceServer must be embedded to have forward compatible implementations. +type UnimplementedPostarServiceServer struct { +} + +func (UnimplementedPostarServiceServer) SendEmail(context.Context, *SendEmailRequest) (*SendEmailResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SendEmail not implemented") +} +func (UnimplementedPostarServiceServer) mustEmbedUnimplementedPostarServiceServer() {} + +// UnsafePostarServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to PostarServiceServer will +// result in compilation errors. +type UnsafePostarServiceServer interface { + mustEmbedUnimplementedPostarServiceServer() +} + +func RegisterPostarServiceServer(s grpc.ServiceRegistrar, srv PostarServiceServer) { + s.RegisterService(&PostarService_ServiceDesc, srv) +} + +func _PostarService_SendEmail_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SendEmailRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PostarServiceServer).SendEmail(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/github.com.avinoplan.postar.api.PostarService/SendEmail", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PostarServiceServer).SendEmail(ctx, req.(*SendEmailRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// PostarService_ServiceDesc is the grpc.ServiceDesc for PostarService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var PostarService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "github.com.avinoplan.postar.api.PostarService", + HandlerType: (*PostarServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SendEmail", + Handler: _PostarService_SendEmail_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "postar.proto", +} diff --git a/api/postard_grpc.pb.go b/api/postard_grpc.pb.go deleted file mode 100644 index 7a9da98..0000000 --- a/api/postard_grpc.pb.go +++ /dev/null @@ -1,103 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. - -package api - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 - -// PostardClient is the client API for Postard service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type PostardClient interface { - // SendEmail send one email. - SendEmail(ctx context.Context, in *SendEmailRequest, opts ...grpc.CallOption) (*SendEmailResponse, error) -} - -type postardClient struct { - cc grpc.ClientConnInterface -} - -func NewPostardClient(cc grpc.ClientConnInterface) PostardClient { - return &postardClient{cc} -} - -func (c *postardClient) SendEmail(ctx context.Context, in *SendEmailRequest, opts ...grpc.CallOption) (*SendEmailResponse, error) { - out := new(SendEmailResponse) - err := c.cc.Invoke(ctx, "/github.com.avinoplan.postar.api.Postard/SendEmail", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// PostardServer is the server API for Postard service. -// All implementations must embed UnimplementedPostardServer -// for forward compatibility -type PostardServer interface { - // SendEmail send one email. - SendEmail(context.Context, *SendEmailRequest) (*SendEmailResponse, error) - mustEmbedUnimplementedPostardServer() -} - -// UnimplementedPostardServer must be embedded to have forward compatible implementations. -type UnimplementedPostardServer struct { -} - -func (UnimplementedPostardServer) SendEmail(context.Context, *SendEmailRequest) (*SendEmailResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SendEmail not implemented") -} -func (UnimplementedPostardServer) mustEmbedUnimplementedPostardServer() {} - -// UnsafePostardServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to PostardServer will -// result in compilation errors. -type UnsafePostardServer interface { - mustEmbedUnimplementedPostardServer() -} - -func RegisterPostardServer(s grpc.ServiceRegistrar, srv PostardServer) { - s.RegisterService(&Postard_ServiceDesc, srv) -} - -func _Postard_SendEmail_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SendEmailRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(PostardServer).SendEmail(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/github.com.avinoplan.postar.api.Postard/SendEmail", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PostardServer).SendEmail(ctx, req.(*SendEmailRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// Postard_ServiceDesc is the grpc.ServiceDesc for Postard service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var Postard_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "github.com.avinoplan.postar.api.Postard", - HandlerType: (*PostardServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "SendEmail", - Handler: _Postard_SendEmail_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "postard.proto", -} diff --git a/cmd/postard/main.go b/cmd/postar/main.go similarity index 55% rename from cmd/postard/main.go rename to cmd/postar/main.go index 9ff1bf7..0f7c8e0 100644 --- a/cmd/postard/main.go +++ b/cmd/postar/main.go @@ -9,31 +9,42 @@ package main import ( - "net" - "github.com/FishGoddess/logit" + "github.com/avinoplan/postar/configs" "github.com/avinoplan/postar/internal/biz" "github.com/avinoplan/postar/internal/server" "github.com/panjf2000/ants/v2" ) -func main() { - logger := logit.NewLogger() +func loadConfig() (*configs.Config, error) { + // TODO 加载配置文件初始化配置 + return configs.NewDefaultConfig(), nil +} + +func initLogger(c *configs.Config) *logit.Logger { + return logit.NewLogger() +} + +func initPool(c *configs.Config) *ants.Pool { pool, err := ants.NewPool(64) if err != nil { panic(err) } - defer pool.Release() - - smtpBiz := biz.NewSmtpBiz(pool, "", 0, "", "") - svr := server.NewGrpcServer(logger, smtpBiz) + return pool +} - listener, err := net.Listen("tcp", ":5897") +func main() { + c, err := loadConfig() if err != nil { panic(err) } - err = svr.Run(listener) + logger := initLogger(c) + pool := initPool(c) + defer pool.Release() + + smtpBiz := biz.NewSMTPBiz(c, logger, pool) + err = server.NewGRPCServer(c, logger, smtpBiz).Start() if err != nil { panic(err) } diff --git a/configs/config.go b/configs/config.go new file mode 100644 index 0000000..67e27db --- /dev/null +++ b/configs/config.go @@ -0,0 +1,41 @@ +// Copyright 2022 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2022/01/23 01:28:36 + +package configs + +type ServerConfig struct { + Type string `int:"type"` // The type of server. + Address string `ini:"address"` // The address(including ip and port) of server. +} +type SMTPConfig struct { + Host string `int:"host"` // The host of smtp server. + Port int `int:"port"` // The port of smtp server. + User string `int:"user"` // The user of smtp server. + Password string `int:"password"` // The password of smtp server. +} + +// Config stores all configurations of postar. +type Config struct { + Server ServerConfig `int:"server"` + SMTP SMTPConfig `int:"smtp"` +} + +// NewDefaultConfig returns a new config. +func NewDefaultConfig() *Config { + return &Config{ + Server: ServerConfig{ + Type: "http", + Address: ":5897", + }, + SMTP: SMTPConfig{ + Port: 587, + }, + } +} + +// TODO 封装配置查询方法 diff --git a/internal/biz/smtp.go b/internal/biz/smtp.go index c977261..0db0638 100644 --- a/internal/biz/smtp.go +++ b/internal/biz/smtp.go @@ -12,44 +12,41 @@ import ( "context" "github.com/FishGoddess/logit" + "github.com/avinoplan/postar/configs" "github.com/avinoplan/postar/internal/model" "github.com/avinoplan/postar/pkg/errors" "github.com/panjf2000/ants/v2" "gopkg.in/gomail.v2" ) -// SmtpBiz is the biz of smtp. -type SmtpBiz struct { - pool *ants.Pool // The pool of workers. - host string // The host of smtp server. - port int // The port of smtp server. - user string // The user of smtp server. - password string // The password of smtp server. +// SMTPBiz is the biz of smtp. +type SMTPBiz struct { + c *configs.Config + logger *logit.Logger + pool *ants.Pool // The pool of workers. } -// NewSmtpBiz returns a new SmtpBiz. -func NewSmtpBiz(pool *ants.Pool, host string, port int, user string, password string) *SmtpBiz { - return &SmtpBiz{ - pool: pool, - host: host, - port: port, - user: user, - password: password, +// NewSMTPBiz returns a new SMTPBiz. +func NewSMTPBiz(c *configs.Config, logger *logit.Logger, pool *ants.Pool) *SMTPBiz { + return &SMTPBiz{ + c: c, + logger: logger, + pool: pool, } } // sendEmail sends email and returns an error if something wrong happens. -func (sb *SmtpBiz) sendEmail(email *model.Email) error { +func (sb *SMTPBiz) sendEmail(email *model.Email) error { msg := gomail.NewMessage() - msg.SetHeader("From", sb.user) - msg.SetHeader("To", email.To...) msg.SetHeader("Subject", email.Subject) + msg.SetHeader("From", sb.c.SMTP.User) + msg.SetHeader("To", email.Receivers...) msg.SetBody(email.BodyType, email.Body) - return gomail.NewDialer(sb.host, sb.port, sb.user, sb.password).DialAndSend(msg) + return gomail.NewDialer(sb.c.SMTP.Host, sb.c.SMTP.Port, sb.c.SMTP.User, sb.c.SMTP.Password).DialAndSend(msg) } // SendEmail sends email to somewhere. -func (sb *SmtpBiz) SendEmail(ctx context.Context, email *model.Email, options *model.SendEmailOptions) error { +func (sb *SMTPBiz) SendEmail(ctx context.Context, email *model.Email, options *model.SendEmailOptions) error { logger := logit.FromContext(ctx) if options == nil { diff --git a/internal/biz/smtp_test.go b/internal/biz/smtp_test.go index 2b57558..bd0c83d 100644 --- a/internal/biz/smtp_test.go +++ b/internal/biz/smtp_test.go @@ -15,35 +15,45 @@ import ( "testing" "time" + "github.com/FishGoddess/logit" + "github.com/avinoplan/postar/configs" "github.com/avinoplan/postar/internal/model" "github.com/panjf2000/ants/v2" ) -// go test -v -cover -run=^TestSmtpBiz$ -func TestSmtpBiz(t *testing.T) { - host := os.Getenv("POSTAR_SMTP_HOST") - user := os.Getenv("POSTAR_SMTP_USER") - password := os.Getenv("POSTAR_SMTP_PASSWORD") - to := os.Getenv("POSTAR_SMTP_TO") - if host == "" || user == "" || password == "" || to == "" { - t.Skipf("smtp host %s or user %s or password %s or to %s is empty", host, user, password, to) - } - +func newConfig() *configs.Config { port, err := strconv.ParseInt(os.Getenv("POSTAR_SMTP_PORT"), 10, 64) if err != nil { port = 587 } + c := configs.NewDefaultConfig() + c.SMTP.Host = os.Getenv("POSTAR_SMTP_HOST") + c.SMTP.Port = int(port) + c.SMTP.User = os.Getenv("POSTAR_SMTP_USER") + c.SMTP.Password = os.Getenv("POSTAR_SMTP_PASSWORD") + return c +} + +// go test -v -cover -run=^TestSMTPBiz$ +func TestSMTPBiz(t *testing.T) { + c := newConfig() + receiver := os.Getenv("POSTAR_SMTP_RECEIVER") + if c.SMTP.Host == "" || c.SMTP.User == "" || c.SMTP.Password == "" || receiver == "" { + t.Skipf("smtp host %s or user %s or password %s or receiver %s is empty", c.SMTP.Host, c.SMTP.User, c.SMTP.Password, receiver) + } + pool, _ := ants.NewPool(64) defer pool.Release() - - smtpService := NewSmtpBiz(pool, host, int(port), user, password) - err = smtpService.SendEmail(context.Background(), &model.Email{ - To: []string{to}, - Subject: t.Name(), - BodyType: "text/html;charset=utf-8", - Body: t.Name() + time.Now().Format("20060102150405.000"), + + smtpService := NewSMTPBiz(c, logit.NewLogger(), pool) + err := smtpService.SendEmail(context.Background(), &model.Email{ + Subject: t.Name(), + Receivers: []string{receiver}, + BodyType: "text/html;charset=utf-8", + Body: t.Name() + time.Now().Format("20060102150405.000"), }, model.DefaultSendEmailOptions()) + if err != nil { t.Error(err) } diff --git a/internal/model/smtp.go b/internal/model/smtp.go index 46d40f6..8333571 100644 --- a/internal/model/smtp.go +++ b/internal/model/smtp.go @@ -12,10 +12,10 @@ import "time" // Email is an email. type Email struct { - To []string // Receivers. - Subject string // Subject. - BodyType string // Body type. - Body string // Body. + Subject string // Subject. + Receivers []string // Receivers. + BodyType string // Body type. + Body string // Body. } // SendEmailOptions is the options of sending one email. diff --git a/internal/server/grpc.go b/internal/server/grpc.go index c67f742..72eb9f8 100644 --- a/internal/server/grpc.go +++ b/internal/server/grpc.go @@ -14,6 +14,7 @@ import ( "github.com/FishGoddess/logit" "github.com/avinoplan/postar/api" + "github.com/avinoplan/postar/configs" "github.com/avinoplan/postar/internal/biz" "github.com/avinoplan/postar/pkg/errors" "github.com/avinoplan/postar/pkg/trace" @@ -22,15 +23,17 @@ import ( // GRPCServer is a grpc implement of PostardServer. type GRPCServer struct { - api.UnimplementedPostardServer + api.UnimplementedPostarServiceServer server *grpc.Server + c *configs.Config logger *logit.Logger - smtpBiz *biz.SmtpBiz + smtpBiz *biz.SMTPBiz } -// NewGrpcServer returns a new GRPCServer. -func NewGrpcServer(logger *logit.Logger, smtpBiz *biz.SmtpBiz) *GRPCServer { +// NewGRPCServer returns a new GRPCServer. +func NewGRPCServer(c *configs.Config, logger *logit.Logger, smtpBiz *biz.SMTPBiz) Server { return &GRPCServer{ + c: c, logger: logger, smtpBiz: smtpBiz, } @@ -65,14 +68,20 @@ func (gs *GRPCServer) SendEmail(ctx context.Context, request *api.SendEmailReque }, nil } -// Run runs GRPCServer with listener. -func (gs *GRPCServer) Run(listener net.Listener) error { +// Start starts GRPCServer. +func (gs *GRPCServer) Start() error { + listener, err := net.Listen("tcp", gs.c.Server.Address) + if err != nil { + return err + } + gs.server = grpc.NewServer() - api.RegisterPostardServer(gs.server, gs) + api.RegisterPostarServiceServer(gs.server, gs) return gs.server.Serve(listener) } -// Shutdown shutdowns GRPCServer gracefully. -func (gs *GRPCServer) Shutdown() { +// Stop stops GRPCServer gracefully. +func (gs *GRPCServer) Stop() error { gs.server.GracefulStop() + return nil } diff --git a/internal/server/server.go b/internal/server/server.go new file mode 100644 index 0000000..6df80b6 --- /dev/null +++ b/internal/server/server.go @@ -0,0 +1,26 @@ +// Copyright 2022 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2022/01/23 02:18:24 + +package server + +import ( + "github.com/FishGoddess/logit" + "github.com/avinoplan/postar/configs" + "github.com/avinoplan/postar/internal/biz" +) + +var ( + servers = map[string]func(c *configs.Config, logger *logit.Logger, smtpBiz *biz.SMTPBiz) Server{ + "grpc": NewGRPCServer, + } +) + +type Server interface { + Start() error + Stop() error +} From f580ce9f0b8e2c2fc3e80cdb526c5192766adfce Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Mon, 24 Jan 2022 02:10:53 +0800 Subject: [PATCH 12/27] =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 22 ++++++-------- LICENSE | 20 ------------- build.sh | 62 +++++++++++++++++++++++++++++++++++++++ cmd/postar/main.go | 50 +++++++++++++++++++++++++++---- go.mod | 1 + go.sum | 4 +++ internal/server/server.go | 10 +++++++ 7 files changed, 130 insertions(+), 39 deletions(-) delete mode 100644 LICENSE create mode 100644 build.sh diff --git a/.gitignore b/.gitignore index 475809f..a201e7f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,22 +1,18 @@ -# Binaries for programs and plugins +# OS *.exe *.exe~ *.dll *.so *.dylib -*.tar.gz -target/ -# Test binary, built with `go test -c` -*.test +# IDE +.idea/ +*.iml +.vscode/ -# Output of the go coverage tool, specifically when used with LiteIDE +# Program +*.test *.out - -# logs +target/ +*.tar.gz *.log - -# ide -.idea/ -*.iml -.vscode/* diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 41c001c..0000000 --- a/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2021 Ye Zi Jie - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..017ad5d --- /dev/null +++ b/build.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +VERSION=v0.3.0-alpha +echo "VERSION: $VERSION" +echo "----------------------------------------------------------------------" + +# Check +WORKDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +echo "WORKDIR: $WORKDIR" +TARGET=$WORKDIR/target +echo "TARGET: $TARGET" +CONFIG_FILE=$WORKDIR/_examples/config/postar.ini +echo "CONFIG_FILE: $CONFIG_FILE" +LICENSE_FILE=$WORKDIR/LICENSE +echo "LICENSE_FILE: $LICENSE_FILE" +echo "----------------------------------------------------------------------" + +echo "Please check the paths above! It's right? (y/n)" +read -r right +if [ -z "$right" ]; then + echo "Input y or n to continue..." + exit +fi + +if [ "$right" == "n" ]; then + echo "Fix the wrong paths to continue..." + exit +fi +echo "----------------------------------------------------------------------" + +# Start building +echo "Start building..." +rm -r "${TARGET:?}" && mkdir -p "$TARGET" +cd "$WORKDIR"/cmd/postar || exit + +# Go build: windows, linux and darwin +echo "Building windows version..." +CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o "$TARGET"/postar-windows.exe + +echo "Building linux version..." +CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o "$TARGET"/postar-linux +chmod +x "$TARGET"/postar-linux + +echo "Building darwin version..." +CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o "$TARGET"/postar-darwin +chmod +x "$TARGET"/postar-darwin + +# Before packaging +echo "Start packaging..." +cd "$TARGET" || exit +cp "$CONFIG_FILE" "$TARGET"/ +cp "$LICENSE_FILE" "$TARGET"/ + +# Start Packaging +echo "Packaging windows version..." +tar -czf postar-$VERSION-windows.tar.gz "$TARGET"/postar-windows.exe "$CONFIG_FILE" "$LICENSE_FILE" +tar -czf postar-$VERSION-linux.tar.gz "$TARGET"/postar-linux "$CONFIG_FILE" "$LICENSE_FILE" +tar -czf postar-$VERSION-darwin.tar.gz "$TARGET"/postar-darwin "$CONFIG_FILE" "$LICENSE_FILE" + +# Done +echo "Done." +rm "$TARGET"/postar-windows.exe "$TARGET"/postar-linux "$TARGET"/postar-darwin "$CONFIG_FILE" "$LICENSE_FILE" diff --git a/cmd/postar/main.go b/cmd/postar/main.go index 0f7c8e0..4cad744 100644 --- a/cmd/postar/main.go +++ b/cmd/postar/main.go @@ -9,16 +9,38 @@ package main import ( + "flag" + "fmt" + "os" + "os/signal" + "runtime" + "syscall" + "github.com/FishGoddess/logit" "github.com/avinoplan/postar/configs" "github.com/avinoplan/postar/internal/biz" "github.com/avinoplan/postar/internal/server" + "github.com/go-ini/ini" "github.com/panjf2000/ants/v2" ) +const ( + version = "postar-v0.3.0-alpha" +) + func loadConfig() (*configs.Config, error) { - // TODO 加载配置文件初始化配置 - return configs.NewDefaultConfig(), nil + configFile := flag.String("config.file", "postar.ini", "The configuration file of postar.") + showVersion := flag.Bool("version", false, "Check version of postar.") + flag.Parse() + + if *showVersion { + fmt.Printf("%s (%s, %s, %s)\n", version, runtime.GOOS, runtime.GOARCH, runtime.Version()) + os.Exit(0) + } + + c := configs.NewDefaultConfig() + err := ini.MapTo(c, *configFile) + return c, err } func initLogger(c *configs.Config) *logit.Logger { @@ -33,6 +55,23 @@ func initPool(c *configs.Config) *ants.Pool { return pool } +func runServer(c *configs.Config, logger *logit.Logger, smtpBiz *biz.SMTPBiz) { + svr := server.NewServer(c, logger, smtpBiz) + + go func() { + err := svr.Start() + if err != nil { + panic(err) + } + }() + + signalCh := make(chan os.Signal, 1) + signal.Notify(signalCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM) + <-signalCh + fmt.Println("Postar is shutdown gracefully...") + svr.Stop() +} + func main() { c, err := loadConfig() if err != nil { @@ -40,12 +79,11 @@ func main() { } logger := initLogger(c) + defer logger.Close() + pool := initPool(c) defer pool.Release() smtpBiz := biz.NewSMTPBiz(c, logger, pool) - err = server.NewGRPCServer(c, logger, smtpBiz).Start() - if err != nil { - panic(err) - } + runServer(c, logger, smtpBiz) } diff --git a/go.mod b/go.mod index aa6fdc3..523085d 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.15 require ( github.com/FishGoddess/errors v0.0.2 github.com/FishGoddess/logit v0.4.11 + github.com/go-ini/ini v1.66.3 github.com/panjf2000/ants/v2 v2.4.7 google.golang.org/grpc v1.40.0 google.golang.org/protobuf v1.27.1 diff --git a/go.sum b/go.sum index 8077208..268e58b 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-ini/ini v1.66.3 h1:Ftrhd6NNIEu4LdPoqP7fyXRFu/I3vRnY8GWpHa/Xsz4= +github.com/go-ini/ini v1.66.3/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -126,6 +128,8 @@ gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= +gopkg.in/ini.v1 v1.66.3 h1:jRskFVxYaMGAMUbN0UZ7niA9gzL9B49DOqE78vg0k3w= +gopkg.in/ini.v1 v1.66.3/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/server/server.go b/internal/server/server.go index 6df80b6..4bf4dd2 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -9,6 +9,8 @@ package server import ( + "fmt" + "github.com/FishGoddess/logit" "github.com/avinoplan/postar/configs" "github.com/avinoplan/postar/internal/biz" @@ -24,3 +26,11 @@ type Server interface { Start() error Stop() error } + +func NewServer(c *configs.Config, logger *logit.Logger, smtpBiz *biz.SMTPBiz) Server { + newServer, ok := servers[c.Server.Type] + if !ok { + panic(fmt.Errorf("server: type %s not found", c.Server.Type)) + } + return newServer(c, logger, smtpBiz) +} From 392f0fba92ef427bab8e657c8de0dd8487703cb7 Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Mon, 24 Jan 2022 23:54:09 +0800 Subject: [PATCH 13/27] =?UTF-8?q?=E6=9E=84=E5=BB=BA=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LICENSE | 20 ++++++++++++++++ README.en.md | 15 ++++++------ README.md | 14 +++++------ _examples/config/postar.ini | 0 build.sh | 47 +++++++++++++++++-------------------- pkg/encode/encode.go | 17 +++++++------- pkg/encode/encode_test.go | 34 +++++++++++++++++---------- pkg/trace/trace.go | 4 ++-- 8 files changed, 89 insertions(+), 62 deletions(-) create mode 100644 LICENSE create mode 100644 _examples/config/postar.ini diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..41c001c --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2021 Ye Zi Jie + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.en.md b/README.en.md index f190ee8..7c71d72 100644 --- a/README.en.md +++ b/README.en.md @@ -12,8 +12,8 @@ * Plain and Html form email supports * Synchronous/Asynchronous mode supports, and timeout is available in synchronous mode -* HTTP api supports, and use http2 is ok -* Gracefully shutdown with signal mechanism supports +* Support http/http2/grpc/vex/udp protocol +* Gracefully shutdown with signal mechanism _Check [HISTORY.md](./HISTORY.md) and [FUTURE.md](./FUTURE.md) to know about more information._ @@ -31,9 +31,9 @@ Postar has two ways to get binary: 1. Invoking `./build.sh` in the root of source code will generate target directory, which contains all binary files. -2. Build by `go` command, see `go build`. +2. Building by `go build` (or running by `go run`) in `cmd/postar`, see `go`. -_Notice: Default config file is `/opt/postar/conf/postar.ini`, default log output directory is `/opt/postar/log/`, and you need them to start service._ +_Notice: Default config file is `./postar.ini`, default log output directory is `./log/service.log`._ > Want to know how to use? See [_examples](_examples). @@ -43,6 +43,7 @@ If you find that something is not working as expected please open an _**issue**_ ### 📦 Projects postar used -| Project | Author | Description | link | -|---------|-------------|-------------------------------------------------------|-----------------------------------------------------------------------------------------------| -| logit | FishGoddess | A high-performance and easy-to-use logging foundation | [Gitee](https://gitee.com/FishGoddess/logit) / [GitHub](https://github.com/FishGoddess/logit) | +| Project | Author | Description | link | +|---------|-------------|-------------------------------------------------------|-------------------------------------------------------------------------------------------------| +| logit | FishGoddess | A high-performance and easy-to-use logging foundation | [Gitee](https://gitee.com/FishGoddess/logit) / [GitHub](https://github.com/FishGoddess/logit) | +| errors | FishGoddess | A lib for handling error gracefully in Go | [Gitee](https://gitee.com/FishGoddess/errors) / [GitHub](https://github.com/FishGoddess/errors) | diff --git a/README.md b/README.md index 112cd74..945baa1 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ * 支持发送文本邮件和 HTML 邮件 * 支持同步、异步邮件发送,同步模式可配置超时 -* 支持 http 请求调用接口,可配置 http2 协议 +* 支持 http/http2/grpc/vex/udp 等网络协议 * 支持 signal 通知的平滑下线 _历史版本的特性请查看 [HISTORY.md](./HISTORY.md)。未来版本的新特性和计划请查看 [FUTURE.md](./FUTURE.md)。_ @@ -31,9 +31,9 @@ Postar 的二进制执行包可以通过源码进行编译得到,一共有两 1. 在源码根目录执行 `./build.sh` 会生成 target 目录,所有的二进制包都在里面 -2. 通过 `go` 命令构建或启动服务,参考 `go build`。 +2. 在 `cmd/postar` 目录下使用 `go build` 构建服务(或 `go run` 启动服务),参考 `go` 命令。 -_注意:默认的配置文件路径是 `/opt/postar/conf/postar.ini`,默认的日志输出路径是 `/opt/postar/log/`,需要有对应的文件和文件夹才可以启动。_ +_注意:默认的配置文件路径是 `./postar.ini`,默认的日志输出是 `./log/service.log`。_ > 想知道怎么使用?查看 [_examples](_examples)。 @@ -43,7 +43,7 @@ _注意:默认的配置文件路径是 `/opt/postar/conf/postar.ini`,默认 ### 📦 postar 使用的技术 -| 项目 | 作者 | 描述 | 链接 | -|-------|-------------|---------------------|--------------------------------------------------------------------------------------------| -| logit | FishGoddess | 一个高性能、功能强大且极易上手的日志库 | [码云](https://gitee.com/FishGoddess/logit) / [GitHub](https://github.com/FishGoddess/logit) | - +| 项目 | 作者 | 描述 | 链接 | +|--------|-------------|---------------------|----------------------------------------------------------------------------------------------| +| logit | FishGoddess | 一个高性能、功能强大且极易上手的日志库 | [码云](https://gitee.com/FishGoddess/logit) / [GitHub](https://github.com/FishGoddess/logit) | +| errors | FishGoddess | 一个用于优雅地处理 Go 中错误的库 | [码云](https://gitee.com/FishGoddess/errors) / [GitHub](https://github.com/FishGoddess/errors) | diff --git a/_examples/config/postar.ini b/_examples/config/postar.ini new file mode 100644 index 0000000..e69de29 diff --git a/build.sh b/build.sh index 017ad5d..ec1f57b 100644 --- a/build.sh +++ b/build.sh @@ -15,7 +15,7 @@ LICENSE_FILE=$WORKDIR/LICENSE echo "LICENSE_FILE: $LICENSE_FILE" echo "----------------------------------------------------------------------" -echo "Please check the paths above! It's right? (y/n)" +echo "Want to continue? (y/n)" read -r right if [ -z "$right" ]; then echo "Input y or n to continue..." @@ -23,40 +23,37 @@ if [ -z "$right" ]; then fi if [ "$right" == "n" ]; then - echo "Fix the wrong paths to continue..." + echo "Fix the problems to continue..." exit fi echo "----------------------------------------------------------------------" -# Start building -echo "Start building..." -rm -r "${TARGET:?}" && mkdir -p "$TARGET" +# Prepare +echo "Preparing..." +mkdir -p "$TARGET" && rm -rf "${TARGET:?}"/*.tar.gz cd "$WORKDIR"/cmd/postar || exit -# Go build: windows, linux and darwin -echo "Building windows version..." -CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o "$TARGET"/postar-windows.exe +# build builds the target os and arch version package +function build() { + local GOOS=$1 + local GOARCH=$2 + local BINARY_FILE=$3 + local PKG_FILE="$TARGET"/postar-$VERSION-$GOOS-$GOARCH.tar.gz -echo "Building linux version..." -CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o "$TARGET"/postar-linux -chmod +x "$TARGET"/postar-linux + CGO_ENABLED=0 GOOS=$GOOS GOARCH=$GOARCH go build -o "$BINARY_FILE" + tar -czPf "$PKG_FILE" "$BINARY_FILE" "$CONFIG_FILE" "$LICENSE_FILE" + echo "The $GOOS-$GOARCH package can be found in $PKG_FILE" + rm "$BINARY_FILE" +} -echo "Building darwin version..." -CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o "$TARGET"/postar-darwin -chmod +x "$TARGET"/postar-darwin +echo "Building windows-amd64 version..." +build windows amd64 "$TARGET"/postar.exe -# Before packaging -echo "Start packaging..." -cd "$TARGET" || exit -cp "$CONFIG_FILE" "$TARGET"/ -cp "$LICENSE_FILE" "$TARGET"/ +echo "Building linux-amd64 version..." +build linux amd64 "$TARGET"/postar -# Start Packaging -echo "Packaging windows version..." -tar -czf postar-$VERSION-windows.tar.gz "$TARGET"/postar-windows.exe "$CONFIG_FILE" "$LICENSE_FILE" -tar -czf postar-$VERSION-linux.tar.gz "$TARGET"/postar-linux "$CONFIG_FILE" "$LICENSE_FILE" -tar -czf postar-$VERSION-darwin.tar.gz "$TARGET"/postar-darwin "$CONFIG_FILE" "$LICENSE_FILE" +echo "Building darwin-amd64 version..." +build darwin amd64 "$TARGET"/postar # Done echo "Done." -rm "$TARGET"/postar-windows.exe "$TARGET"/postar-linux "$TARGET"/postar-darwin "$CONFIG_FILE" "$LICENSE_FILE" diff --git a/pkg/encode/encode.go b/pkg/encode/encode.go index 851d529..3ce70ce 100644 --- a/pkg/encode/encode.go +++ b/pkg/encode/encode.go @@ -13,7 +13,6 @@ import ( "fmt" "math/rand" "os" - "strconv" "time" ) @@ -27,15 +26,10 @@ var ( 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', } - pid = strconv.Itoa(os.Getpid()) + pidHex = numberHex(uint64(os.Getpid()), 6, 0) random = rand.New(rand.NewSource(time.Now().Unix())) ) -// PID returns pid in string. -func PID() string { - return pid -} - // numberHex returns num in hex string. // The hex string will be cut with start and end. func numberHex(num uint64, start int, end int) string { @@ -53,13 +47,18 @@ func numberHex(num uint64, start int, end int) string { return fmt.Sprintf("%x", b[start:end]) } +// PIDHex returns pid in string. +func PIDHex() string { + return pidHex +} + // NowHex returns in current time in hex string. func NowHex() string { return numberHex(uint64(time.Now().Unix()), 4, 0) } -// RandomString returns a string including 0-9/a-z/A-Z not longer than length. -func RandomString(length int) string { +// StringHex returns a string including 0-9/a-z/A-Z not longer than length. +func StringHex(length int) string { b := make([]byte, length) for i := 0; i < length; i++ { b[i] = letters[random.Intn(62)] diff --git a/pkg/encode/encode_test.go b/pkg/encode/encode_test.go index 554b9bd..08be21d 100644 --- a/pkg/encode/encode_test.go +++ b/pkg/encode/encode_test.go @@ -8,7 +8,22 @@ package encode -import "testing" +import ( + "os" + "testing" + "time" +) + +// go test -v -cover -run=^TestPidHex$ +func TestPidHex(t *testing.T) { + pidHex := PIDHex() + if len(pidHex) != 4 { + t.Errorf("length of PIDHex is wrong with %s, %d", pidHex, len(pidHex)) + } + + pid := uint64(os.Getpid()) + t.Log(pid, numberHex(pid, 0, 0), pidHex) +} // go test -v -cover -run=^NowTimeHex$ func TestNowTimeHex(t *testing.T) { @@ -16,21 +31,16 @@ func TestNowTimeHex(t *testing.T) { if len(timeHex) != 8 { t.Errorf("length of TimeHex is wrong with %s, %d", timeHex, len(timeHex)) } -} -// go test -v -cover -run=^TestPidHex$ -func TestPidHex(t *testing.T) { - pidHex := PID() - if len(pidHex) != 8 { - t.Errorf("length of PID is wrong with %s, %d", pidHex, len(pidHex)) - } + now := uint64(time.Now().Unix()) + t.Log(now, numberHex(now, 0, 0), timeHex) } -// go test -v -cover -run=^TestRandomString$ -func TestRandomString(t *testing.T) { +// go test -v -cover -run=^TestStringHex$ +func TestStringHex(t *testing.T) { length := 16 - str := RandomString(length) + str := StringHex(length) if len(str) != length { - t.Errorf("length of RandomString is wrong with %d", len(str)) + t.Errorf("length of StringHex is wrong with %d", len(str)) } } diff --git a/pkg/trace/trace.go b/pkg/trace/trace.go index 69d1aa7..f617f77 100644 --- a/pkg/trace/trace.go +++ b/pkg/trace/trace.go @@ -20,8 +20,8 @@ var ( // NewTraceID returns a new trace id. func NewTraceID() string { - salt := encode.RandomString(6) - return encode.NowHex() + salt[:3] + encode.PID() + salt[3:] + salt := encode.StringHex(6) + return encode.NowHex() + salt[:3] + encode.PIDHex() + salt[3:] } // NewContext wraps ctx with a trace id. From 21be669bab3ae84b11d36a2428c842e9c3c11d74 Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Tue, 25 Jan 2022 01:26:29 +0800 Subject: [PATCH 14/27] =?UTF-8?q?=E9=87=8D=E6=9E=84=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _examples/config/postar.ini | 10 ++++++++++ _icons/coverage.svg | 4 ++-- cmd/postar/main.go | 2 +- configs/config.go | 37 ++++++++++++++++++++++++++++++++++++- internal/biz/smtp.go | 4 ++-- internal/biz/smtp_test.go | 2 +- internal/server/grpc.go | 2 +- internal/server/server.go | 4 ++-- 8 files changed, 55 insertions(+), 10 deletions(-) diff --git a/_examples/config/postar.ini b/_examples/config/postar.ini index e69de29..3573613 100644 --- a/_examples/config/postar.ini +++ b/_examples/config/postar.ini @@ -0,0 +1,10 @@ +[worker] +number = 64 +[server] +type = "http" +address = ":5897" +[smtp] +host = "" +port = 587 +user = "" +password = "" \ No newline at end of file diff --git a/_icons/coverage.svg b/_icons/coverage.svg index 27f053e..303f691 100644 --- a/_icons/coverage.svg +++ b/_icons/coverage.svg @@ -10,7 +10,7 @@ coverage coverage - xx% - xx% + 100% + 100% \ No newline at end of file diff --git a/cmd/postar/main.go b/cmd/postar/main.go index 4cad744..889843f 100644 --- a/cmd/postar/main.go +++ b/cmd/postar/main.go @@ -48,7 +48,7 @@ func initLogger(c *configs.Config) *logit.Logger { } func initPool(c *configs.Config) *ants.Pool { - pool, err := ants.NewPool(64) + pool, err := ants.NewPool(c.WorkerNumber()) if err != nil { panic(err) } diff --git a/configs/config.go b/configs/config.go index 67e27db..eadc3e5 100644 --- a/configs/config.go +++ b/configs/config.go @@ -8,10 +8,15 @@ package configs +type WorkerConfig struct { + Number int `int:"number"` // The number of worker. +} + type ServerConfig struct { Type string `int:"type"` // The type of server. Address string `ini:"address"` // The address(including ip and port) of server. } + type SMTPConfig struct { Host string `int:"host"` // The host of smtp server. Port int `int:"port"` // The port of smtp server. @@ -21,6 +26,7 @@ type SMTPConfig struct { // Config stores all configurations of postar. type Config struct { + Worker WorkerConfig `ini:"worker"` Server ServerConfig `int:"server"` SMTP SMTPConfig `int:"smtp"` } @@ -28,6 +34,9 @@ type Config struct { // NewDefaultConfig returns a new config. func NewDefaultConfig() *Config { return &Config{ + Worker: WorkerConfig{ + Number: 64, + }, Server: ServerConfig{ Type: "http", Address: ":5897", @@ -38,4 +47,30 @@ func NewDefaultConfig() *Config { } } -// TODO 封装配置查询方法 +func (c *Config) WorkerNumber() int { + return c.Worker.Number +} + +func (c *Config) ServerType() string { + return c.Server.Type +} + +func (c *Config) ServerAddress() string { + return c.Server.Address +} + +func (c *Config) SMTPHost() string { + return c.SMTP.Host +} + +func (c *Config) SMTPPort() int { + return c.SMTP.Port +} + +func (c *Config) SMTPUser() string { + return c.SMTP.User +} + +func (c *Config) SMTPPassword() string { + return c.SMTP.Password +} diff --git a/internal/biz/smtp.go b/internal/biz/smtp.go index 0db0638..535a8e5 100644 --- a/internal/biz/smtp.go +++ b/internal/biz/smtp.go @@ -39,10 +39,10 @@ func NewSMTPBiz(c *configs.Config, logger *logit.Logger, pool *ants.Pool) *SMTPB func (sb *SMTPBiz) sendEmail(email *model.Email) error { msg := gomail.NewMessage() msg.SetHeader("Subject", email.Subject) - msg.SetHeader("From", sb.c.SMTP.User) + msg.SetHeader("From", sb.c.SMTPUser()) msg.SetHeader("To", email.Receivers...) msg.SetBody(email.BodyType, email.Body) - return gomail.NewDialer(sb.c.SMTP.Host, sb.c.SMTP.Port, sb.c.SMTP.User, sb.c.SMTP.Password).DialAndSend(msg) + return gomail.NewDialer(sb.c.SMTPHost(), sb.c.SMTPPort(), sb.c.SMTPUser(), sb.c.SMTPPassword()).DialAndSend(msg) } // SendEmail sends email to somewhere. diff --git a/internal/biz/smtp_test.go b/internal/biz/smtp_test.go index bd0c83d..c56bef1 100644 --- a/internal/biz/smtp_test.go +++ b/internal/biz/smtp_test.go @@ -45,7 +45,7 @@ func TestSMTPBiz(t *testing.T) { pool, _ := ants.NewPool(64) defer pool.Release() - + smtpService := NewSMTPBiz(c, logit.NewLogger(), pool) err := smtpService.SendEmail(context.Background(), &model.Email{ Subject: t.Name(), diff --git a/internal/server/grpc.go b/internal/server/grpc.go index 72eb9f8..c27767c 100644 --- a/internal/server/grpc.go +++ b/internal/server/grpc.go @@ -70,7 +70,7 @@ func (gs *GRPCServer) SendEmail(ctx context.Context, request *api.SendEmailReque // Start starts GRPCServer. func (gs *GRPCServer) Start() error { - listener, err := net.Listen("tcp", gs.c.Server.Address) + listener, err := net.Listen("tcp", gs.c.ServerAddress()) if err != nil { return err } diff --git a/internal/server/server.go b/internal/server/server.go index 4bf4dd2..75e4633 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -28,9 +28,9 @@ type Server interface { } func NewServer(c *configs.Config, logger *logit.Logger, smtpBiz *biz.SMTPBiz) Server { - newServer, ok := servers[c.Server.Type] + newServer, ok := servers[c.ServerType()] if !ok { - panic(fmt.Errorf("server: type %s not found", c.Server.Type)) + panic(fmt.Errorf("server: type %s not found", c.ServerType())) } return newServer(c, logger, smtpBiz) } From fe48da8773a2f563c2cdb8eb7682b3ffc46465fb Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Tue, 25 Jan 2022 01:43:05 +0800 Subject: [PATCH 15/27] =?UTF-8?q?=E8=BD=AC=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/config.go | 18 ++++++++++++++++-- internal/biz/smtp.go | 2 +- internal/biz/smtp_test.go | 4 ++-- internal/model/smtp.go | 24 +++++++++++++++++++----- internal/server/grpc.go | 2 +- internal/server/server.go | 29 +++++++++++++++++++++++++++++ 6 files changed, 68 insertions(+), 11 deletions(-) diff --git a/configs/config.go b/configs/config.go index eadc3e5..7de50fc 100644 --- a/configs/config.go +++ b/configs/config.go @@ -8,8 +8,12 @@ package configs +import "time" + type WorkerConfig struct { - Number int `int:"number"` // The number of worker. + Number int `int:"number"` // The number of worker. + Async bool `int:"async"` // The sending mode of worker. + Timeout int `int:"timeout"` // The sending timeout of worker. } type ServerConfig struct { @@ -35,7 +39,9 @@ type Config struct { func NewDefaultConfig() *Config { return &Config{ Worker: WorkerConfig{ - Number: 64, + Number: 64, + Async: false, + Timeout: 10000, }, Server: ServerConfig{ Type: "http", @@ -51,6 +57,14 @@ func (c *Config) WorkerNumber() int { return c.Worker.Number } +func (c *Config) WorkerAsync() bool { + return c.Worker.Async +} + +func (c *Config) WorkerTimeout() time.Duration { + return time.Duration(c.Worker.Timeout) * time.Millisecond +} + func (c *Config) ServerType() string { return c.Server.Type } diff --git a/internal/biz/smtp.go b/internal/biz/smtp.go index 535a8e5..d45e184 100644 --- a/internal/biz/smtp.go +++ b/internal/biz/smtp.go @@ -50,7 +50,7 @@ func (sb *SMTPBiz) SendEmail(ctx context.Context, email *model.Email, options *m logger := logit.FromContext(ctx) if options == nil { - options = model.DefaultSendEmailOptions() + options = model.DefaultSendEmailOptions(sb.c) logger.Debug("options is nil, using default options").Any("options", options).End() } diff --git a/internal/biz/smtp_test.go b/internal/biz/smtp_test.go index c56bef1..4a34267 100644 --- a/internal/biz/smtp_test.go +++ b/internal/biz/smtp_test.go @@ -43,7 +43,7 @@ func TestSMTPBiz(t *testing.T) { t.Skipf("smtp host %s or user %s or password %s or receiver %s is empty", c.SMTP.Host, c.SMTP.User, c.SMTP.Password, receiver) } - pool, _ := ants.NewPool(64) + pool, _ := ants.NewPool(c.WorkerNumber()) defer pool.Release() smtpService := NewSMTPBiz(c, logit.NewLogger(), pool) @@ -52,7 +52,7 @@ func TestSMTPBiz(t *testing.T) { Receivers: []string{receiver}, BodyType: "text/html;charset=utf-8", Body: t.Name() + time.Now().Format("20060102150405.000"), - }, model.DefaultSendEmailOptions()) + }, model.DefaultSendEmailOptions(c)) if err != nil { t.Error(err) diff --git a/internal/model/smtp.go b/internal/model/smtp.go index 8333571..e858868 100644 --- a/internal/model/smtp.go +++ b/internal/model/smtp.go @@ -8,9 +8,12 @@ package model -import "time" +import ( + "time" + + "github.com/avinoplan/postar/configs" +) -// Email is an email. type Email struct { Subject string // Subject. Receivers []string // Receivers. @@ -18,6 +21,10 @@ type Email struct { Body string // Body. } +func NewEmail() *Email { + return new(Email) +} + // SendEmailOptions is the options of sending one email. type SendEmailOptions struct { Async bool // The mode of sending one email. @@ -25,9 +32,16 @@ type SendEmailOptions struct { } // DefaultSendEmailOptions returns a default options for sending emails. -func DefaultSendEmailOptions() *SendEmailOptions { +func DefaultSendEmailOptions(c *configs.Config) *SendEmailOptions { + if c == nil { + return &SendEmailOptions{ + Async: false, + Timeout: 10 * time.Second, + } + } + return &SendEmailOptions{ - Async: false, - Timeout: 5 * time.Second, + Async: c.WorkerAsync(), + Timeout: c.WorkerTimeout(), } } diff --git a/internal/server/grpc.go b/internal/server/grpc.go index c27767c..6d762a7 100644 --- a/internal/server/grpc.go +++ b/internal/server/grpc.go @@ -45,7 +45,7 @@ func (gs *GRPCServer) SendEmail(ctx context.Context, request *api.SendEmailReque ctx = trace.NewContext(ctx, traceID) ctx = logit.NewContext(ctx, gs.logger) - err := gs.smtpBiz.SendEmail(ctx, nil, nil) + err := gs.smtpBiz.SendEmail(ctx, toModelEmail(request.Email), toModelSendEmailOptions(gs.c, request.Options)) if errors.IsTimeout(err) { return &api.SendEmailResponse{ Code: api.ServerCode_TIMEOUT, diff --git a/internal/server/server.go b/internal/server/server.go index 75e4633..58a2a91 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -10,10 +10,13 @@ package server import ( "fmt" + "time" "github.com/FishGoddess/logit" + "github.com/avinoplan/postar/api" "github.com/avinoplan/postar/configs" "github.com/avinoplan/postar/internal/biz" + "github.com/avinoplan/postar/internal/model" ) var ( @@ -34,3 +37,29 @@ func NewServer(c *configs.Config, logger *logit.Logger, smtpBiz *biz.SMTPBiz) Se } return newServer(c, logger, smtpBiz) } + +func toModelEmail(email *api.Email) *model.Email { + if email == nil { + return nil + } + + result := model.NewEmail() + result.Subject = email.Subject + result.Receivers = email.Receivers + result.BodyType = email.BodyType + result.Body = email.Body + return result +} + +func toModelSendEmailOptions(c *configs.Config, opts *api.SendEmailOptions) *model.SendEmailOptions { + if opts == nil { + return nil + } + + result := model.DefaultSendEmailOptions(c) + result.Async = opts.Async + if opts.Timeout > 0 { + result.Timeout = time.Duration(opts.Timeout) * time.Millisecond + } + return result +} From 643c944968ba69996d5e5fd7c36572da106805d7 Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Tue, 25 Jan 2022 22:41:58 +0800 Subject: [PATCH 16/27] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20build.sh=20=E6=89=93?= =?UTF-8?q?=E5=8C=85=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/build.sh b/build.sh index ec1f57b..153bf0e 100644 --- a/build.sh +++ b/build.sh @@ -7,12 +7,13 @@ echo "----------------------------------------------------------------------" # Check WORKDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) echo "WORKDIR: $WORKDIR" +CONFIG_DIR=$WORKDIR/_examples/config +CONFIG_FILE=postar.ini +echo "CONFIG: $CONFIG_DIR/$CONFIG_FILE" +LICENSE_FILE=LICENSE +echo "LICENSE: $WORKDIR/$LICENSE" TARGET=$WORKDIR/target echo "TARGET: $TARGET" -CONFIG_FILE=$WORKDIR/_examples/config/postar.ini -echo "CONFIG_FILE: $CONFIG_FILE" -LICENSE_FILE=$WORKDIR/LICENSE -echo "LICENSE_FILE: $LICENSE_FILE" echo "----------------------------------------------------------------------" echo "Want to continue? (y/n)" @@ -40,20 +41,20 @@ function build() { local BINARY_FILE=$3 local PKG_FILE="$TARGET"/postar-$VERSION-$GOOS-$GOARCH.tar.gz - CGO_ENABLED=0 GOOS=$GOOS GOARCH=$GOARCH go build -o "$BINARY_FILE" - tar -czPf "$PKG_FILE" "$BINARY_FILE" "$CONFIG_FILE" "$LICENSE_FILE" + CGO_ENABLED=0 GOOS=$GOOS GOARCH=$GOARCH go build -o "$TARGET"/"$BINARY_FILE" + tar -czf "$PKG_FILE" -C "$TARGET" "$BINARY_FILE" -C "$CONFIG_DIR" "$CONFIG_FILE" -C "$WORKDIR" "$LICENSE_FILE" echo "The $GOOS-$GOARCH package can be found in $PKG_FILE" - rm "$BINARY_FILE" + rm "$TARGET"/"$BINARY_FILE" } echo "Building windows-amd64 version..." -build windows amd64 "$TARGET"/postar.exe +build windows amd64 postar.exe echo "Building linux-amd64 version..." -build linux amd64 "$TARGET"/postar +build linux amd64 postar echo "Building darwin-amd64 version..." -build darwin amd64 "$TARGET"/postar +build darwin amd64 postar # Done echo "Done." From 2d52b740aafd17b3b9bbff00516d4e1767d8c74f Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Tue, 25 Jan 2022 23:35:06 +0800 Subject: [PATCH 17/27] =?UTF-8?q?=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _examples/config/postar.ini | 28 +++++++++++++++++++++++-- _icons/coverage.svg | 4 ++-- build.sh | 10 ++++----- cmd/postar/main.go | 2 +- configs/config.go | 42 ++++++++++++++++++++++++------------- internal/biz/smtp_test.go | 2 +- internal/model/smtp.go | 4 ++-- internal/model/smtp_test.go | 38 +++++++++++++++++++++++++++++++++ 8 files changed, 102 insertions(+), 28 deletions(-) create mode 100644 internal/model/smtp_test.go diff --git a/_examples/config/postar.ini b/_examples/config/postar.ini index 3573613..e57c77c 100644 --- a/_examples/config/postar.ini +++ b/_examples/config/postar.ini @@ -1,10 +1,34 @@ -[worker] -number = 64 +[task] +# Start how many workers to finish tasks. +# Default is 64. +worker_number = 64 +# The max size of task queue, tasks will be blocked if queue is full. +# Set to 0 if you want queue is unlimited. +queue_size = 0 +# The sending mode of task, available values: true, false. +# This will apply to all tasks and you can use options to override this value in request. +# Default is false. +async = false +# The sending timeout in millisecond of task. +# This will apply to all tasks and you can use options to override this value in request. +# Default is 10s. +timeout = 10000 [server] +# Available values: tcp, tcp4, tcp6, unix, unixpacket. +# Default is tcp. +network = "tcp" +# Available values: http, grpc. +# Default is http. type = "http" +# You can bind a specific ip and port for your server. address = ":5897" [smtp] +# Specify your smtp server host. host = "" +# Specify your smtp server port. +# Default is 587. port = 587 +# Specify your smtp server user. user = "" +# Specify your smtp server password. password = "" \ No newline at end of file diff --git a/_icons/coverage.svg b/_icons/coverage.svg index 303f691..7a7a964 100644 --- a/_icons/coverage.svg +++ b/_icons/coverage.svg @@ -10,7 +10,7 @@ coverage coverage - 100% - 100% + 64% + 64% \ No newline at end of file diff --git a/build.sh b/build.sh index 153bf0e..84be297 100644 --- a/build.sh +++ b/build.sh @@ -31,7 +31,7 @@ echo "----------------------------------------------------------------------" # Prepare echo "Preparing..." -mkdir -p "$TARGET" && rm -rf "${TARGET:?}"/*.tar.gz +mkdir -p "$TARGET" && rm -rf "${TARGET:?}"/*.tar.gz || exit cd "$WORKDIR"/cmd/postar || exit # build builds the target os and arch version package @@ -41,10 +41,10 @@ function build() { local BINARY_FILE=$3 local PKG_FILE="$TARGET"/postar-$VERSION-$GOOS-$GOARCH.tar.gz - CGO_ENABLED=0 GOOS=$GOOS GOARCH=$GOARCH go build -o "$TARGET"/"$BINARY_FILE" - tar -czf "$PKG_FILE" -C "$TARGET" "$BINARY_FILE" -C "$CONFIG_DIR" "$CONFIG_FILE" -C "$WORKDIR" "$LICENSE_FILE" - echo "The $GOOS-$GOARCH package can be found in $PKG_FILE" - rm "$TARGET"/"$BINARY_FILE" + CGO_ENABLED=0 GOOS=$GOOS GOARCH=$GOARCH go build -o "$TARGET"/"$BINARY_FILE" || exit + tar -czf "$PKG_FILE" -C "$TARGET" "$BINARY_FILE" -C "$CONFIG_DIR" "$CONFIG_FILE" -C "$WORKDIR" "$LICENSE_FILE" || exit + echo "The $GOOS-$GOARCH package can be found in $PKG_FILE" || exit + rm "$TARGET"/"$BINARY_FILE" || exit } echo "Building windows-amd64 version..." diff --git a/cmd/postar/main.go b/cmd/postar/main.go index 889843f..5749cb6 100644 --- a/cmd/postar/main.go +++ b/cmd/postar/main.go @@ -48,7 +48,7 @@ func initLogger(c *configs.Config) *logit.Logger { } func initPool(c *configs.Config) *ants.Pool { - pool, err := ants.NewPool(c.WorkerNumber()) + pool, err := ants.NewPool(c.TaskWorkerNumber(), ants.WithMaxBlockingTasks(c.TaskQueueSize())) if err != nil { panic(err) } diff --git a/configs/config.go b/configs/config.go index 7de50fc..3dbdc9a 100644 --- a/configs/config.go +++ b/configs/config.go @@ -10,13 +10,15 @@ package configs import "time" -type WorkerConfig struct { - Number int `int:"number"` // The number of worker. - Async bool `int:"async"` // The sending mode of worker. - Timeout int `int:"timeout"` // The sending timeout of worker. +type TaskConfig struct { + WorkerNumber int `int:"worker_number"` // The number of task worker. + QueueSize int `int:"queue_size"` // The max size of task queue. + Async bool `int:"async"` // The sending mode of task. + Timeout int `int:"timeout"` // The sending timeout in millisecond of task. } type ServerConfig struct { + Network string `int:"network"` // The network of server, see net.Listen. Type string `int:"type"` // The type of server. Address string `ini:"address"` // The address(including ip and port) of server. } @@ -30,7 +32,7 @@ type SMTPConfig struct { // Config stores all configurations of postar. type Config struct { - Worker WorkerConfig `ini:"worker"` + Task TaskConfig `ini:"task"` Server ServerConfig `int:"server"` SMTP SMTPConfig `int:"smtp"` } @@ -38,12 +40,14 @@ type Config struct { // NewDefaultConfig returns a new config. func NewDefaultConfig() *Config { return &Config{ - Worker: WorkerConfig{ - Number: 64, - Async: false, - Timeout: 10000, + Task: TaskConfig{ + WorkerNumber: 64, + QueueSize: 0, + Async: false, + Timeout: 10000, // 10s }, Server: ServerConfig{ + Network: "tcp", Type: "http", Address: ":5897", }, @@ -53,16 +57,24 @@ func NewDefaultConfig() *Config { } } -func (c *Config) WorkerNumber() int { - return c.Worker.Number +func (c *Config) TaskWorkerNumber() int { + return c.Task.WorkerNumber } -func (c *Config) WorkerAsync() bool { - return c.Worker.Async +func (c *Config) TaskQueueSize() int { + return c.Task.QueueSize } -func (c *Config) WorkerTimeout() time.Duration { - return time.Duration(c.Worker.Timeout) * time.Millisecond +func (c *Config) TaskAsync() bool { + return c.Task.Async +} + +func (c *Config) TaskTimeout() time.Duration { + return time.Duration(c.Task.Timeout) * time.Millisecond +} + +func (c *Config) ServerNetwork() string { + return c.Server.Network } func (c *Config) ServerType() string { diff --git a/internal/biz/smtp_test.go b/internal/biz/smtp_test.go index 4a34267..6186092 100644 --- a/internal/biz/smtp_test.go +++ b/internal/biz/smtp_test.go @@ -43,7 +43,7 @@ func TestSMTPBiz(t *testing.T) { t.Skipf("smtp host %s or user %s or password %s or receiver %s is empty", c.SMTP.Host, c.SMTP.User, c.SMTP.Password, receiver) } - pool, _ := ants.NewPool(c.WorkerNumber()) + pool, _ := ants.NewPool(c.TaskWorkerNumber()) defer pool.Release() smtpService := NewSMTPBiz(c, logit.NewLogger(), pool) diff --git a/internal/model/smtp.go b/internal/model/smtp.go index e858868..0ba4dbc 100644 --- a/internal/model/smtp.go +++ b/internal/model/smtp.go @@ -41,7 +41,7 @@ func DefaultSendEmailOptions(c *configs.Config) *SendEmailOptions { } return &SendEmailOptions{ - Async: c.WorkerAsync(), - Timeout: c.WorkerTimeout(), + Async: c.TaskAsync(), + Timeout: c.TaskTimeout(), } } diff --git a/internal/model/smtp_test.go b/internal/model/smtp_test.go new file mode 100644 index 0000000..cf33370 --- /dev/null +++ b/internal/model/smtp_test.go @@ -0,0 +1,38 @@ +// Copyright 2022 Ye Zi Jie. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. +// +// Author: FishGoddess +// Email: fishgoddess@qq.com +// Created at 2022/01/25 23:24:00 + +package model + +import ( + "testing" + "time" + + "github.com/avinoplan/postar/configs" +) + +// go test -v -cover -run=^TestDefaultSendEmailOptions$ +func TestDefaultSendEmailOptions(t *testing.T) { + opts := DefaultSendEmailOptions(nil) + if opts.Async { + t.Errorf("opts.Async %+v is wrong", opts.Async) + } + + if opts.Timeout != 10*time.Second { + t.Errorf("opts.Timeout %d != 10 * time.Second", opts.Timeout) + } + + c := configs.NewDefaultConfig() + opts = DefaultSendEmailOptions(c) + if opts.Async != c.TaskAsync() { + t.Errorf("opts.Async %+v != c.TaskAsync() %+v", opts.Async, c.TaskAsync()) + } + + if opts.Timeout != c.TaskTimeout() { + t.Errorf("opts.Timeout %d != c.TaskTimeout() %d", opts.Timeout, c.TaskTimeout()) + } +} From 1c3d44f91d5b74f1d6723578ca05d5f497b85c99 Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Tue, 25 Jan 2022 23:53:15 +0800 Subject: [PATCH 18/27] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20ini=20=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/postar/main.go | 8 +++++++- configs/config.go | 24 ++++++++++++------------ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/cmd/postar/main.go b/cmd/postar/main.go index 5749cb6..0684f56 100644 --- a/cmd/postar/main.go +++ b/cmd/postar/main.go @@ -11,6 +11,7 @@ package main import ( "flag" "fmt" + "log" "os" "os/signal" "runtime" @@ -40,7 +41,12 @@ func loadConfig() (*configs.Config, error) { c := configs.NewDefaultConfig() err := ini.MapTo(c, *configFile) - return c, err + if err != nil { + return nil, err + } + + log.Printf("Load config %+v\n", *c) + return c, nil } func initLogger(c *configs.Config) *logit.Logger { diff --git a/configs/config.go b/configs/config.go index 3dbdc9a..34eef31 100644 --- a/configs/config.go +++ b/configs/config.go @@ -11,30 +11,30 @@ package configs import "time" type TaskConfig struct { - WorkerNumber int `int:"worker_number"` // The number of task worker. - QueueSize int `int:"queue_size"` // The max size of task queue. - Async bool `int:"async"` // The sending mode of task. - Timeout int `int:"timeout"` // The sending timeout in millisecond of task. + WorkerNumber int `ini:"worker_number"` // The number of task worker. + QueueSize int `ini:"queue_size"` // The max size of task queue. + Async bool `ini:"async"` // The sending mode of task. + Timeout int `ini:"timeout"` // The sending timeout in millisecond of task. } type ServerConfig struct { - Network string `int:"network"` // The network of server, see net.Listen. - Type string `int:"type"` // The type of server. + Network string `ini:"network"` // The network of server, see net.Listen. + Type string `ini:"type"` // The type of server. Address string `ini:"address"` // The address(including ip and port) of server. } type SMTPConfig struct { - Host string `int:"host"` // The host of smtp server. - Port int `int:"port"` // The port of smtp server. - User string `int:"user"` // The user of smtp server. - Password string `int:"password"` // The password of smtp server. + Host string `ini:"host"` // The host of smtp server. + Port int `ini:"port"` // The port of smtp server. + User string `ini:"user"` // The user of smtp server. + Password string `ini:"password"` // The password of smtp server. } // Config stores all configurations of postar. type Config struct { Task TaskConfig `ini:"task"` - Server ServerConfig `int:"server"` - SMTP SMTPConfig `int:"smtp"` + Server ServerConfig `ini:"server"` + SMTP SMTPConfig `ini:"smtp"` } // NewDefaultConfig returns a new config. From c1026c0932c7ebacd5a2fb770dfa20883144189a Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Fri, 28 Jan 2022 23:57:50 +0800 Subject: [PATCH 19/27] =?UTF-8?q?=E5=8E=BB=E9=99=A4=20server.network=20?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _examples/config/postar.ini | 3 --- build.sh | 0 configs/config.go | 6 ------ 3 files changed, 9 deletions(-) mode change 100644 => 100755 build.sh diff --git a/_examples/config/postar.ini b/_examples/config/postar.ini index e57c77c..6a07dca 100644 --- a/_examples/config/postar.ini +++ b/_examples/config/postar.ini @@ -14,9 +14,6 @@ async = false # Default is 10s. timeout = 10000 [server] -# Available values: tcp, tcp4, tcp6, unix, unixpacket. -# Default is tcp. -network = "tcp" # Available values: http, grpc. # Default is http. type = "http" diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 diff --git a/configs/config.go b/configs/config.go index 34eef31..5c10be4 100644 --- a/configs/config.go +++ b/configs/config.go @@ -18,7 +18,6 @@ type TaskConfig struct { } type ServerConfig struct { - Network string `ini:"network"` // The network of server, see net.Listen. Type string `ini:"type"` // The type of server. Address string `ini:"address"` // The address(including ip and port) of server. } @@ -47,7 +46,6 @@ func NewDefaultConfig() *Config { Timeout: 10000, // 10s }, Server: ServerConfig{ - Network: "tcp", Type: "http", Address: ":5897", }, @@ -73,10 +71,6 @@ func (c *Config) TaskTimeout() time.Duration { return time.Duration(c.Task.Timeout) * time.Millisecond } -func (c *Config) ServerNetwork() string { - return c.Server.Network -} - func (c *Config) ServerType() string { return c.Server.Type } From d122f2da74c869b4eadab1e621ccaa98c354b479 Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Sat, 29 Jan 2022 19:57:46 +0800 Subject: [PATCH 20/27] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20.gitignore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index a201e7f..a9be9ca 100644 --- a/.gitignore +++ b/.gitignore @@ -4,15 +4,15 @@ *.dll *.so *.dylib +.DS_Store # IDE .idea/ -*.iml .vscode/ +*.iml # Program +target/ *.test *.out -target/ -*.tar.gz *.log From c3c33ee360f30dbc7cfa33b05dad422c29ead2d9 Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Sat, 29 Jan 2022 20:51:19 +0800 Subject: [PATCH 21/27] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=A7=BF=E5=8A=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/postar/main.go | 29 ++++++++++---------------- go.mod | 2 +- go.sum | 15 +++++++------ internal/biz/smtp.go | 16 ++++++-------- internal/server/grpc.go | 6 +----- internal/server/server.go | 7 +++---- pkg/log/log.go | 44 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 73 insertions(+), 46 deletions(-) create mode 100644 pkg/log/log.go diff --git a/cmd/postar/main.go b/cmd/postar/main.go index 0684f56..e6a001c 100644 --- a/cmd/postar/main.go +++ b/cmd/postar/main.go @@ -11,13 +11,12 @@ package main import ( "flag" "fmt" - "log" + "github.com/avinoplan/postar/pkg/log" "os" "os/signal" "runtime" "syscall" - "github.com/FishGoddess/logit" "github.com/avinoplan/postar/configs" "github.com/avinoplan/postar/internal/biz" "github.com/avinoplan/postar/internal/server" @@ -41,16 +40,7 @@ func loadConfig() (*configs.Config, error) { c := configs.NewDefaultConfig() err := ini.MapTo(c, *configFile) - if err != nil { - return nil, err - } - - log.Printf("Load config %+v\n", *c) - return c, nil -} - -func initLogger(c *configs.Config) *logit.Logger { - return logit.NewLogger() + return c, err } func initPool(c *configs.Config) *ants.Pool { @@ -61,8 +51,8 @@ func initPool(c *configs.Config) *ants.Pool { return pool } -func runServer(c *configs.Config, logger *logit.Logger, smtpBiz *biz.SMTPBiz) { - svr := server.NewServer(c, logger, smtpBiz) +func runServer(c *configs.Config, smtpBiz *biz.SMTPBiz) { + svr := server.NewServer(c, smtpBiz) go func() { err := svr.Start() @@ -84,12 +74,15 @@ func main() { panic(err) } - logger := initLogger(c) - defer logger.Close() + err = log.Initialize(c) + if err != nil { + panic(err) + } + defer log.Close() pool := initPool(c) defer pool.Release() - smtpBiz := biz.NewSMTPBiz(c, logger, pool) - runServer(c, logger, smtpBiz) + smtpBiz := biz.NewSMTPBiz(c, pool) + runServer(c, smtpBiz) } diff --git a/go.mod b/go.mod index 523085d..76beee2 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.15 require ( github.com/FishGoddess/errors v0.0.2 - github.com/FishGoddess/logit v0.4.11 + github.com/FishGoddess/logit v0.4.12-alpha github.com/go-ini/ini v1.66.3 github.com/panjf2000/ants/v2 v2.4.7 google.golang.org/grpc v1.40.0 diff --git a/go.sum b/go.sum index 268e58b..1f679e3 100644 --- a/go.sum +++ b/go.sum @@ -3,13 +3,10 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/FishGoddess/errors v0.0.2 h1:g1S1HDrnjSuMS0sl92lyE+tXsfTn7Dp/lKlOvAPN5DA= github.com/FishGoddess/errors v0.0.2/go.mod h1:2YWw8ijAMQIfs4FyOFjvahUPQWhgS6lFTkBG++n61+Y= -github.com/FishGoddess/logit v0.4.7-alpha h1:6pYVA+Qp+KFr/vB7Z4ad9JIhq7Nm9QMWBMcES9QYtvU= -github.com/FishGoddess/logit v0.4.7-alpha/go.mod h1:a2dFSibwfZXNNZ/V1jMOQBImPoySaxdPFThbsm17ZdY= -github.com/FishGoddess/logit v0.4.11 h1:d/KQf4eONmuTg1EVGJ+yQaK+Q/oANsE0G+BEI6hzZC4= -github.com/FishGoddess/logit v0.4.11/go.mod h1:a2dFSibwfZXNNZ/V1jMOQBImPoySaxdPFThbsm17ZdY= +github.com/FishGoddess/logit v0.4.12-alpha h1:dZ+dQbycdubCuyP5SR081Qsm3x9l4A6+x3GrJ0nm+9k= +github.com/FishGoddess/logit v0.4.12-alpha/go.mod h1:a2dFSibwfZXNNZ/V1jMOQBImPoySaxdPFThbsm17ZdY= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/avino-plan/postar v0.2.3-alpha h1:4q/OiQl6qJBBfkQbo8S2b2aLtEKAikJ6+rILCt9DQ+s= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -17,6 +14,7 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -51,15 +49,16 @@ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/panjf2000/ants v1.3.0 h1:8pQ+8leaLc9lys2viEEr8md0U4RN6uOSUCE9bOYjQ9M= github.com/panjf2000/ants/v2 v2.4.7 h1:MZnw2JRyTJxFwtaMtUJcwE618wKD04POWk2gwwP4E2M= github.com/panjf2000/ants/v2 v2.4.7/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A= +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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -125,13 +124,13 @@ google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+Rur google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= +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/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= -gopkg.in/ini.v1 v1.66.3 h1:jRskFVxYaMGAMUbN0UZ7niA9gzL9B49DOqE78vg0k3w= -gopkg.in/ini.v1 v1.66.3/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/biz/smtp.go b/internal/biz/smtp.go index d45e184..e144eef 100644 --- a/internal/biz/smtp.go +++ b/internal/biz/smtp.go @@ -10,8 +10,8 @@ package biz import ( "context" + "github.com/avinoplan/postar/pkg/log" - "github.com/FishGoddess/logit" "github.com/avinoplan/postar/configs" "github.com/avinoplan/postar/internal/model" "github.com/avinoplan/postar/pkg/errors" @@ -22,15 +22,13 @@ import ( // SMTPBiz is the biz of smtp. type SMTPBiz struct { c *configs.Config - logger *logit.Logger pool *ants.Pool // The pool of workers. } // NewSMTPBiz returns a new SMTPBiz. -func NewSMTPBiz(c *configs.Config, logger *logit.Logger, pool *ants.Pool) *SMTPBiz { +func NewSMTPBiz(c *configs.Config, pool *ants.Pool) *SMTPBiz { return &SMTPBiz{ c: c, - logger: logger, pool: pool, } } @@ -47,11 +45,9 @@ func (sb *SMTPBiz) sendEmail(email *model.Email) error { // SendEmail sends email to somewhere. func (sb *SMTPBiz) SendEmail(ctx context.Context, email *model.Email, options *model.SendEmailOptions) error { - logger := logit.FromContext(ctx) - if options == nil { options = model.DefaultSendEmailOptions(sb.c) - logger.Debug("options is nil, using default options").Any("options", options).End() + log.Debug("options is nil, using default options").Any("options", options).End() } ctx, cancel := context.WithTimeout(ctx, options.Timeout) @@ -64,7 +60,7 @@ func (sb *SMTPBiz) SendEmail(ctx context.Context, email *model.Email, options *m }) if err != nil { - logger.Error("submit email sending task to pool failed").Error("err", err).End() + log.Error(err, "submit email sending task to pool failed").End() return errors.SendEmailFailedErr(err) } @@ -75,13 +71,13 @@ func (sb *SMTPBiz) SendEmail(ctx context.Context, email *model.Email, options *m select { case err = <-errorCh: if err != nil { - logger.Error("send email failed").Error("err", err).End() + log.Error(err, "send email failed").End() return errors.SendEmailFailedErr(err) } case <-ctx.Done(): err = ctx.Err() if err != nil { - logger.Error("send email timeout").Error("err", err).End() + log.Error(err, "send email timeout").End() return errors.TimeoutErr(err) } } diff --git a/internal/server/grpc.go b/internal/server/grpc.go index 6d762a7..c77a526 100644 --- a/internal/server/grpc.go +++ b/internal/server/grpc.go @@ -12,7 +12,6 @@ import ( "context" "net" - "github.com/FishGoddess/logit" "github.com/avinoplan/postar/api" "github.com/avinoplan/postar/configs" "github.com/avinoplan/postar/internal/biz" @@ -26,15 +25,13 @@ type GRPCServer struct { api.UnimplementedPostarServiceServer server *grpc.Server c *configs.Config - logger *logit.Logger smtpBiz *biz.SMTPBiz } // NewGRPCServer returns a new GRPCServer. -func NewGRPCServer(c *configs.Config, logger *logit.Logger, smtpBiz *biz.SMTPBiz) Server { +func NewGRPCServer(c *configs.Config, smtpBiz *biz.SMTPBiz) Server { return &GRPCServer{ c: c, - logger: logger, smtpBiz: smtpBiz, } } @@ -43,7 +40,6 @@ func NewGRPCServer(c *configs.Config, logger *logit.Logger, smtpBiz *biz.SMTPBiz func (gs *GRPCServer) SendEmail(ctx context.Context, request *api.SendEmailRequest) (*api.SendEmailResponse, error) { traceID := trace.NewTraceID() ctx = trace.NewContext(ctx, traceID) - ctx = logit.NewContext(ctx, gs.logger) err := gs.smtpBiz.SendEmail(ctx, toModelEmail(request.Email), toModelSendEmailOptions(gs.c, request.Options)) if errors.IsTimeout(err) { diff --git a/internal/server/server.go b/internal/server/server.go index 58a2a91..84b0192 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -12,7 +12,6 @@ import ( "fmt" "time" - "github.com/FishGoddess/logit" "github.com/avinoplan/postar/api" "github.com/avinoplan/postar/configs" "github.com/avinoplan/postar/internal/biz" @@ -20,7 +19,7 @@ import ( ) var ( - servers = map[string]func(c *configs.Config, logger *logit.Logger, smtpBiz *biz.SMTPBiz) Server{ + servers = map[string]func(c *configs.Config, smtpBiz *biz.SMTPBiz) Server{ "grpc": NewGRPCServer, } ) @@ -30,12 +29,12 @@ type Server interface { Stop() error } -func NewServer(c *configs.Config, logger *logit.Logger, smtpBiz *biz.SMTPBiz) Server { +func NewServer(c *configs.Config, smtpBiz *biz.SMTPBiz) Server { newServer, ok := servers[c.ServerType()] if !ok { panic(fmt.Errorf("server: type %s not found", c.ServerType())) } - return newServer(c, logger, smtpBiz) + return newServer(c, smtpBiz) } func toModelEmail(email *api.Email) *model.Email { diff --git a/pkg/log/log.go b/pkg/log/log.go new file mode 100644 index 0000000..cb9f3ec --- /dev/null +++ b/pkg/log/log.go @@ -0,0 +1,44 @@ +package log + +import ( + "github.com/FishGoddess/logit" + "github.com/avinoplan/postar/configs" +) + +var ( + // globalLogger is the logger using in global. + globalLogger *logit.Logger +) + +// Initialize initializes log package with config. +func Initialize(c *configs.Config) error { + options := logit.Options() + globalLogger = logit.NewLogger( + options.WithCallerDepth(4), + ) + return nil +} + +// Debug returns a Log with debug level if debug level is enabled. +func Debug(msg string, args ...interface{}) *logit.Log { + return globalLogger.Debug(msg, args) +} + +// Info returns a Log with info level if info level is enabled. +func Info(msg string, args ...interface{}) *logit.Log { + return globalLogger.Info(msg, args) +} + +// Warn returns a Log with warn level if warn level is enabled. +func Warn(msg string, args ...interface{}) *logit.Log { + return globalLogger.Warn(msg, args) +} + +// Error adds an entry which key is string and value is error type to l. +func Error(err error, msg string, args ...interface{}) *logit.Log { + return globalLogger.Error(msg, args).Error("err", err) +} + +func Close() error { + return globalLogger.Close() +} From ea70b25ea7e64ecf2ad40431e4c0a8c3cd936d42 Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Sat, 29 Jan 2022 23:12:24 +0800 Subject: [PATCH 22/27] =?UTF-8?q?=E6=97=A5=E5=BF=97=E5=92=8C=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/postar/main.go | 16 ++++++++++++++-- go.mod | 2 +- go.sum | 4 ++-- internal/biz/smtp_test.go | 3 +-- pkg/log/log.go | 9 +++++---- pkg/log/log_test.go | 23 +++++++++++++++++++++++ 6 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 pkg/log/log_test.go diff --git a/cmd/postar/main.go b/cmd/postar/main.go index e6a001c..7f08aef 100644 --- a/cmd/postar/main.go +++ b/cmd/postar/main.go @@ -26,6 +26,17 @@ import ( const ( version = "postar-v0.3.0-alpha" + + // You know, for cool. + banner = `.______ ______ _______.___________. ___ .______ +| _ \ / __ \ / | | / \ | _ \ +| |_) | | | | | | (--- '---| |----' / ^ \ | |_) | +| ___/ | | | | \ \ | | / /_\ \ | / +| | | '--' | .----) | | | / _____ \ | |\ \----. +| _| \______/ |_______/ |__| /__/ \__\ | _| '._____| + +Postar is running... +` ) func loadConfig() (*configs.Config, error) { @@ -83,6 +94,7 @@ func main() { pool := initPool(c) defer pool.Release() - smtpBiz := biz.NewSMTPBiz(c, pool) - runServer(c, smtpBiz) + fmt.Print(banner) + log.Info("run server with config").Any("config", c).End() + runServer(c, biz.NewSMTPBiz(c, pool)) } diff --git a/go.mod b/go.mod index 76beee2..3468303 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.15 require ( github.com/FishGoddess/errors v0.0.2 - github.com/FishGoddess/logit v0.4.12-alpha + github.com/FishGoddess/logit v0.4.13-alpha github.com/go-ini/ini v1.66.3 github.com/panjf2000/ants/v2 v2.4.7 google.golang.org/grpc v1.40.0 diff --git a/go.sum b/go.sum index 1f679e3..f45d100 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/FishGoddess/errors v0.0.2 h1:g1S1HDrnjSuMS0sl92lyE+tXsfTn7Dp/lKlOvAPN5DA= github.com/FishGoddess/errors v0.0.2/go.mod h1:2YWw8ijAMQIfs4FyOFjvahUPQWhgS6lFTkBG++n61+Y= -github.com/FishGoddess/logit v0.4.12-alpha h1:dZ+dQbycdubCuyP5SR081Qsm3x9l4A6+x3GrJ0nm+9k= -github.com/FishGoddess/logit v0.4.12-alpha/go.mod h1:a2dFSibwfZXNNZ/V1jMOQBImPoySaxdPFThbsm17ZdY= +github.com/FishGoddess/logit v0.4.13-alpha h1:cbhYGnI+Pasyb2Mat/1XDbB2+mnKDDmz6tJt44jsa8s= +github.com/FishGoddess/logit v0.4.13-alpha/go.mod h1:a2dFSibwfZXNNZ/V1jMOQBImPoySaxdPFThbsm17ZdY= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/internal/biz/smtp_test.go b/internal/biz/smtp_test.go index 6186092..71d3abb 100644 --- a/internal/biz/smtp_test.go +++ b/internal/biz/smtp_test.go @@ -15,7 +15,6 @@ import ( "testing" "time" - "github.com/FishGoddess/logit" "github.com/avinoplan/postar/configs" "github.com/avinoplan/postar/internal/model" "github.com/panjf2000/ants/v2" @@ -46,7 +45,7 @@ func TestSMTPBiz(t *testing.T) { pool, _ := ants.NewPool(c.TaskWorkerNumber()) defer pool.Release() - smtpService := NewSMTPBiz(c, logit.NewLogger(), pool) + smtpService := NewSMTPBiz(c, pool) err := smtpService.SendEmail(context.Background(), &model.Email{ Subject: t.Name(), Receivers: []string{receiver}, diff --git a/pkg/log/log.go b/pkg/log/log.go index cb9f3ec..ae09a62 100644 --- a/pkg/log/log.go +++ b/pkg/log/log.go @@ -21,24 +21,25 @@ func Initialize(c *configs.Config) error { // Debug returns a Log with debug level if debug level is enabled. func Debug(msg string, args ...interface{}) *logit.Log { - return globalLogger.Debug(msg, args) + return globalLogger.Debug(msg, args...) } // Info returns a Log with info level if info level is enabled. func Info(msg string, args ...interface{}) *logit.Log { - return globalLogger.Info(msg, args) + return globalLogger.Info(msg, args...) } // Warn returns a Log with warn level if warn level is enabled. func Warn(msg string, args ...interface{}) *logit.Log { - return globalLogger.Warn(msg, args) + return globalLogger.Warn(msg, args...) } // Error adds an entry which key is string and value is error type to l. func Error(err error, msg string, args ...interface{}) *logit.Log { - return globalLogger.Error(msg, args).Error("err", err) + return globalLogger.Error(msg, args...).Error("err", err).WithCaller() } +// Close closes logger in log. func Close() error { return globalLogger.Close() } diff --git a/pkg/log/log_test.go b/pkg/log/log_test.go new file mode 100644 index 0000000..b32bb6d --- /dev/null +++ b/pkg/log/log_test.go @@ -0,0 +1,23 @@ +package log + +import ( + "github.com/avinoplan/postar/configs" + "testing" +) + +// go test -v -cover -run=^TestInitialize$ +func TestInitialize(t *testing.T) { + if globalLogger != nil { + t.Errorf("globalLogger %+v != nil", globalLogger) + } + + c := configs.NewDefaultConfig() + err := Initialize(c) + if err != nil { + t.Error(err) + } + + if globalLogger == nil { + t.Error("globalLogger == nil") + } +} From 0de07d9f833dfcd1b39077528dfb522683201bd1 Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Sun, 30 Jan 2022 12:04:22 +0800 Subject: [PATCH 23/27] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=A2=9E=E5=8A=A0=20ht?= =?UTF-8?q?tp=20=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/server/grpc.go | 12 +++++----- internal/server/http.go | 50 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/internal/server/grpc.go b/internal/server/grpc.go index c77a526..224c164 100644 --- a/internal/server/grpc.go +++ b/internal/server/grpc.go @@ -23,17 +23,21 @@ import ( // GRPCServer is a grpc implement of PostardServer. type GRPCServer struct { api.UnimplementedPostarServiceServer - server *grpc.Server c *configs.Config smtpBiz *biz.SMTPBiz + server *grpc.Server } // NewGRPCServer returns a new GRPCServer. func NewGRPCServer(c *configs.Config, smtpBiz *biz.SMTPBiz) Server { - return &GRPCServer{ + gs := &GRPCServer{ c: c, smtpBiz: smtpBiz, + server: grpc.NewServer(), } + + api.RegisterPostarServiceServer(gs.server, gs) + return gs } // SendEmail sends emails. @@ -70,9 +74,7 @@ func (gs *GRPCServer) Start() error { if err != nil { return err } - - gs.server = grpc.NewServer() - api.RegisterPostarServiceServer(gs.server, gs) + defer listener.Close() return gs.server.Serve(listener) } diff --git a/internal/server/http.go b/internal/server/http.go index ebe1a1a..cfbfbf6 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -8,4 +8,52 @@ package server -// TODO http server +import ( + "context" + "github.com/avinoplan/postar/configs" + "github.com/avinoplan/postar/internal/biz" + "net" + "net/http" + "time" +) + +type HTTPServer struct { + c *configs.Config + smtpBiz *biz.SMTPBiz + server *http.Server +} + +func NewHTTPServer(c *configs.Config, smtpBiz *biz.SMTPBiz) Server { + hs := &HTTPServer{ + c: c, + smtpBiz: smtpBiz, + } + + hs.server = &http.Server{ + Addr: c.ServerAddress(), + Handler: hs, + } + return hs +} + +func (hs *HTTPServer) ServeHTTP(writer http.ResponseWriter, request *http.Request) { + writer.WriteHeader(http.StatusOK) + writer.Write([]byte("Hi, I am postar!")) +} + +// Start starts HTTPServer. +func (hs *HTTPServer) Start() error { + listener, err := net.Listen("tcp", hs.c.ServerAddress()) + if err != nil { + return err + } + defer listener.Close() + return hs.server.Serve(listener) +} + +// Stop stops HTTPServer gracefully. +func (hs *HTTPServer) Stop() error { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + return hs.server.Shutdown(ctx) +} From 01d2465381286110409c740ef68668489b7ec8f3 Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Sun, 30 Jan 2022 13:21:15 +0800 Subject: [PATCH 24/27] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=A2=9E=E5=8A=A0=20ht?= =?UTF-8?q?tp=20=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _examples/config/postar.ini | 3 +++ cmd/postar/main.go | 33 ++++++++++++++++++++++++++------- configs/config.go | 14 ++++++++++---- go.mod | 1 + go.sum | 6 ++++++ internal/server/grpc.go | 23 +++++++++++++++++++---- internal/server/http.go | 10 +++++++--- internal/server/server.go | 1 + pkg/log/log.go | 7 ++++++- 9 files changed, 79 insertions(+), 19 deletions(-) diff --git a/_examples/config/postar.ini b/_examples/config/postar.ini index 6a07dca..5b26675 100644 --- a/_examples/config/postar.ini +++ b/_examples/config/postar.ini @@ -19,6 +19,9 @@ timeout = 10000 type = "http" # You can bind a specific ip and port for your server. address = ":5897" +# The closing timeout in second of server. +# Default is 30s. +stop_timeout = 30 [smtp] # Specify your smtp server host. host = "" diff --git a/cmd/postar/main.go b/cmd/postar/main.go index 7f08aef..d69e43e 100644 --- a/cmd/postar/main.go +++ b/cmd/postar/main.go @@ -12,6 +12,7 @@ import ( "flag" "fmt" "github.com/avinoplan/postar/pkg/log" + "go.uber.org/automaxprocs/maxprocs" "os" "os/signal" "runtime" @@ -27,7 +28,7 @@ import ( const ( version = "postar-v0.3.0-alpha" - // You know, for cool. + // You know, for funny. banner = `.______ ______ _______.___________. ___ .______ | _ \ / __ \ / | | / \ | _ \ | |_) | | | | | | (--- '---| |----' / ^ \ | |_) | @@ -39,6 +40,10 @@ Postar is running... ` ) +func funnyFunnyChickenHomie() { + fmt.Print(banner) +} + func loadConfig() (*configs.Config, error) { configFile := flag.String("config.file", "postar.ini", "The configuration file of postar.") showVersion := flag.Bool("version", false, "Check version of postar.") @@ -62,9 +67,13 @@ func initPool(c *configs.Config) *ants.Pool { return pool } -func runServer(c *configs.Config, smtpBiz *biz.SMTPBiz) { - svr := server.NewServer(c, smtpBiz) +func runServer(c *configs.Config, smtpBiz *biz.SMTPBiz) error { + cc := *c + cc.SMTP.User = "*" + cc.SMTP.Password = "*" + log.Info("run server with config").Any("config", cc).End() + svr := server.NewServer(c, smtpBiz) go func() { err := svr.Start() if err != nil { @@ -75,11 +84,14 @@ func runServer(c *configs.Config, smtpBiz *biz.SMTPBiz) { signalCh := make(chan os.Signal, 1) signal.Notify(signalCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM) <-signalCh + fmt.Println("Postar is shutdown gracefully...") - svr.Stop() + return svr.Stop() } func main() { + funnyFunnyChickenHomie() + c, err := loadConfig() if err != nil { panic(err) @@ -91,10 +103,17 @@ func main() { } defer log.Close() + undo, err := maxprocs.Set(maxprocs.Logger(log.Printf)) + if err != nil { + undo() + log.Error(err, "set maxprocs failed").End() + } + pool := initPool(c) defer pool.Release() - fmt.Print(banner) - log.Info("run server with config").Any("config", c).End() - runServer(c, biz.NewSMTPBiz(c, pool)) + err = runServer(c, biz.NewSMTPBiz(c, pool)) + if err != nil { + log.Error(err, "stop server failed").End() + } } diff --git a/configs/config.go b/configs/config.go index 5c10be4..09487ed 100644 --- a/configs/config.go +++ b/configs/config.go @@ -18,8 +18,9 @@ type TaskConfig struct { } type ServerConfig struct { - Type string `ini:"type"` // The type of server. - Address string `ini:"address"` // The address(including ip and port) of server. + Type string `ini:"type"` // The type of server. + Address string `ini:"address"` // The address(including ip and port) of server. + StopTimeout int `ini:"stop_timeout"` // The closing timeout in second of server. } type SMTPConfig struct { @@ -46,8 +47,9 @@ func NewDefaultConfig() *Config { Timeout: 10000, // 10s }, Server: ServerConfig{ - Type: "http", - Address: ":5897", + Type: "http", + Address: ":5897", + StopTimeout: 30, // 30s }, SMTP: SMTPConfig{ Port: 587, @@ -79,6 +81,10 @@ func (c *Config) ServerAddress() string { return c.Server.Address } +func (c *Config) ServerStopTimeout() time.Duration { + return time.Duration(c.Server.StopTimeout) * time.Second +} + func (c *Config) SMTPHost() string { return c.SMTP.Host } diff --git a/go.mod b/go.mod index 3468303..1c01bb4 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/FishGoddess/logit v0.4.13-alpha github.com/go-ini/ini v1.66.3 github.com/panjf2000/ants/v2 v2.4.7 + go.uber.org/automaxprocs v1.4.0 google.golang.org/grpc v1.40.0 google.golang.org/protobuf v1.27.1 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect diff --git a/go.sum b/go.sum index f45d100..310dbef 100644 --- a/go.sum +++ b/go.sum @@ -49,6 +49,9 @@ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/panjf2000/ants/v2 v2.4.7 h1:MZnw2JRyTJxFwtaMtUJcwE618wKD04POWk2gwwP4E2M= github.com/panjf2000/ants/v2 v2.4.7/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -61,6 +64,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/automaxprocs v1.4.0 h1:CpDZl6aOlLhReez+8S3eEotD7Jx0Os++lemPlMULQP0= +go.uber.org/automaxprocs v1.4.0/go.mod h1:/mTEdr7LvHhs0v7mjdxDreTz1OG5zdZGqgOnhWiR/+Q= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -126,6 +131,7 @@ gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gG gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= 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/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/server/grpc.go b/internal/server/grpc.go index 224c164..fc0f060 100644 --- a/internal/server/grpc.go +++ b/internal/server/grpc.go @@ -10,14 +10,14 @@ package server import ( "context" - "net" - "github.com/avinoplan/postar/api" "github.com/avinoplan/postar/configs" "github.com/avinoplan/postar/internal/biz" "github.com/avinoplan/postar/pkg/errors" "github.com/avinoplan/postar/pkg/trace" "google.golang.org/grpc" + "net" + "time" ) // GRPCServer is a grpc implement of PostardServer. @@ -80,6 +80,21 @@ func (gs *GRPCServer) Start() error { // Stop stops GRPCServer gracefully. func (gs *GRPCServer) Stop() error { - gs.server.GracefulStop() - return nil + stopCh := make(chan struct{}, 1) + + go func() { + gs.server.GracefulStop() + time.Sleep(time.Minute) + stopCh <- struct{}{} + }() + + ctx, cancel := context.WithTimeout(context.Background(), gs.c.ServerStopTimeout()) + defer cancel() + + select { + case <-ctx.Done(): + return ctx.Err() + case <-stopCh: + return nil + } } diff --git a/internal/server/http.go b/internal/server/http.go index cfbfbf6..97305ae 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -14,7 +14,6 @@ import ( "github.com/avinoplan/postar/internal/biz" "net" "net/http" - "time" ) type HTTPServer struct { @@ -48,12 +47,17 @@ func (hs *HTTPServer) Start() error { return err } defer listener.Close() - return hs.server.Serve(listener) + + err = hs.server.Serve(listener) + if err != nil && err != http.ErrServerClosed { + return err + } + return nil } // Stop stops HTTPServer gracefully. func (hs *HTTPServer) Stop() error { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), hs.c.ServerStopTimeout()) defer cancel() return hs.server.Shutdown(ctx) } diff --git a/internal/server/server.go b/internal/server/server.go index 84b0192..bd51bc7 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -20,6 +20,7 @@ import ( var ( servers = map[string]func(c *configs.Config, smtpBiz *biz.SMTPBiz) Server{ + "http": NewHTTPServer, "grpc": NewGRPCServer, } ) diff --git a/pkg/log/log.go b/pkg/log/log.go index ae09a62..7a26f4e 100644 --- a/pkg/log/log.go +++ b/pkg/log/log.go @@ -36,7 +36,12 @@ func Warn(msg string, args ...interface{}) *logit.Log { // Error adds an entry which key is string and value is error type to l. func Error(err error, msg string, args ...interface{}) *logit.Log { - return globalLogger.Error(msg, args...).Error("err", err).WithCaller() + return globalLogger.Error(msg, args...).Error("err", err) +} + +// Printf prints a log as info level. +func Printf(msg string, args ...interface{}) { + globalLogger.Info(msg, args...).End() } // Close closes logger in log. From 7048643d61c9b24152429d1ff673020e20ddcd2c Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Sun, 30 Jan 2022 14:33:00 +0800 Subject: [PATCH 25/27] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=AE=8C=E6=88=90=20ht?= =?UTF-8?q?tp=20=E6=9C=8D=E5=8A=A1=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _examples/basic.http | 25 -------- _examples/http-client.env.json | 8 --- api/api.pb.go | 26 +++++---- api/api.proto | 3 +- go.mod | 1 + go.sum | 2 + internal/biz/smtp.go | 13 +++-- internal/server/http.go | 103 ++++++++++++++++++++++++++++++--- pkg/errors/errors.go | 11 ++++ pkg/errors/errors_test.go | 17 ++++++ 10 files changed, 153 insertions(+), 56 deletions(-) delete mode 100644 _examples/basic.http delete mode 100644 _examples/http-client.env.json diff --git a/_examples/basic.http b/_examples/basic.http deleted file mode 100644 index ba79dd0..0000000 --- a/_examples/basic.http +++ /dev/null @@ -1,25 +0,0 @@ -# 获取服务信息 -GET http://{{address}}/ -Content-Type: application/json - -### - -# 发送邮件 -PUT http://{{address}}/send -Content-Type: application/json - -{ - "email": { - "to": [ - "1149062639@qq.com" - ], - "subject": "高考加分党费的会计师!!!", - "content": "高科技适当卡尔是那么快乐????" - }, - "options": { - "async": false, - "timeout": 3000 - } -} - -### \ No newline at end of file diff --git a/_examples/http-client.env.json b/_examples/http-client.env.json deleted file mode 100644 index bd2c846..0000000 --- a/_examples/http-client.env.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "local": { - "address": "127.0.0.1:5897" - }, - "ubuntu-vm": { - "address": "192.168.32.128:5897" - } -} \ No newline at end of file diff --git a/api/api.pb.go b/api/api.pb.go index 9de5b13..f3af165 100644 --- a/api/api.pb.go +++ b/api/api.pb.go @@ -9,7 +9,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.18.0 +// protoc v3.17.3 // source: api.proto package api @@ -33,7 +33,8 @@ type ServerCode int32 const ( ServerCode_OK ServerCode = 0 - ServerCode_TIMEOUT ServerCode = 429 + ServerCode_BAD_REQUEST ServerCode = 400 + ServerCode_TIMEOUT ServerCode = 408 ServerCode_SEND_EMAIL_FAILED ServerCode = 11000 ) @@ -41,12 +42,14 @@ const ( var ( ServerCode_name = map[int32]string{ 0: "OK", - 429: "TIMEOUT", + 400: "BAD_REQUEST", + 408: "TIMEOUT", 11000: "SEND_EMAIL_FAILED", } ServerCode_value = map[string]int32{ "OK": 0, - "TIMEOUT": 429, + "BAD_REQUEST": 400, + "TIMEOUT": 408, "SEND_EMAIL_FAILED": 11000, } ) @@ -83,14 +86,15 @@ var File_api_proto protoreflect.FileDescriptor var file_api_proto_rawDesc = []byte{ 0x0a, 0x09, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, - 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2a, 0x3a, 0x0a, 0x0a, + 0x6e, 0x2e, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2a, 0x4c, 0x0a, 0x0a, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, - 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0xad, 0x03, - 0x12, 0x16, 0x0a, 0x11, 0x53, 0x45, 0x4e, 0x44, 0x5f, 0x45, 0x4d, 0x41, 0x49, 0x4c, 0x5f, 0x46, - 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0xf8, 0x55, 0x42, 0x21, 0x5a, 0x1f, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x61, 0x6e, - 0x2f, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0b, 0x42, 0x41, 0x44, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, + 0x54, 0x10, 0x90, 0x03, 0x12, 0x0c, 0x0a, 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, + 0x98, 0x03, 0x12, 0x16, 0x0a, 0x11, 0x53, 0x45, 0x4e, 0x44, 0x5f, 0x45, 0x4d, 0x41, 0x49, 0x4c, + 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0xf8, 0x55, 0x42, 0x21, 0x5a, 0x1f, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x69, 0x6e, 0x6f, 0x70, 0x6c, + 0x61, 0x6e, 0x2f, 0x70, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/api/api.proto b/api/api.proto index be567c5..40ed615 100644 --- a/api/api.proto +++ b/api/api.proto @@ -15,6 +15,7 @@ option go_package = "github.com/avinoplan/postar/api"; // ResponseCodes is all codes of response. enum ServerCode { OK = 0; - TIMEOUT = 429; + BAD_REQUEST = 400; + TIMEOUT = 408; SEND_EMAIL_FAILED = 11000; } diff --git a/go.mod b/go.mod index 1c01bb4..45e1ace 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/FishGoddess/errors v0.0.2 github.com/FishGoddess/logit v0.4.13-alpha github.com/go-ini/ini v1.66.3 + github.com/julienschmidt/httprouter v1.3.0 github.com/panjf2000/ants/v2 v2.4.7 go.uber.org/automaxprocs v1.4.0 google.golang.org/grpc v1.40.0 diff --git a/go.sum b/go.sum index 310dbef..6e00820 100644 --- a/go.sum +++ b/go.sum @@ -49,6 +49,8 @@ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= diff --git a/internal/biz/smtp.go b/internal/biz/smtp.go index e144eef..e8ffd83 100644 --- a/internal/biz/smtp.go +++ b/internal/biz/smtp.go @@ -12,6 +12,7 @@ import ( "context" "github.com/avinoplan/postar/pkg/log" + liberrors "github.com/FishGoddess/errors" "github.com/avinoplan/postar/configs" "github.com/avinoplan/postar/internal/model" "github.com/avinoplan/postar/pkg/errors" @@ -21,15 +22,15 @@ import ( // SMTPBiz is the biz of smtp. type SMTPBiz struct { - c *configs.Config - pool *ants.Pool // The pool of workers. + c *configs.Config + pool *ants.Pool // The pool of workers. } // NewSMTPBiz returns a new SMTPBiz. func NewSMTPBiz(c *configs.Config, pool *ants.Pool) *SMTPBiz { return &SMTPBiz{ - c: c, - pool: pool, + c: c, + pool: pool, } } @@ -45,6 +46,10 @@ func (sb *SMTPBiz) sendEmail(email *model.Email) error { // SendEmail sends email to somewhere. func (sb *SMTPBiz) SendEmail(ctx context.Context, email *model.Email, options *model.SendEmailOptions) error { + if email == nil { + return errors.BadRequestErr(liberrors.New("email == nil")) + } + if options == nil { options = model.DefaultSendEmailOptions(sb.c) log.Debug("options is nil, using default options").Any("options", options).End() diff --git a/internal/server/http.go b/internal/server/http.go index 97305ae..7fe07eb 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -10,8 +10,17 @@ package server import ( "context" + liberrors "github.com/FishGoddess/errors" + "github.com/avinoplan/postar/api" "github.com/avinoplan/postar/configs" "github.com/avinoplan/postar/internal/biz" + "github.com/avinoplan/postar/pkg/errors" + "github.com/avinoplan/postar/pkg/log" + "github.com/avinoplan/postar/pkg/trace" + "github.com/julienschmidt/httprouter" + "google.golang.org/protobuf/proto" + "io" + "io/ioutil" "net" "net/http" ) @@ -26,18 +35,98 @@ func NewHTTPServer(c *configs.Config, smtpBiz *biz.SMTPBiz) Server { hs := &HTTPServer{ c: c, smtpBiz: smtpBiz, + server: &http.Server{ + Addr: c.ServerAddress(), + }, } - hs.server = &http.Server{ - Addr: c.ServerAddress(), - Handler: hs, - } + router := httprouter.New() + router.POST("/sendEmail", hs.sendEmail) + hs.server.Handler = router return hs } -func (hs *HTTPServer) ServeHTTP(writer http.ResponseWriter, request *http.Request) { - writer.WriteHeader(http.StatusOK) - writer.Write([]byte("Hi, I am postar!")) +func (hs *HTTPServer) unmarshalSendEmailRequest(reader io.Reader) (*api.SendEmailRequest, error) { + marshaled, err := ioutil.ReadAll(reader) + if err != nil { + return nil, err + } + + request := new(api.SendEmailRequest) + err = proto.Unmarshal(marshaled, request) + if err != nil { + return nil, err + } + + if request.Email == nil { + return nil, errors.BadRequestErr(liberrors.New("request.Email == nil")) + } + return request, nil +} + +func (hs *HTTPServer) marshalSendEmailResponse(response *api.SendEmailResponse) []byte { + marshaled, err := proto.Marshal(response) + if err != nil { + log.Error(err, "proto.Marshal(response) failed").Stringer("response", response).End() + return nil // should never happen... + } + return marshaled +} + +func (hs *HTTPServer) writeSendEmailResponse(writer http.ResponseWriter, statusCode int, response *api.SendEmailResponse) { + writer.WriteHeader(statusCode) + _, err := writer.Write(hs.marshalSendEmailResponse(response)) + if err != nil { + log.Error(err, "write send email response failed").Int("statusCode", statusCode).Stringer("response", response).End() + } +} + +func (hs *HTTPServer) sendEmail(writer http.ResponseWriter, request *http.Request, params httprouter.Params) { + traceID := trace.NewTraceID() + ctx := trace.NewContext(request.Context(), traceID) + + req, err := hs.unmarshalSendEmailRequest(request.Body) + if errors.IsBadRequest(err) { + hs.writeSendEmailResponse(writer, http.StatusBadRequest, &api.SendEmailResponse{ + Code: api.ServerCode_BAD_REQUEST, + Msg: err.Error(), + TraceId: traceID, + }) + return + } + + if err != nil { + hs.writeSendEmailResponse(writer, http.StatusBadRequest, &api.SendEmailResponse{ + Code: api.ServerCode_BAD_REQUEST, + Msg: "unmarshal send email request failed", + TraceId: traceID, + }) + return + } + + err = hs.smtpBiz.SendEmail(ctx, toModelEmail(req.Email), toModelSendEmailOptions(hs.c, req.Options)) + if errors.IsTimeout(err) { + hs.writeSendEmailResponse(writer, http.StatusRequestTimeout, &api.SendEmailResponse{ + Code: api.ServerCode_TIMEOUT, + Msg: "send email timeout", + TraceId: traceID, + }) + return + } + + if err != nil { + hs.writeSendEmailResponse(writer, http.StatusInternalServerError, &api.SendEmailResponse{ + Code: api.ServerCode_SEND_EMAIL_FAILED, + Msg: "send email failed", + TraceId: traceID, + }) + return + } + + hs.writeSendEmailResponse(writer, http.StatusOK, &api.SendEmailResponse{ + Code: api.ServerCode_OK, + TraceId: traceID, + }) } // Start starts HTTPServer. diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go index 6cb9a55..c2ed7d5 100644 --- a/pkg/errors/errors.go +++ b/pkg/errors/errors.go @@ -11,6 +11,7 @@ package errors import "github.com/FishGoddess/errors" const ( + codeBadRequest = 400 codeSendEmailFailed = 11000 ) @@ -24,6 +25,16 @@ func IsTimeout(err error) bool { return errors.IsTimeout(err) } +// BadRequestErr returns a bad request error. +func BadRequestErr(err error) error { + return errors.Wrap(err, codeBadRequest) +} + +// IsBadRequest returns if err is bad request. +func IsBadRequest(err error) bool { + return errors.Is(err, codeBadRequest) +} + // SendEmailFailedErr returns a send email failed error. func SendEmailFailedErr(err error) error { return errors.Wrap(err, codeSendEmailFailed) diff --git a/pkg/errors/errors_test.go b/pkg/errors/errors_test.go index a46ec1f..18b91e7 100644 --- a/pkg/errors/errors_test.go +++ b/pkg/errors/errors_test.go @@ -13,6 +13,23 @@ import ( "testing" ) +// go test -v -cover -run=^TestIsBadRequest$ +func TestIsBadRequest(t *testing.T) { + testCases := []struct { + err error + result bool + }{ + {BadRequestErr(errors.New("bad request")), true}, + {errors.New("unknown error"), false}, + } + + for i, testCase := range testCases { + if IsBadRequest(testCase.err) != testCase.result { + t.Errorf("testCase %d failed with err %+v, result %+v", i, testCase.err, testCase.result) + } + } +} + // go test -v -cover -run=^TestIsSendEmailFailed$ func TestIsSendEmailFailed(t *testing.T) { testCases := []struct { From 8af9dcf783f72a2e4d557640fceb78ee51a601fb Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Sun, 30 Jan 2022 14:59:50 +0800 Subject: [PATCH 26/27] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _examples/grpc.go | 35 ++++++++++++++++++++++++++++ _examples/http.go | 49 +++++++++++++++++++++++++++++++++++++++ internal/biz/smtp.go | 17 +++++++++----- internal/server/grpc.go | 10 ++++++-- internal/server/http.go | 15 ++++-------- pkg/errors/errors.go | 8 +++---- pkg/errors/errors_test.go | 2 +- 7 files changed, 113 insertions(+), 23 deletions(-) create mode 100644 _examples/grpc.go create mode 100644 _examples/http.go diff --git a/_examples/grpc.go b/_examples/grpc.go new file mode 100644 index 0000000..84a68cd --- /dev/null +++ b/_examples/grpc.go @@ -0,0 +1,35 @@ +package main + +import ( + "context" + "fmt" + "github.com/avinoplan/postar/api" + "google.golang.org/grpc" +) + +func main() { + conn, err := grpc.Dial("127.0.0.1:5897", grpc.WithInsecure()) + if err != nil { + panic(err) + } + defer conn.Close() + + req := &api.SendEmailRequest{ + Email: &api.Email{ + Receivers: nil, + Subject: "测试邮件", + BodyType: "text/html", + Body: "

邮件内容

", + }, + Options: nil, + } + fmt.Printf("client req: %+v\n", req) + + client := api.NewPostarServiceClient(conn) + rsp, err := client.SendEmail(context.Background(), req) + if err != nil { + panic(err) + } + + fmt.Printf("server rsp: %+v\n", rsp) +} diff --git a/_examples/http.go b/_examples/http.go new file mode 100644 index 0000000..8443c5d --- /dev/null +++ b/_examples/http.go @@ -0,0 +1,49 @@ +package main + +import ( + "bytes" + "fmt" + "github.com/avinoplan/postar/api" + "google.golang.org/protobuf/proto" + "io/ioutil" + "net/http" +) + +func main() { + url := "http://127.0.0.1:5897/sendEmail" + + emailReq := &api.SendEmailRequest{ + Email: &api.Email{ + Receivers: nil, + Subject: "测试邮件", + BodyType: "text/html", + Body: "

邮件内容

", + }, + Options: nil, + } + fmt.Printf("client req: %+v\n", emailReq) + + marshaled, err := proto.Marshal(emailReq) + if err != nil { + panic(err) + } + + resp, err := http.Post(url, "application/octet-stream", bytes.NewReader(marshaled)) + if err != nil { + panic(err) + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + panic(err) + } + + emailRsp := new(api.SendEmailResponse) + err = proto.Unmarshal(body, emailRsp) + if err != nil { + panic(err) + } + + fmt.Printf("server rsp: %+v\n", emailRsp) +} diff --git a/internal/biz/smtp.go b/internal/biz/smtp.go index e8ffd83..d81b1ff 100644 --- a/internal/biz/smtp.go +++ b/internal/biz/smtp.go @@ -11,6 +11,7 @@ package biz import ( "context" "github.com/avinoplan/postar/pkg/log" + "github.com/avinoplan/postar/pkg/trace" liberrors "github.com/FishGoddess/errors" "github.com/avinoplan/postar/configs" @@ -46,13 +47,17 @@ func (sb *SMTPBiz) sendEmail(email *model.Email) error { // SendEmail sends email to somewhere. func (sb *SMTPBiz) SendEmail(ctx context.Context, email *model.Email, options *model.SendEmailOptions) error { + traceID := trace.FromContext(ctx) + if email == nil { - return errors.BadRequestErr(liberrors.New("email == nil")) + err := liberrors.New("email is nil") + log.Error(err, "email is nil").String("traceID", traceID).End() + return errors.BadRequest(err) } if options == nil { options = model.DefaultSendEmailOptions(sb.c) - log.Debug("options is nil, using default options").Any("options", options).End() + log.Debug("options is nil, using default options").String("traceID", traceID).Any("options", options).End() } ctx, cancel := context.WithTimeout(ctx, options.Timeout) @@ -65,7 +70,7 @@ func (sb *SMTPBiz) SendEmail(ctx context.Context, email *model.Email, options *m }) if err != nil { - log.Error(err, "submit email sending task to pool failed").End() + log.Error(err, "submit email sending task to pool failed").String("traceID", traceID).Any("email", email).End() return errors.SendEmailFailedErr(err) } @@ -76,14 +81,14 @@ func (sb *SMTPBiz) SendEmail(ctx context.Context, email *model.Email, options *m select { case err = <-errorCh: if err != nil { - log.Error(err, "send email failed").End() + log.Error(err, "send email failed").String("traceID", traceID).Any("email", email).End() return errors.SendEmailFailedErr(err) } case <-ctx.Done(): err = ctx.Err() if err != nil { - log.Error(err, "send email timeout").End() - return errors.TimeoutErr(err) + log.Error(err, "send email timeout").String("traceID", traceID).Any("email", email).End() + return errors.Timeout(err) } } diff --git a/internal/server/grpc.go b/internal/server/grpc.go index fc0f060..577f5fb 100644 --- a/internal/server/grpc.go +++ b/internal/server/grpc.go @@ -17,7 +17,6 @@ import ( "github.com/avinoplan/postar/pkg/trace" "google.golang.org/grpc" "net" - "time" ) // GRPCServer is a grpc implement of PostardServer. @@ -46,6 +45,14 @@ func (gs *GRPCServer) SendEmail(ctx context.Context, request *api.SendEmailReque ctx = trace.NewContext(ctx, traceID) err := gs.smtpBiz.SendEmail(ctx, toModelEmail(request.Email), toModelSendEmailOptions(gs.c, request.Options)) + if errors.IsBadRequest(err) { + return &api.SendEmailResponse{ + Code: api.ServerCode_BAD_REQUEST, + Msg: err.Error(), + TraceId: traceID, + }, nil + } + if errors.IsTimeout(err) { return &api.SendEmailResponse{ Code: api.ServerCode_TIMEOUT, @@ -84,7 +91,6 @@ func (gs *GRPCServer) Stop() error { go func() { gs.server.GracefulStop() - time.Sleep(time.Minute) stopCh <- struct{}{} }() diff --git a/internal/server/http.go b/internal/server/http.go index 7fe07eb..8bff95b 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -10,7 +10,6 @@ package server import ( "context" - liberrors "github.com/FishGoddess/errors" "github.com/avinoplan/postar/api" "github.com/avinoplan/postar/configs" "github.com/avinoplan/postar/internal/biz" @@ -57,10 +56,6 @@ func (hs *HTTPServer) unmarshalSendEmailRequest(reader io.Reader) (*api.SendEmai if err != nil { return nil, err } - - if request.Email == nil { - return nil, errors.BadRequestErr(liberrors.New("request.Email == nil")) - } return request, nil } @@ -86,25 +81,25 @@ func (hs *HTTPServer) sendEmail(writer http.ResponseWriter, request *http.Reques ctx := trace.NewContext(request.Context(), traceID) req, err := hs.unmarshalSendEmailRequest(request.Body) - if errors.IsBadRequest(err) { + if err != nil { hs.writeSendEmailResponse(writer, http.StatusBadRequest, &api.SendEmailResponse{ Code: api.ServerCode_BAD_REQUEST, - Msg: err.Error(), + Msg: "unmarshal send email request failed", TraceId: traceID, }) return } - if err != nil { + err = hs.smtpBiz.SendEmail(ctx, toModelEmail(req.Email), toModelSendEmailOptions(hs.c, req.Options)) + if errors.IsBadRequest(err) { hs.writeSendEmailResponse(writer, http.StatusBadRequest, &api.SendEmailResponse{ Code: api.ServerCode_BAD_REQUEST, - Msg: "unmarshal send email request failed", + Msg: err.Error(), TraceId: traceID, }) return } - err = hs.smtpBiz.SendEmail(ctx, toModelEmail(req.Email), toModelSendEmailOptions(hs.c, req.Options)) if errors.IsTimeout(err) { hs.writeSendEmailResponse(writer, http.StatusRequestTimeout, &api.SendEmailResponse{ Code: api.ServerCode_TIMEOUT, diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go index c2ed7d5..9e71cd6 100644 --- a/pkg/errors/errors.go +++ b/pkg/errors/errors.go @@ -15,8 +15,8 @@ const ( codeSendEmailFailed = 11000 ) -// TimeoutErr returns a timeout error. -func TimeoutErr(err error) error { +// Timeout returns a timeout error. +func Timeout(err error) error { return errors.Timeout(err) } @@ -25,8 +25,8 @@ func IsTimeout(err error) bool { return errors.IsTimeout(err) } -// BadRequestErr returns a bad request error. -func BadRequestErr(err error) error { +// BadRequest returns a bad request error. +func BadRequest(err error) error { return errors.Wrap(err, codeBadRequest) } diff --git a/pkg/errors/errors_test.go b/pkg/errors/errors_test.go index 18b91e7..44ccdd1 100644 --- a/pkg/errors/errors_test.go +++ b/pkg/errors/errors_test.go @@ -19,7 +19,7 @@ func TestIsBadRequest(t *testing.T) { err error result bool }{ - {BadRequestErr(errors.New("bad request")), true}, + {BadRequest(errors.New("bad request")), true}, {errors.New("unknown error"), false}, } From d234cafe5c99f6dd71835d79df3bdf39974f2e62 Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Sun, 30 Jan 2022 19:46:30 +0800 Subject: [PATCH 27/27] =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FUTURE.md | 9 +++++---- HISTORY.md | 6 ++++++ README.en.md | 4 ++++ README.md | 6 +++++- _examples/grpc.go | 3 ++- _examples/http.go | 3 ++- 6 files changed, 24 insertions(+), 7 deletions(-) diff --git a/FUTURE.md b/FUTURE.md index 3614cc7..d64dd7e 100644 --- a/FUTURE.md +++ b/FUTURE.md @@ -5,15 +5,16 @@ * [ ] dialer 对象池或连接池优化 (P0) * [ ] 增加 net/rpc 远程调用接口 (P1) -### v0.2.x +### v0.3.x * [ ] 支持邮件中携带附件 (P0) -* [ ] 接入监控平台 (P0) -* [ ] 增加 http 远程调用接口 (P0) +* [ ] 接入监控平台 (P1) +* [ ] TLS 证书支持 (P1) +* [x] 增加 http 远程调用接口 (P0) * [x] 增加 grpc 远程调用接口 (P0) * [ ] 增加 vex 远程调用接口 (P1) * [ ] 增加 udp 远程调用接口 (P2) -### v0.1.x +### v0.1.x - v0.2.x * [x] 邮件发送功能 diff --git a/HISTORY.md b/HISTORY.md index c4d8b27..3c1d0df 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,11 @@ ## ✒ 历史版本的特性介绍 (Features in old versions) +### v0.3.0-alpha + +> 此版本发布于 2022-01-30 + +* 全新重构版本!!! + ### v0.2.3-alpha > 此版本发布于 2021-09-03 diff --git a/README.en.md b/README.en.md index 7c71d72..edc0d05 100644 --- a/README.en.md +++ b/README.en.md @@ -47,3 +47,7 @@ If you find that something is not working as expected please open an _**issue**_ |---------|-------------|-------------------------------------------------------|-------------------------------------------------------------------------------------------------| | logit | FishGoddess | A high-performance and easy-to-use logging foundation | [Gitee](https://gitee.com/FishGoddess/logit) / [GitHub](https://github.com/FishGoddess/logit) | | errors | FishGoddess | A lib for handling error gracefully in Go | [Gitee](https://gitee.com/FishGoddess/errors) / [GitHub](https://github.com/FishGoddess/errors) | +| ants | panjf2000 | A high-performance and low-cost goroutine pool | [GitHub](https://github.com/panjf2000/ants) | +| gomail | alexcesaro | The best way to send emails in Go | [GitHub](https://github.com/go-gomail/gomail/tree/v2) | +| ini | unknwon | Provides INI file read and write functionality in Go | [GitHub](https://github.com/go-ini/ini) | +| httprouter | julienschmidt | A high performance HTTP request router | [GitHub](https://github.com/julienschmidt/httprouter) | diff --git a/README.md b/README.md index 945baa1..3046dcf 100644 --- a/README.md +++ b/README.md @@ -41,9 +41,13 @@ _注意:默认的配置文件路径是 `./postar.ini`,默认的日志输出 如果您觉得 **postar** 缺少您需要的功能,请不要犹豫,马上参与进来,发起一个 _**issue**_。 -### 📦 postar 使用的技术 +### 📦 Postar 使用的技术 | 项目 | 作者 | 描述 | 链接 | |--------|-------------|---------------------|----------------------------------------------------------------------------------------------| | logit | FishGoddess | 一个高性能、功能强大且极易上手的日志库 | [码云](https://gitee.com/FishGoddess/logit) / [GitHub](https://github.com/FishGoddess/logit) | | errors | FishGoddess | 一个用于优雅地处理 Go 中错误的库 | [码云](https://gitee.com/FishGoddess/errors) / [GitHub](https://github.com/FishGoddess/errors) | +| ants | panjf2000 | 一个高性能且低损耗的 goroutine 池 | [GitHub](https://github.com/panjf2000/ants) | +| gomail | alexcesaro | 一个用于在 Go 中发送邮件的库 | [GitHub](https://github.com/go-gomail/gomail/tree/v2) | +| ini | unknwon | 一个操作 ini 配置的库 | [GitHub](https://github.com/go-ini/ini) | +| httprouter | julienschmidt | 一个高性能的 http 路由库 | [GitHub](https://github.com/julienschmidt/httprouter) | diff --git a/_examples/grpc.go b/_examples/grpc.go index 84a68cd..d0e03ca 100644 --- a/_examples/grpc.go +++ b/_examples/grpc.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/avinoplan/postar/api" "google.golang.org/grpc" + "os" ) func main() { @@ -16,7 +17,7 @@ func main() { req := &api.SendEmailRequest{ Email: &api.Email{ - Receivers: nil, + Receivers: []string{os.Getenv("POSTAR_RECEIVER")}, Subject: "测试邮件", BodyType: "text/html", Body: "

邮件内容

", diff --git a/_examples/http.go b/_examples/http.go index 8443c5d..2038ef2 100644 --- a/_examples/http.go +++ b/_examples/http.go @@ -7,6 +7,7 @@ import ( "google.golang.org/protobuf/proto" "io/ioutil" "net/http" + "os" ) func main() { @@ -14,7 +15,7 @@ func main() { emailReq := &api.SendEmailRequest{ Email: &api.Email{ - Receivers: nil, + Receivers: []string{os.Getenv("POSTAR_RECEIVER")}, Subject: "测试邮件", BodyType: "text/html", Body: "

邮件内容

",