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(), }, }) } }