183 lines
4.1 KiB
Go
183 lines
4.1 KiB
Go
package merp
|
|
|
|
import "encoding/json"
|
|
import "net/http"
|
|
import "time"
|
|
|
|
import "github.com/astaxie/beego/orm"
|
|
import "github.com/gin-gonic/gin"
|
|
import "github.com/google/uuid"
|
|
import "github.com/rs/zerolog/log"
|
|
import "github.com/sneak/merp/models"
|
|
|
|
const MAX_MERPS_TO_RETURN = 50
|
|
const LONGPOLL_TIMEOUT_SECS = 60
|
|
|
|
func decodeJSON(in []byte) (interface{}, error) {
|
|
var out interface{}
|
|
err := json.Unmarshal(in, &out)
|
|
if err != nil {
|
|
log.Error().Msg("error decoding json")
|
|
return nil, err
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (ms *Server) listenForMerps() gin.HandlerFunc {
|
|
// /listen/for/merps/from/my-thing-name
|
|
return func(c *gin.Context) {
|
|
thing := c.Param("thing")
|
|
if thing != "" {
|
|
if thingRegex.MatchString(thing) == false {
|
|
c.JSON(http.StatusPreconditionFailed, gin.H{
|
|
"this": "failed",
|
|
"status": http.StatusPreconditionFailed,
|
|
"because": "invalid thing format, try [a-zA-Z0-9-_.]",
|
|
})
|
|
return
|
|
}
|
|
}
|
|
|
|
el := NewEventListener(MerpTopic(thing))
|
|
|
|
timeout := time.After(LONGPOLL_TIMEOUT_SECS * time.Second)
|
|
// Keep trying until we're timed out or got a result or got an error
|
|
for {
|
|
select {
|
|
case <-c.Done():
|
|
// unregister listener and close conn FIXME
|
|
case <-timeout:
|
|
// unregister listener and close conn FIXME
|
|
case newMerpJSON := <-el.NewMerpJSONChannel:
|
|
log.Info().Msg(newMerpJSON)
|
|
// send them some json FIXME
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (ms *Server) getLatestMerps() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
thing := c.Param("thing")
|
|
|
|
if thing != "" {
|
|
if thingRegex.MatchString(thing) == false {
|
|
c.JSON(http.StatusPreconditionFailed, gin.H{
|
|
"this": "failed",
|
|
"status": http.StatusPreconditionFailed,
|
|
"because": "invalid thing format, try [a-zA-Z0-9-_]",
|
|
})
|
|
return
|
|
}
|
|
}
|
|
|
|
var qs orm.QuerySeter
|
|
if thing == "" {
|
|
qs = ms.db.QueryTable("merp").OrderBy("-created").Limit(MAX_MERPS_TO_RETURN)
|
|
} else {
|
|
qs = ms.db.QueryTable("merp").Filter("thing", thing).OrderBy("-created").Limit(MAX_MERPS_TO_RETURN)
|
|
}
|
|
|
|
var merps []*models.Merp
|
|
qs.All(&merps)
|
|
|
|
output := make([]map[string]interface{}, 0)
|
|
for _, merp := range merps {
|
|
outelem := make(map[string]interface{})
|
|
outelem["thing"] = merp.Thing
|
|
outjs, err := decodeJSON([]byte(merp.Content))
|
|
if err != nil {
|
|
outelem["content"] = gin.H{}
|
|
} else {
|
|
outelem["content"] = outjs
|
|
}
|
|
outelem["created"] = merp.Created
|
|
output = append(output, outelem)
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"this": "succeeded",
|
|
"by": "getting",
|
|
"the": "merps",
|
|
"with": output,
|
|
})
|
|
}
|
|
}
|
|
|
|
func (ms *Server) handleNewMerp() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// request time
|
|
thing := c.Param("thing")
|
|
if thingRegex.MatchString(thing) == false {
|
|
c.JSON(http.StatusPreconditionFailed, gin.H{
|
|
"this": "failed",
|
|
"status": http.StatusPreconditionFailed,
|
|
"because": "invalid thing format, try a-zA-Z0-9-_",
|
|
})
|
|
return
|
|
}
|
|
|
|
// FIXME rate limit this a bit on thing+clientip+json to cut down on
|
|
// repeated messages
|
|
content := make(map[string]interface{})
|
|
respContent := gin.H{}
|
|
// FIXME support POST data as well
|
|
|
|
for k, v := range c.Request.URL.Query() {
|
|
content[k] = v[0]
|
|
respContent[k] = v[0]
|
|
}
|
|
|
|
u := uuid.New()
|
|
at := time.Now().UTC()
|
|
atString := at.Format(time.RFC3339)
|
|
|
|
serialized, jsonerr := json.MarshalIndent(content, "", " ")
|
|
|
|
if jsonerr != nil {
|
|
c.JSON(
|
|
http.StatusPreconditionFailed,
|
|
gin.H{
|
|
"this": "failed",
|
|
"status": http.StatusPreconditionFailed,
|
|
"because": jsonerr.Error(),
|
|
},
|
|
)
|
|
return
|
|
}
|
|
|
|
merp := models.Merp{
|
|
Created: at,
|
|
Thing: thing,
|
|
Content: string(serialized),
|
|
UUID: u.String(),
|
|
}
|
|
|
|
_, err := ms.db.Insert(&merp)
|
|
|
|
if err != nil {
|
|
c.JSON(
|
|
http.StatusPreconditionFailed,
|
|
gin.H{
|
|
"this": "failed",
|
|
"status": http.StatusPreconditionFailed,
|
|
"because": err.Error(),
|
|
},
|
|
)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"this": "succeeded",
|
|
"by": "merping",
|
|
"the": "merp",
|
|
"with": gin.H{
|
|
"thing": thing,
|
|
"created": atString,
|
|
"content": respContent,
|
|
"transaction": u.String(),
|
|
},
|
|
})
|
|
}
|
|
}
|