Philipp Heckel 4 лет назад
Родитель
Сommit
39574c954b
2 измененных файлов с 68 добавлено и 2 удалено
  1. 53 0
      examples/example_eventsource_sse.html
  2. 15 2
      server/server.go

+ 53 - 0
examples/example_eventsource_sse.html

@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>ntfy.sh: EventSource Example</title>
+    <style>
+        body { font-size: 1.2em; line-height: 130%; }
+        #events { font-family: monospace; }
+    </style>
+</head>
+<body>
+<h1>ntfy.sh: EventSource Example</h1>
+<p>
+    This is an example showing how to use <a href="https://ntfy.sh">ntfy.sh</a> with
+    <a href="https://developer.mozilla.org/en-US/docs/Web/API/EventSource">EventSource</a>.<br/>
+</p>
+<button id="publishButton">Send test notification</button>
+<p><b>Log:</b></p>
+<div id="events"></div>
+
+<script type="text/javascript">
+    const publishURL = `https://ntfy.sh/example`;
+    const subscribeURL = `https://ntfy.sh/example/sse`;
+    const events = document.getElementById('events');
+    const eventSource = new EventSource(subscribeURL);
+
+    // Publish button
+    document.getElementById("publishButton").onclick = () => {
+        fetch(publishURL, {
+            method: 'POST', // works with PUT as well, though that sends an OPTIONS request too!
+            body: `It is ${new Date().toString()}. This is a test.`
+        })
+    };
+
+    // Incoming events
+    eventSource.onopen = () => {
+        let event = document.createElement('div');
+        event.innerHTML = `EventSource connected to ${subscribeURL}`;
+        events.appendChild(event);
+    };
+    eventSource.onerror = (e) => {
+        let event = document.createElement('div');
+        event.innerHTML = `EventSource error: Failed to connect to ${subscribeURL}`;
+        events.appendChild(event);
+    };
+    eventSource.onmessage = (e) => {
+        let event = document.createElement('div');
+        event.innerHTML = e.data;
+        events.appendChild(event);
+    };
+</script>
+
+</body>
+</html>

+ 15 - 2
server/server.go

@@ -131,6 +131,8 @@ func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request) error {
 		return s.handleSubscribeRaw(w, r)
 	} else if (r.Method == http.MethodPut || r.Method == http.MethodPost) && topicRegex.MatchString(r.URL.Path) {
 		return s.handlePublishHTTP(w, r)
+	} else if r.Method == http.MethodOptions {
+		return s.handleOptions(w, r)
 	}
 	return errHTTPNotFound
 }
@@ -154,7 +156,11 @@ func (s *Server) handlePublishHTTP(w http.ResponseWriter, r *http.Request) error
 		Time:    time.Now().UnixMilli(),
 		Message: string(b),
 	}
-	return t.Publish(msg)
+	if err := t.Publish(msg); err != nil {
+		return err
+	}
+	w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
+	return nil
 }
 
 func (s *Server) handleSubscribeJSON(w http.ResponseWriter, r *http.Request) error {
@@ -169,6 +175,7 @@ func (s *Server) handleSubscribeJSON(w http.ResponseWriter, r *http.Request) err
 		return nil
 	})
 	defer s.unsubscribe(t, subscriberID)
+	w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
 	select {
 	case <-t.ctx.Done():
 	case <-r.Context().Done():
@@ -194,7 +201,7 @@ func (s *Server) handleSubscribeSSE(w http.ResponseWriter, r *http.Request) erro
 	})
 	defer s.unsubscribe(t, subscriberID)
 	w.Header().Set("Content-Type", "text/event-stream")
-	w.WriteHeader(http.StatusOK)
+	w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
 	if _, err := io.WriteString(w, "event: open\n\n"); err != nil {
 		return err
 	}
@@ -228,6 +235,12 @@ func (s *Server) handleSubscribeRaw(w http.ResponseWriter, r *http.Request) erro
 	return nil
 }
 
+func (s *Server) handleOptions(w http.ResponseWriter, r *http.Request) error {
+	w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
+	w.Header().Set("Access-Control-Allow-Methods", "GET, PUT, POST")
+	return nil
+}
+
 func (s *Server) createTopic(id string) *topic {
 	s.mu.Lock()
 	defer s.mu.Unlock()