diff --git a/README.md b/README.md index e40e227..20d3c66 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,17 @@ Supply environment variables by either exporting them or editing ```.env```. | LISTEN | Host and port number to listen on | :8080 | | REDIS | Host and port of redis | :6379 | +## Status Codes + +The system supports arbitrary status codes. However, in the interest of standardisation, a system of codes (based on Skype statuses) is listed here: + +| Code | Description | +| ---- | ----------- | +| 0 | Active | +| 1 | Away | +| 2 | Do not disturb | +| 3 | Invisible | + ## API ### Subscribe User @@ -21,16 +32,25 @@ Supply environment variables by either exporting them or editing ```.env```. GET /subscribe/:userid/client/:clientid ``` -Subscribe to a user. Every time a user pings this service, the time will be sent to all subscribed users. Upon subscription, if it exists, the last cached time of the target user will be pushed immediately to the stream. +Subscribe to a user. Every time a user pings this service, the time will be sent to all subscribed users. Upon subscription, if it exists, the last cached ping of the target user will be pushed immediately to the stream. ```js const es = new EventSource(`${host}/subscribe/${user}/client/${device}`); es.onmessage = (e) => { const timestamp = e.data; - // Do whatever with the timestamp + // Do whatever with the ping data }; ``` +Ping data: + +```json +{ + "time": "", + "status": "" +} +``` + #### URL Params | Name | Type | Description | Required | @@ -58,6 +78,12 @@ Ping the server. | ---- | ----------- | | X-User-Claim | Stringified user claim, populated by `backend-auth` called by `traefik` | +#### Body + +| Name | Type | Description | +| ---- | ---- | ----------- | +| status | String | Status code | + #### Success Response (200 OK) Empty body. diff --git a/main.go b/main.go index 74be42c..bd207ee 100644 --- a/main.go +++ b/main.go @@ -22,6 +22,11 @@ type RawClient struct { ClientId string `json:"clientid"` } +type Ping struct { + Time string `json:"time"` + Status string `json:"status"` +} + var connections map[RawClient][]chan []byte var redisClient *redis.Client @@ -76,10 +81,18 @@ func Subscribe(w http.ResponseWriter, r *http.Request, p httprouter.Params) { ticker := time.NewTicker(25 * time.Second) // Push cached value (if it exists) to the connection - cachedTime, err := redisClient.Get(client.UserId + client.ClientId).Result() - if err == nil { - fmt.Fprintf(w, "data: %s\n\n", cachedTime) - flusher.Flush() + cachedTime, err1 := redisClient.HGet(client.UserId + client.ClientId, "time").Result() + cachedStatus, err2 := redisClient.HGet(client.UserId + client.ClientId, "status").Result() + if err1 == nil && err2 == nil { + ping := Ping { + Time: cachedTime, + Status: cachedStatus, + } + pingBytes, err := json.Marshal(&ping) + if err == nil { + fmt.Fprintf(w, "data: %s\n\n", pingBytes) + flusher.Flush() + } } for { @@ -97,6 +110,9 @@ func Subscribe(w http.ResponseWriter, r *http.Request, p httprouter.Params) { } } +type PostTimeRequest struct { + Status string `json:"status"` +} func PostTime(w http.ResponseWriter, r *http.Request, p httprouter.Params) { ua := r.Header.Get("X-User-Claim") if ua == "" { @@ -112,12 +128,31 @@ func PostTime(w http.ResponseWriter, r *http.Request, p httprouter.Params) { return } - time := []byte(strconv.FormatInt(time.Now().UTC().Unix(), 10)) // UTC Epoch Time in []byte + decoder := json.NewDecoder(r.Body) + var ptRequest PostTimeRequest + err = decoder.Decode(&ptRequest) + if err != nil { + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) + return + } + + ping := Ping { + Time: strconv.FormatInt(time.Now().UTC().Unix(), 10), // UTC Epoch time, + Status: ptRequest.Status, + } + key := client.UserId + client.ClientId - _ = redisClient.Set(key, time, 0) + _ = redisClient.HSet(key, "time", []byte(ping.Time)) + _ = redisClient.HSet(key, "status", []byte(ping.Status)) + + pingBytes, err := json.Marshal(&ping) + if err != nil { + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } for _, connection := range connections[client] { - connection <- time + connection <- pingBytes } w.WriteHeader(http.StatusOK)