-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathmain.go
139 lines (111 loc) · 3.41 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package main
import (
"fmt"
"log"
"net/http"
"github.com/tj/go/env"
"github.com/tj/go/http/response"
"github.com/tj/go-news"
"github.com/tj/news-api/token"
)
// secret for token signing.
var secret = env.Get("TOKEN_SECRET")
// apiToken for authentication.
var apiToken = env.Get("API_TOKEN")
// subscribeURL is the redirect URL used after a subscribe.
var subscribeURL = env.Get("SUBSCRIBE_REDIRECT_URL")
// unsubscribeURL is the redirect URL used after an unsubscribe.
var unsubscribeURL = env.Get("UNSUBSCRIBE_REDIRECT_URL")
// newsletters storage.
var newsletters = news.New(env.GetDefault("DYNAMO_TABLE", "news"))
// port number to bind on.
var port = env.GetDefault("PORT", "3000")
func main() {
http.HandleFunc("/subscribers", auth(subscribers))
http.HandleFunc("/subscribe", subscribe)
http.HandleFunc("/unsubscribe", unsubscribe)
http.HandleFunc("/_health", health)
log.Printf("Listening on :%s", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}
// auth middleware.
func auth(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
_, pass, ok := r.BasicAuth()
if !ok {
response.Unauthorized(w, "Route requires api token")
return
}
if pass != apiToken {
response.Forbidden(w, "Invalid api token")
return
}
next.ServeHTTP(w, r)
}
}
// subscribers route.
func subscribers(w http.ResponseWriter, r *http.Request) {
newsletter := r.URL.Query().Get("newsletter")
if newsletter == "" {
response.BadRequest(w, "The newsletter query-string parameter is required")
return
}
emails, err := newsletters.GetSubscribers(newsletter)
if err != nil {
log.Printf("error fetching subscribers: %v\n", err)
return
}
response.OK(w, emails)
}
// subscribe route.
func subscribe(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
log.Printf("error parsing form: %v", err)
response.BadRequest(w, "Error parsing form values")
return
}
newsletter := r.FormValue("newsletter")
email := r.FormValue("email")
err = newsletters.AddSubscriber(newsletter, email)
if err != nil {
log.Printf("error subscribing email %q to %q: %v", email, newsletter, err)
response.InternalServerError(w, "Error subscribing to newsletter")
return
}
log.Printf("subscribed email %q to %q", email, newsletter)
w.Header().Set("Location", subscribeURL)
response.Found(w, "Redirecting to "+subscribeURL)
}
// unsubscribe route.
func unsubscribe(w http.ResponseWriter, r *http.Request) {
newsletter := r.URL.Query().Get("newsletter")
unsubscribeToken := r.URL.Query().Get("token")
if newsletter == "" {
response.BadRequest(w, "The newsletter query-string parameter is required")
return
}
if unsubscribeToken == "" {
response.BadRequest(w, "The token query-string parameter is required")
return
}
email, ok := token.Unsign(secret, unsubscribeToken)
if !ok {
log.Printf("error unsigning %q", unsubscribeToken)
response.BadRequest(w, "Invalid unsubscribe token")
return
}
err := newsletters.RemoveSubscriber(newsletter, email)
if err != nil {
log.Printf("error unsubscribing %q: %v", email, err)
response.InternalServerError(w, "Error unsubscribing from newsletter")
return
}
log.Printf("unsubscribed email %q from %q", email, newsletter)
w.Header().Set("Location", unsubscribeURL)
response.Found(w, "Redirecting to "+unsubscribeURL)
}
// health route.
func health(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, ":)")
}