feat: Hub接收RESTful API的消息
This commit is contained in:
@ -10,16 +10,20 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.jinshen.cn/remilia/push-server/interval/api"
|
"git.jinshen.cn/remilia/push-server/interval/api"
|
||||||
|
"git.jinshen.cn/remilia/push-server/interval/hub"
|
||||||
"git.jinshen.cn/remilia/push-server/interval/server"
|
"git.jinshen.cn/remilia/push-server/interval/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
_, serverCancel := context.WithCancel(context.Background())
|
serverCtx, serverCancel := context.WithCancel(context.Background())
|
||||||
defer func() {
|
defer func() {
|
||||||
serverCancel()
|
serverCancel()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
httpServer := server.NewHTTPServer(":8080", api.NewRouter())
|
h := hub.NewHub()
|
||||||
|
go h.Run(serverCtx)
|
||||||
|
|
||||||
|
httpServer := server.NewHTTPServer(":8080", api.NewRouter(h))
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
log.Println("Starting HTTP server on :8080")
|
log.Println("Starting HTTP server on :8080")
|
||||||
|
|||||||
5
interval/api/dto/publish.go
Normal file
5
interval/api/dto/publish.go
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
type PublishRequest struct {
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
47
interval/api/handler/push.go
Normal file
47
interval/api/handler/push.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.jinshen.cn/remilia/push-server/interval/api/dto"
|
||||||
|
"git.jinshen.cn/remilia/push-server/interval/hub"
|
||||||
|
"git.jinshen.cn/remilia/push-server/interval/model"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PushHandler(hub *hub.Hub) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
topicStr := chi.URLParam(r, "topic")
|
||||||
|
topic := model.Topic(topicStr)
|
||||||
|
if !topic.Valid() {
|
||||||
|
http.Error(w, "invalid topic", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req dto.PublishRequest
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
|
http.Error(w, "invalid request body", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if req.Content == "" {
|
||||||
|
http.Error(w, "content cannot be empty", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := model.Message{
|
||||||
|
Topic: topic,
|
||||||
|
Content: []byte(req.Content),
|
||||||
|
Timestamp: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := hub.BroadcastMessage(r.Context(), msg); err != nil {
|
||||||
|
http.Error(w, "request cancelled", http.StatusRequestTimeout)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusAccepted)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,13 +4,15 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.jinshen.cn/remilia/push-server/interval/api/handler"
|
"git.jinshen.cn/remilia/push-server/interval/api/handler"
|
||||||
|
"git.jinshen.cn/remilia/push-server/interval/hub"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewRouter() http.Handler {
|
func NewRouter(h *hub.Hub) http.Handler {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
|
|
||||||
r.Post("/health", handler.Health)
|
r.Post("/health", handler.Health)
|
||||||
|
r.Post("/push/{topic}", handler.PushHandler(h))
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,11 +65,18 @@ func (h *Hub) Unsubscribe(sub model.Subscription) {
|
|||||||
h.unsubscribe <- sub
|
h.unsubscribe <- sub
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hub) BroadcastMessage(message model.Message) {
|
func (h *Hub) BroadcastMessage(ctx context.Context, msg model.Message) error {
|
||||||
h.broadcast <- message
|
select {
|
||||||
|
case h.broadcast <- msg:
|
||||||
|
return nil
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hub) Run(ctx context.Context) {
|
func (h *Hub) Run(ctx context.Context) {
|
||||||
|
log.Println("Hub is running")
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case c := <-h.register:
|
case c := <-h.register:
|
||||||
@ -155,6 +162,7 @@ func (h *Hub) onBroadcast(msg model.Message) {
|
|||||||
log.Printf("Broadcast failed: invalid topic")
|
log.Printf("Broadcast failed: invalid topic")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log.Printf("Receiving message for topic %s: %s", msg.Topic, string(msg.Content))
|
||||||
for _, c := range h.clientsByTopic[msg.Topic] {
|
for _, c := range h.clientsByTopic[msg.Topic] {
|
||||||
select {
|
select {
|
||||||
case c.SendChan <- msg.Content:
|
case c.SendChan <- msg.Content:
|
||||||
|
|||||||
@ -3,4 +3,5 @@ package model
|
|||||||
type Message struct {
|
type Message struct {
|
||||||
Topic Topic
|
Topic Topic
|
||||||
Content []byte
|
Content []byte
|
||||||
|
Timestamp int64
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user