summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--srv/cmd/mediocre-blog/mailinglist.go22
-rw-r--r--srv/cmd/mediocre-blog/main.go13
-rw-r--r--srv/mailinglist/store.go2
-rw-r--r--static/src/mailinglist/finalize.md53
-rw-r--r--static/src/mailinglist/unsubscribe.md54
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>
+