diff options
-rw-r--r-- | srv/cmd/mediocre-blog/mailinglist.go | 22 | ||||
-rw-r--r-- | srv/cmd/mediocre-blog/main.go | 13 | ||||
-rw-r--r-- | srv/mailinglist/store.go | 2 | ||||
-rw-r--r-- | static/src/mailinglist/finalize.md | 53 | ||||
-rw-r--r-- | static/src/mailinglist/unsubscribe.md | 54 |
5 files changed, 133 insertions, 11 deletions
diff --git a/srv/cmd/mediocre-blog/mailinglist.go b/srv/cmd/mediocre-blog/mailinglist.go index 4a1ddce..39ab0d4 100644 --- a/srv/cmd/mediocre-blog/mailinglist.go +++ b/srv/cmd/mediocre-blog/mailinglist.go @@ -32,18 +32,24 @@ func mailingListSubscribeHandler(ml mailinglist.MailingList) http.Handler { } func mailingListFinalizeHandler(ml mailinglist.MailingList) http.Handler { + var errInvalidSubToken = errors.New("invalid subToken") + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { subToken := r.PostFormValue("subToken") if l := len(subToken); l == 0 || l > 128 { - badRequest(rw, r, errors.New("invalid subToken")) + badRequest(rw, r, errInvalidSubToken) return } err := ml.FinalizeSubscription(subToken) - if errors.Is(err, mailinglist.ErrNotFound) || - errors.Is(err, mailinglist.ErrAlreadyVerified) { - badRequest(rw, r, err) + + if errors.Is(err, mailinglist.ErrNotFound) { + badRequest(rw, r, errInvalidSubToken) return + + } else if errors.Is(err, mailinglist.ErrAlreadyVerified) { + // no problem + } else if err != nil { internalServerError(rw, r, err) return @@ -54,17 +60,21 @@ func mailingListFinalizeHandler(ml mailinglist.MailingList) http.Handler { } func mailingListUnsubscribeHandler(ml mailinglist.MailingList) http.Handler { + var errInvalidUnsubToken = errors.New("invalid unsubToken") + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { unsubToken := r.PostFormValue("unsubToken") if l := len(unsubToken); l == 0 || l > 128 { - badRequest(rw, r, errors.New("invalid unsubToken")) + badRequest(rw, r, errInvalidUnsubToken) return } err := ml.Unsubscribe(unsubToken) + if errors.Is(err, mailinglist.ErrNotFound) { - badRequest(rw, r, err) + badRequest(rw, r, errInvalidUnsubToken) return + } else if err != nil { internalServerError(rw, r, err) return diff --git a/srv/cmd/mediocre-blog/main.go b/srv/cmd/mediocre-blog/main.go index 748e10b..b94d405 100644 --- a/srv/cmd/mediocre-blog/main.go +++ b/srv/cmd/mediocre-blog/main.go @@ -27,7 +27,7 @@ func main() { logger := mlog.NewLogger(nil) - hostname := flag.String("hostname", "localhost:4000", "Hostname to advertise this server as") + publicURLStr := flag.String("public-url", "http://localhost:4000", "URL this service is accessible at") listenAddr := flag.String("listen-addr", ":4000", "Address to listen for HTTP requests on") dataDir := flag.String("data-dir", ".", "Directory to use for long term storage") @@ -55,6 +55,11 @@ func main() { logger.Fatal(context.Background(), "-ml-smtp-auth is required") } + publicURL, err := url.Parse(*publicURLStr) + if err != nil { + loggerFatalErr(context.Background(), logger, "parsing -public-url", err) + } + var staticProxyURL *url.URL if *staticProxyURLStr != "" { var err error @@ -79,7 +84,7 @@ func main() { // initialization ctx := mctx.Annotate(context.Background(), - "hostname", *hostname, + "publicURL", publicURL.String(), "listenAddr", *listenAddr, "dataDir", *dataDir, "powTarget", fmt.Sprintf("%x", powTarget), @@ -124,8 +129,8 @@ func main() { Store: mlStore, Mailer: mailer, Clock: clock, - FinalizeSubURL: *hostname + "/mailinglist/finalize.html", - UnsubURL: *hostname + "/mailinglist/unsubscribe.html", + FinalizeSubURL: path.Join(publicURL.String(), "/mailinglist/finalize.html"), + UnsubURL: path.Join(publicURL.String(), "/mailinglist/unsubscribe.html"), }) mux := http.NewServeMux() diff --git a/srv/mailinglist/store.go b/srv/mailinglist/store.go index 185e14d..f9790c0 100644 --- a/srv/mailinglist/store.go +++ b/srv/mailinglist/store.go @@ -17,7 +17,7 @@ import ( var ( // ErrNotFound is used to indicate an email could not be found in the // database. - ErrNotFound = errors.New("no record for given email found") + ErrNotFound = errors.New("no record found") ) // EmailIterator will iterate through a sequence of emails, returning the next diff --git a/static/src/mailinglist/finalize.md b/static/src/mailinglist/finalize.md new file mode 100644 index 0000000..fe8f741 --- /dev/null +++ b/static/src/mailinglist/finalize.md @@ -0,0 +1,53 @@ +--- +layout: page +title: "" +nofollow: true +--- + +<style> +#result.success { color: green; } +#result.fail { color: red; } +</style> + +<span id="result"></span> + +<script> + +(async () => { + const resultSpan = document.getElementById("result"); + + function setErr(errStr) { + resultSpan.className = "fail"; + resultSpan.innerHTML = errStr; + } + + const urlParams = new URLSearchParams(window.location.search); + const subToken = urlParams.get('subToken'); + + if (!subToken) { + setErr("No subscription token provided"); + return; + } + + const finalizeForm = new FormData(); + finalizeForm.append('subToken', subToken); + + const finalizeReq = new Request('/api/mailinglist/finalize', { + method: 'POST', + body: finalizeForm, + }); + + const res = await fetch(finalizeReq) + .then(response => response.json()); + + if (res.error) { + setErr(res.error); + return; + } + + resultSpan.className = "success"; + resultSpan.innerHTML = "Your email subscription has been finalized! Please go on about your day."; + +})(); + +</script> diff --git a/static/src/mailinglist/unsubscribe.md b/static/src/mailinglist/unsubscribe.md new file mode 100644 index 0000000..6a118a0 --- /dev/null +++ b/static/src/mailinglist/unsubscribe.md @@ -0,0 +1,54 @@ +--- +layout: page +title: "" +nofollow: true +--- + +<style> +#result.success { color: green; } +#result.fail { color: red; } +</style> + +<span id="result"></span> + +<script> + +(async () => { + const resultSpan = document.getElementById("result"); + + function setErr(errStr) { + resultSpan.className = "fail"; + resultSpan.innerHTML = errStr; + } + + const urlParams = new URLSearchParams(window.location.search); + const unsubToken = urlParams.get('unsubToken'); + + if (!unsubToken) { + setErr("No unsubscribe token provided"); + return; + } + + const unsubscribeForm = new FormData(); + unsubscribeForm.append('unsubToken', unsubToken); + + const unsubscribeReq = new Request('/api/mailinglist/unsubscribe', { + method: 'POST', + body: unsubscribeForm, + }); + + const res = await fetch(unsubscribeReq) + .then(response => response.json()); + + if (res.error) { + setErr(res.error); + return; + } + + resultSpan.className = "success"; + resultSpan.innerHTML = "You have been unsubscribed! Please go on about your day."; + +})(); + +</script> + |