improved sendmail + docs
This commit is contained in:
parent
9755f2af55
commit
14d5acf5e8
2
Makefile
2
Makefile
|
@ -19,7 +19,7 @@ all: test build
|
|||
deploy:
|
||||
which gcloud
|
||||
gcloud components install app-engine-go
|
||||
gcloud app deploy
|
||||
gcloud app deploy --quiet
|
||||
|
||||
build:
|
||||
$(GOBUILD) -o $(PACKAGE_NAME) -v
|
||||
|
|
83
README.md
83
README.md
|
@ -3,24 +3,85 @@
|
|||
|
||||
# Configuration variables
|
||||
|
||||
* Create the App Engine configuration file
|
||||
```
|
||||
cp app.yaml.sample app.yaml
|
||||
```
|
||||
* Edit the `app.yaml` file with your favorite editor and set the following environement variables appropriately:
|
||||
|
||||
```
|
||||
SERVER_ADDR=":8393"
|
||||
SMTP_SERVER_ADDR="mail.example.com:465"
|
||||
SMTP_CLIENT_USERNAME="demo@example.com"
|
||||
SMTP_CLIENT_PASSWORD="dem0ToBeChanged"
|
||||
CONTACT_REPLY_EMAIL="noreply@example.com"
|
||||
CONTACT_REPLY_CC_EMAIL="contact@example.com"
|
||||
DEMO_URL="https://demo.example.com/"
|
||||
ALLOWED_ORIGIN_DOMAIN="localhost:1313"
|
||||
TEMPLATE_DEMO_REQUEST_REPLY=templates/template_demo_request_reply.html
|
||||
TEMPLATE_CONTACT_REQUEST_REPLY=templates/template_contact_request_reply.html
|
||||
SMTP_SERVER_ADDR: "smtp.mailgun.org:587"
|
||||
SMTP_VERITY_CERT: true
|
||||
SMTP_CLIENT_USERNAME: "postmaster@example.com"
|
||||
SMTP_CLIENT_PASSWORD: "postmasterSecretPassWord"
|
||||
CONTACT_REPLY_EMAIL: "noreply@example.com"
|
||||
CONTACT_REPLY_CC_EMAIL: "contact@example.com"
|
||||
DEMO_URL: "https://demo.example.com/"
|
||||
ALLOWED_ORIGIN_DOMAIN: "example.com"
|
||||
```
|
||||
|
||||
|
||||
## Required HTTP Headers
|
||||
|
||||
* `Origin`
|
||||
* `Referer`
|
||||
|
||||
## SMTP
|
||||
https://cloud.google.com/compute/docs/tutorials/sending-mail/using-mailgun?hl=fr
|
||||
|
||||
## Test
|
||||
```
|
||||
curl -H'Origin: http://realopinsight.com' \
|
||||
-H'Referer: realopinsight.com' \
|
||||
-H'Content-Type: application/x-www-form-urlencoded' \
|
||||
-d 'target=contact' \
|
||||
-XPOST https://hugo-mx-gateway.ew.r.appspot.com/sendmail
|
||||
```
|
||||
# Build
|
||||
|
||||
```sh
|
||||
make build
|
||||
```
|
||||
|
||||
# Hugo Contact Form
|
||||
See `./model/hugo-contact-form.html`.
|
||||
|
||||
|
||||
```
|
||||
<div id="reply-message"></div>
|
||||
<div>
|
||||
<fieldset>
|
||||
<legend>Please fill in the form to submit your request</legend>
|
||||
<form action="https://contact-request-endpoint/" method="post">
|
||||
<div class="form-item">
|
||||
<label for="name">Name</label>
|
||||
<input type="text" name="name" id="name" placeholder="Mr. Smith" />
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label for="email">Email <span class="req"></span></label>
|
||||
<input type="text" name="email" id="email" class="required email" placeholder="smith@company.com" />
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label for="organization">Organization</label>
|
||||
<input type="text" name="organization" id="organization" placeholder="Company, Inc." />
|
||||
</div>
|
||||
{{ if in .Params.tags "contact" }}
|
||||
<div class="form-item">
|
||||
<label for="subject">Subject</label>
|
||||
<input type="text" name="subject" id="subject" value="" placeholder="Need help or expertise?" />
|
||||
<input type="hidden" name="target" id="target" value="contact" />
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label for="message">Message</label>
|
||||
<textarea rows="6" name="message" id="message" placeholder="Please add details concerning your request."></textarea>
|
||||
</div>
|
||||
{{ else }}
|
||||
<div class="form-item">
|
||||
<input type="hidden" name="subject" id="subject" value="Your Access to Product Demo!" />
|
||||
<input type="hidden" name="target" id="target" value="demo" />
|
||||
</div>
|
||||
{{ end }}
|
||||
<input class="button" type="submit" value="Submit">
|
||||
</form>
|
||||
</fieldset>
|
||||
</div>
|
||||
```
|
||||
|
|
|
@ -5,9 +5,7 @@ env_variables:
|
|||
SMTP_VERITY_CERT: true
|
||||
SMTP_CLIENT_USERNAME: "postmaster@example.com"
|
||||
SMTP_CLIENT_PASSWORD: "postmasterSecretPassWord"
|
||||
ALLOWED_ORIGINS: "127.0.0.1,example.com"
|
||||
CONTACT_REPLY_EMAIL: "noreply@example.com"
|
||||
CONTACT_REPLY_CC_EMAIL: "contact@example.com"
|
||||
DEMO_URL: "https://demo.example.com/"
|
||||
ALLOWED_ORIGIN_DOMAIN: "example.com"
|
||||
TEMPLATE_DEMO_REQUEST_REPLY: templates/template_reply_demo_request.html
|
||||
TEMPLATE_CONTACT_REQUEST_REPLY: templates/template_reply_contact_request.html
|
|
@ -1,28 +0,0 @@
|
|||
<form action="http://example.com/v1/sendemail" method="post">
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
<input type="text" name="name" id="name" placeholder="Mr. Smith" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input type="text" name="email" id="email" placeholder="smith@company.co" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="organization">Organization</label>
|
||||
<input type="text" name="organization" id="organization" placeholder="Company, Inc." />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="subject">Subject</label>
|
||||
<input type="text" name="subject" id="subject" value="Request subject" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="message">Message</label>
|
||||
<textarea name="message" id="message" placeholder="type your request here"></textarea>
|
||||
</div>
|
||||
|
||||
<input type="submit" value="Send">
|
||||
</form>
|
16
main.go
16
main.go
|
@ -18,7 +18,8 @@ package main
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
"time"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/gorilla/mux"
|
||||
|
@ -76,7 +77,6 @@ func NewRouter() *mux.Router {
|
|||
|
||||
func main() {
|
||||
viper.AutomaticEnv()
|
||||
viper.SetDefault("SERVER_ADDR", ":8080")
|
||||
viper.SetDefault("SERVER_TLS_CERT", "/etc/cert/cert.pem")
|
||||
viper.SetDefault("SERVER_TLS_PRIVATEKEY", "/etc/cert/privkey.pem")
|
||||
viper.SetDefault("SMTP_SERVER_ADDR", "127.0.0.1:465")
|
||||
|
@ -87,13 +87,17 @@ func main() {
|
|||
viper.SetDefault("EMAIL_SUBJECT", "Thanks to try our product")
|
||||
viper.SetDefault("DEMO_URL", "http://company.com/product-demo")
|
||||
|
||||
port := os.Getenv("PORT")
|
||||
if port == "" {
|
||||
port = "8080"
|
||||
log.Infof("Defaulting to port %s", port)
|
||||
}
|
||||
|
||||
router := NewRouter()
|
||||
|
||||
serverAddr := viper.GetString("SERVER_ADDR")
|
||||
log.Infof("Listening on port %s", port)
|
||||
|
||||
log.Infof("Listening on %s", serverAddr)
|
||||
|
||||
log.Fatal(http.ListenAndServe(serverAddr, router))
|
||||
log.Fatal(http.ListenAndServe(":"+port, router))
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<div id="reply-message"></div>
|
||||
<div>
|
||||
<fieldset>
|
||||
<legend>Please fill in the form to submit your request</legend>
|
||||
<form action="https://contact-request-endpoint/" method="post">
|
||||
<div class="form-item">
|
||||
<label for="name">Name</label>
|
||||
<input type="text" name="name" id="name" placeholder="Mr. Smith" />
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label for="email">Email <span class="req"></span></label>
|
||||
<input type="text" name="email" id="email" class="required email" placeholder="smith@company.com" />
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label for="organization">Organization</label>
|
||||
<input type="text" name="organization" id="organization" placeholder="Company, Inc." />
|
||||
</div>
|
||||
{{ if in .Params.tags "contact" }}
|
||||
<div class="form-item">
|
||||
<label for="subject">Subject</label>
|
||||
<input type="text" name="subject" id="subject" value="" placeholder="Need help or expertise?" />
|
||||
<input type="hidden" name="target" id="target" value="contact" />
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label for="message">Message</label>
|
||||
<textarea rows="6" name="message" id="message" placeholder="Please add details concerning your request."></textarea>
|
||||
</div>
|
||||
{{ else }}
|
||||
<div class="form-item">
|
||||
<input type="hidden" name="subject" id="subject" value="Your Access to Product Demo!" />
|
||||
<input type="hidden" name="target" id="target" value="demo" />
|
||||
</div>
|
||||
{{ end }}
|
||||
<input class="button" type="submit" value="Submit">
|
||||
</form>
|
||||
</fieldset>
|
||||
</div>
|
33
sendmail.go
33
sendmail.go
|
@ -22,12 +22,12 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
|
@ -79,7 +79,7 @@ func (m *SendMailRequest) Execute() error {
|
|||
|
||||
// TLS config
|
||||
tlsconfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
InsecureSkipVerify: viper.GetBool("SMTP_VERITY_CERT"),
|
||||
ServerName: smtpServerHost,
|
||||
}
|
||||
|
||||
|
@ -151,24 +151,25 @@ func (m *SendMailRequest) ParseTemplate(templateFileName string, data interface{
|
|||
// SendMail handles HTTP request to send email
|
||||
func SendMail(httpResp http.ResponseWriter, httpReq *http.Request) {
|
||||
|
||||
AllowedOriginDomain := viper.GetString("ALLOWED_ORIGIN_DOMAIN")
|
||||
AllowedOrigins := map[string]bool{
|
||||
fmt.Sprintf("http://%s", AllowedOriginDomain): true,
|
||||
fmt.Sprintf("https://%s", AllowedOriginDomain): true,
|
||||
fmt.Sprintf("http://www.%s", AllowedOriginDomain): true,
|
||||
fmt.Sprintf("https://www.%s", AllowedOriginDomain): true,
|
||||
allowedDomains := strings.Split(viper.GetString("ALLOWED_ORIGINS"), ",")
|
||||
allowedOrigins := make(map[string]bool)
|
||||
for _, domain := range allowedDomains {
|
||||
domainTrimmed := strings.TrimSpace(domain)
|
||||
allowedOrigins[fmt.Sprintf("http://%s", domainTrimmed)] = true
|
||||
allowedOrigins[fmt.Sprintf("https://%s", domainTrimmed)] = true
|
||||
allowedOrigins[fmt.Sprintf("http://www.%s", domainTrimmed)] = true
|
||||
allowedOrigins[fmt.Sprintf("https://www.%s", domainTrimmed)] = true
|
||||
}
|
||||
|
||||
if len(httpReq.Header["Origin"]) == 0 || len(httpReq.Header["Referer"]) == 0 {
|
||||
rawHeader, _ := json.Marshal(httpReq.Header)
|
||||
log.Println("request with unexpected headers:", string(rawHeader))
|
||||
log.Infoln("request with unexpected headers", string(rawHeader))
|
||||
httpResp.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
reqOrigin := httpReq.Header["Origin"][0]
|
||||
if _, domainFound := AllowedOrigins[reqOrigin]; !domainFound {
|
||||
log.Println("Not allowed origin:", reqOrigin)
|
||||
if _, domainFound := allowedOrigins[reqOrigin]; !domainFound {
|
||||
log.Errorln("not allowed origin", reqOrigin)
|
||||
httpResp.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
@ -191,13 +192,13 @@ func SendMail(httpResp http.ResponseWriter, httpReq *http.Request) {
|
|||
case "contact":
|
||||
recipients = []string{viper.GetString("CONTACT_REPLY_CC_EMAIL")}
|
||||
default:
|
||||
log.Println("Not allowed request type:", contactRequest.RequestTarget)
|
||||
log.Infoln("not allowed request type:", contactRequest.RequestTarget)
|
||||
httpResp.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
userData, _ := json.Marshal(contactRequest)
|
||||
log.Println("New Request:", string(userData))
|
||||
log.Infoln("New Request:", string(userData))
|
||||
|
||||
templateData := struct {
|
||||
Name string
|
||||
|
@ -233,7 +234,7 @@ func SendMail(httpResp http.ResponseWriter, httpReq *http.Request) {
|
|||
if err == nil {
|
||||
err := sendMailReq.Execute()
|
||||
if err != nil {
|
||||
log.Printf("error: %s", err.Error())
|
||||
log.Infof("error: %s", err.Error())
|
||||
contactResponse.Status = "error"
|
||||
contactResponse.Message = fmt.Sprintf("An internal error occurred, please try later or send us an email at %s.", viper.GetString("CONTACT_REPLY_CC_EMAIL"))
|
||||
} else {
|
||||
|
@ -245,7 +246,7 @@ func SendMail(httpResp http.ResponseWriter, httpReq *http.Request) {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
log.Printf("error: %s", err.Error())
|
||||
log.Infof("error: %s", err.Error())
|
||||
contactResponse.Status = "error"
|
||||
contactResponse.Message = "Invalid request, please review your input and try again."
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue