func (a *ApplicationController) CreateApplicationHandler(c *gin.Context) { body := &ApplicationModel{} if err := c.Bind(body); err != nil { log.Error(RestError(c, err)) return } if body.Name == "" { log.Error(RestErrorInvalidBody(c)) return } email := ApiUser(c).Email app := models.JSON{ db.ID_FIELD: body.Id, db.NAME_FIELD: body.Name, db.OWNER_FIELD: email, db.MASTER_KEY_FIELD: strings.ToUpper(utils.GetCleanUUID()), } appId, err := a.DbService.CreateApp(email, app) if err != nil { log.Error(RestError(c, err)) return } RespondId(appId, c) }
func (a *AuthController) registerUser(c *gin.Context, isApp bool) { var u models.JSON if err := c.Bind(&u); err != nil { log.Error(RestErrorInvalidBody(c)) return } hashedPassword, err := bcrypt.GenerateFromPassword([]byte(u[db.PASSWORD_FIELD].(string)), 10) if err != nil { log.Error(RestError(c, err)) return } user := models.JSON{ db.EMAIL_FIELD: u[db.EMAIL_FIELD], db.PASSWORD_FIELD: hashedPassword, db.REGISTERED_AT_FIELD: time.Now(), } if isApp { user[db.APP_ID_FIELD] = c.Param(APP_ID_PARAM) } else { user[db.APPS_FIELD] = []interface{}{} } err = a.DbService.CreateUser(user, isApp) if err != nil { log.Error(RestError(c, err)) } }
func authorizeMiddleware(stop bool) gin.HandlerFunc { return func(c *gin.Context) { authHeader := c.Request.Header.Get("Authorization") if authHeader != "" { authHeaderParts := strings.SplitN(authHeader, " ", 2) if len(authHeaderParts) != 2 { log.Error(RestErrorUnauthorized(c)) return } authType := strings.ToLower(authHeaderParts[0]) authValue := authHeaderParts[1] //TODO: authorization for master token, master key, normal token, app id only var token *jwt.Token var err error user := &apiUser{} if authType == "bearer" { token, err = authWithToken(c, authValue) if err == nil { user.Email = token.Claims["user"].(string) user.InApp = token.Claims["inApp"].(bool) user.Master = !user.InApp //we can use the token instead of a master key user.Key = authValue } } else if authType == "masterkey" { email, err := authWithMaster(c, authValue) if err == nil { user.Email = email user.InApp = false user.Master = true user.Key = authValue } } else { c.Next() return } c.Set("user", user) if err != nil { log.Error(RestError(c, err)) return } c.Next() } else { if !stop { c.Next() } else { log.Error(RestErrorUnauthorized(c)) } } } }
func (p WsMessageReceiver) handlePublish(im interceptorMessage, msg *gowamp.Publish) (interface{}, error) { if string(msg.Topic) == "wamp.session.on_leave" { args := msg.Arguments if len(args) == 0 { return nil, nil } if leavingSessionId, ok := args[0].(gowamp.ID); ok { log.Info("Broadcasting session leave:", leavingSessionId) p.broadcaster.Broadcast(leavingSessionId) return nil, nil } log.Info("Leave:", args) return nil, nil } if string(msg.Topic) == "wamp.session.on_join" { return nil, nil } if len(msg.Arguments) == 0 { return nil, nil } m, ok := msg.Arguments[0].(string) if !ok { m = models.String(msg.Arguments[0]) } var clientMessage messaging.Message converError := clientMessage.FromString(m) if converError != nil { log.Error(converError) return nil, converError } if clientMessage.Origin == messaging.ORIGIN_API { return nil, nil } log.Info("Websocket message received:", clientMessage) data, apiError := p.ClientProcessor.Process(clientMessage) if apiError != nil { log.Error(apiError) } return data, apiError }
func (p RpcMessageReceiver) makeErrorResult(err error) *gowamp.CallResult { log.Error(err) return &gowamp.CallResult{ Err: gowamp.URI(err.Error()), Args: []interface{}{err}, } }
func NewWebSocketServer() (*gowamp.WebsocketServer, *gowamp.Client, *wsInterceptor, error) { interceptor := NewWsInterceptor() r := gowamp.Realm{} r.Interceptor = interceptor realms := map[string]gowamp.Realm{} realms[config.CONST_DEFAULT_REALM] = r wsServer, err := gowamp.NewWebsocketServer(realms) if err != nil { return nil, nil, nil, err } wsServer.Upgrader.CheckOrigin = func(r *http.Request) bool { //allow connections from any origin return true } c, err := wsServer.GetLocalClient(config.CONST_DEFAULT_REALM, nil) if err != nil { log.Error(err) return nil, nil, nil, err } http.Handle("/", wsServer) http.HandleFunc("/_status", func(w http.ResponseWriter, r *http.Request) { log.Info("_status check") //we are fine }) return wsServer, c, interceptor, nil }
func validateAppOperationsAuthorizationMiddleware() gin.HandlerFunc { return func(c *gin.Context) { user := ApiUser(c) if user.InApp && !user.Master { log.Error(RestErrorUnauthorized(c), user) return } } }
func String(input interface{}) string { b, err := json.Marshal(input) if err != nil { log.Error(err) return "{}" } return string(b) }
func (a *ApplicationController) GetApplicationsHandler(c *gin.Context) { email := ApiUser(c).Email apps, err := a.DbService.GetApps(email) if err != nil { log.Error(RestError(c, err)) return } c.JSON(http.StatusOK, apps) }
func validateAppPermissionsMiddleware() gin.HandlerFunc { return func(c *gin.Context) { user := ApiUser(c) if !user.Master { log.Error(RestErrorUnauthorized(c), user) c.Next() return } } }
func (t *TypesController) DeleteType(c *gin.Context) { appId := c.Param("appId") typeName := c.Param("typeName") err := t.DbService.DeleteAllItems(appId, typeName) if err != nil { log.Error(RestError(c, err)) return } }
func validateAppMiddleware() gin.HandlerFunc { return func(c *gin.Context) { appId := c.Param("appId") log.Info(appId) if appId == "" { log.Error(RestError(c, "Invalid app id.")) } else { c.Next() } } }
func (t *TypesController) GetTypesHandler(c *gin.Context) { appId := c.Param("appId") types, err := t.DbService.GetTypes(appId) if err != nil { log.Error(RestError(c, err)) return } c.JSON(http.StatusOK, types) }
func (a *AuthController) loginUser(c *gin.Context, isApp bool) { var u UserModel if err := c.Bind(&u); err != nil { log.Error(RestError(c, err)) return } appId := "" if isApp { appId = c.Param(APP_ID_PARAM) } user, err := a.DbService.GetUser(u.Email, isApp, appId) if err != nil { log.Error(RestError(c, err)) return } err = bcrypt.CompareHashAndPassword(user[db.PASSWORD_FIELD].([]byte), []byte(u.Password)) if err != nil { log.Error(RestError(c, err)) return } token := jwt.New(jwt.GetSigningMethod("HS256")) token.Claims["user"] = u.Email token.Claims["expiration"] = time.Now().Add(time.Minute + 60).Unix() token.Claims["inApp"] = isApp tokenStr, err := token.SignedString([]byte("")) if err != nil { log.Error(RestError(c, err)) return } c.JSON(http.StatusOK, models.JSON{ "token": tokenStr, }) }
func (p MessageProcessor) shouldProcessMessage(m Message) bool { key := m.GetRedisKey() timestamp := p.redisClient.Get(key).Val() if timestamp == "" { return true } cachedTime, parseTimeError := time.Parse(time.RFC3339, timestamp) if parseTimeError == nil { log.Error("Error parsing message time:", parseTimeError) return true } messageTime, parseTimeError := time.Parse(time.RFC3339, m.Timestamp) if parseTimeError == nil { log.Error("Error parsing message time:", parseTimeError) return true } return cachedTime.Before(messageTime) }
func (b *Broadcaster) Broadcast(v interface{}) { defer func() { if err := recover(); err != nil { log.Error(err) } }() log.Info(b.listeners) for _, ch := range b.listeners { ch <- v } }
func (t *TypesController) DeleteTypeItemById(c *gin.Context) { //appId := c.Param("appId") //typeName := c.Param("typeName") itemId := c.Param("itemId") //TODO: same as above err := t.DbService.DeleteItemById(itemId) if err != nil { log.Error(RestError(c, err)) return } }
func (t *TypesController) UpdateTypeItemById(c *gin.Context) { //appId := c.Param("appId") //typeName := c.Param("typeName") itemId := c.Param("itemId") //TODO: same as get body := webUtils.GetBody(c) err := t.DbService.UpdateItemById(itemId, body) if err != nil { log.Error(RestError(c, err)) return } }
func (t *TypesController) InsertInTypeHandler(c *gin.Context) { appId := c.Param("appId") typeName := c.Param("typeName") body := webUtils.GetBody(c) id, err := t.DbService.CreateItem(appId, typeName, body) if err != nil { log.Error(RestError(c, err)) return } RespondId(id, c) }
func (a *ApplicationController) GetApplicationHandler(c *gin.Context) { //user := ApiUser(c).Email appId := c.Param("appId") //TODO: permissions app, err := a.DbService.GetApp(appId) if err != nil { log.Error(RestError(c, err)) return } c.JSON(http.StatusOK, app) }
func (c *Client) Connect() { for { conn, err := c.connect() if err == nil { c.isConnected = true c.connection = conn log.Info("Connected to", c.Addr) break } else { log.Error("Error connecting to", c.Addr, err) time.Sleep(time.Second * 5) } } }
func main() { r.SetVerbose(true) defer utils.Recover() utils.ListenSignals() utils.Liveness() err := server.Initialize() if err != nil { panic(err) } log.Info("Realtime service listening") log.Error(http.ListenAndServe(config.Get(config.KEY_REALTIME_PORT), nil)) }
func Liveness() { interval := 1 * time.Second pid := strconv.Itoa(os.Getpid()) filename := "hearthbeat" cwd, err := os.Getwd() filepath := path.Join(cwd, filename) if err != nil { panic(err) } go func() { hearthbeat := func() { if _, err := os.Stat(filepath); os.IsNotExist(err) { f, createErr := os.Create(filepath) if createErr != nil { log.Error("Error creating hearthbeat:", createErr) return } _, writeErr := f.WriteString(pid) if writeErr != nil { log.Error("Error writing heartbeat:", writeErr) return } } } t := time.Tick(interval) for { select { case <-t: hearthbeat() } } }() }
func (t *TypesController) GetTypeItemById(c *gin.Context) { //appId := c.Param("appId") //typeName := c.Param("typeName") itemId := c.Param("itemId") //TODO: this should work out of the box since all ids are unique in the data table //no specific app and type needed //TODO: permissions item, err := t.DbService.GetItemById(itemId) if err != nil { log.Error(RestError(c, err)) return } c.JSON(http.StatusOK, item) }
func (t *TypesController) GetTypeDataHandler(c *gin.Context) { appId := c.Param("appId") typeName := c.Param("typeName") opts := GetHeaderOptions(c) items, err := t.DbService.GetItems(appId, typeName, opts.Filter) if err != nil { log.Error(RestError(c, err)) return } if items == nil { items = make([]models.JSON, 0) } c.JSON(http.StatusOK, items) }
func (c *ApiClient) SendRequest(url, method string, body interface{}, isArray bool) (interface{}, error) { log.Info( "Sending request", "BaseUrl:", c.BaseUrl, "Url:", url, "Method:", method, "Body:", body, "Token:", c.Token, "AppId:", c.AppId, "NotifyRealtime", c.NotifyRealTime, ) var bodyStr = "" if body != nil { b, err := json.Marshal(body) if err != nil { log.Error(err) return nil, err } bodyStr = string(b) } req, err := http.NewRequest(method, c.BaseUrl+url, strings.NewReader(bodyStr)) if err != nil { log.Error(err) return nil, err } opts := models.Options{} //todo: in app and not in app token if c.Token != "" { req.Header.Set("Authorization", "Bearer "+c.Token) } if c.ClientId != "" { opts.ClientId = &c.ClientId } opts.Notify = &c.NotifyRealTime opts.Filter = c.Filter //opts.Origin = c.Origin optsS, err := opts.String() if err != nil { log.Error(err) return nil, err } req.Header.Set("NeutrinoOptions", optsS) client := http.Client{} res, err := client.Do(req) if err != nil { log.Info(err) return nil, err } if res == nil { log.Error("Unknown error") return nil, nil } if res.StatusCode != http.StatusOK { log.Info(res, err) return nil, err } defer res.Body.Close() if err != nil { log.Error(err) return nil, err } bodyRes, err := ioutil.ReadAll(res.Body) if err != nil { log.Error(err) return nil, err } if string(bodyRes) == "" { log.Info("Empty body response!") return nil, nil } var result interface{} log.Info("API response: ", string(bodyRes)) if isArray { jsonArray := make([]models.JSON, 0) err = json.Unmarshal(bodyRes, &jsonArray) result = jsonArray } else { m := models.JSON{} err = json.Unmarshal(bodyRes, &m) result = m } if err != nil { log.Error(err) return nil, err } return result, nil }
func (p WsMessageReceiver) handleSubscribe(im interceptorMessage, msg *gowamp.Subscribe) { opts := models.SubscribeOptions{} err := models.Convert(msg.Options, &opts) if err != nil { log.Error(err) return } topic := fmt.Sprintf("%v", msg.Topic) topicArguments := strings.Split(topic, ".") opts.Topic = topic if opts.IsSpecial() { baseTopic := messaging.BuildTopicArbitrary(topicArguments[:len(topicArguments)-1]...) opts.BaseTopic = baseTopic } else { opts.BaseTopic = topic } log.Info("Listening for changefeed:", opts) d := db.NewDbService() newValuesChan := make(chan map[string]interface{}) //for create and delete we want to listen for changes for the whole collection, for update only for the specific item if opts.Operation == messaging.OP_CREATE || opts.Operation == messaging.OP_DELETE { err = d.Changes(opts.AppId, opts.Type, opts.Filter, newValuesChan) } else if opts.Operation == messaging.OP_UPDATE { opts.ItemId = topicArguments[len(topicArguments)-1] err = d.ChangesId(opts.ItemId, newValuesChan) } if err != nil { log.Error(err) return } messageBuilder := messaging.GetMessageBuilder() go func() { leaveChan := make(chan interface{}) p.broadcaster.Subscribe(leaveChan) for { select { case val := <-newValuesChan: p.processDatabaseUpdate(val, &messageBuilder, opts) case leaveVal := <-leaveChan: sessionId := leaveVal.(gowamp.ID) if im.sess.Id == sessionId { //TODO: newValuesChan seems to be automatically closed by the rethinkdb driver //investigate whether we need to do something else _, leaveChanOpened := <-leaveChan if leaveChanOpened { close(leaveChan) } p.broadcaster.Remove(leaveChan) return } } } }() }
func (p WsMessageReceiver) processDatabaseUpdate( val map[string]interface{}, builder *messaging.MessageBuilder, opts models.SubscribeOptions, ) { messageBuilder := *builder pld := models.JSON{} var dbOp string newVal := val["new_val"] oldVal := val["old_val"] if newVal != nil && oldVal == nil { dbOp = messaging.OP_CREATE } else if newVal == nil && oldVal != nil { dbOp = messaging.OP_DELETE } else { dbOp = messaging.OP_UPDATE } //only emit messages with the same operation as the subscriber if dbOp != opts.Operation { return } var dbVal map[string]interface{} if dbOp == messaging.OP_CREATE { if newVal == nil { log.Error("Invalid message:", val) return } dbVal = newVal.(map[string]interface{}) } else if dbOp == messaging.OP_DELETE && oldVal != nil { if oldVal == nil { log.Error("Invalid message:", val) return } dbVal = oldVal.(map[string]interface{}) } else { //update if newVal == nil { log.Error("Invalid message:", val) return } dbVal = newVal.(map[string]interface{}) } pld = pld.FromMap(dbVal) pld = utils.BlacklistFields([]string{db.TYPE_FIELD, db.APP_ID_FIELD}, pld) msg := messageBuilder.Build( dbOp, messaging.ORIGIN_API, pld, models.Options{}, opts.Type, opts.AppId, "", //TODO: token? ) msg.Topic = opts.Topic log.Info("Publishing database feed: ", strings.ToUpper(msg.Operation), opts, val, msg) publishArgs := []interface{}{msg} p.WsClient.Publish(opts.Topic, publishArgs, nil) }
func Recover() { e := recover() if e != nil { log.Error(e) } }