Example #1
0
func handleSyncMaster(c *gin.Context, data string) {
	if !conf.IsMasterNode() {
		Error(c, BAD_REQUEST, "invalid req type for slave node: "+NODE_REQUEST_TYPE_SYNCMASTER)
		return
	}

	// no need to hold the locker(confWriteMux) to avoid dead-lock, slave will eventually be consistent with master,
	//	confWriteMux.Lock()
	//	defer confWriteMux.Unlock()

	history, err := models.GetAllConfigUpdateHistory(nil)
	if err != nil {
		Error(c, SERVER_ERROR, err.Error())
		return
	}

	memConfMux.RLock()
	webHooks := memConfGlobalWebHooks
	for _, hooks := range memConfAppWebHooks {
		webHooks = append(webHooks, hooks...)
	}
	resData, _ := json.Marshal(syncAllDataT{
		Nodes:       memConfNodes,
		Users:       memConfUsers,
		Apps:        memConfApps,
		WebHooks:    webHooks,
		Configs:     memConfRawConfigs,
		DataVersion: memConfDataVersion,
		ConfHistory: history,
	})
	memConfMux.RUnlock()

	Success(c, string(resData))
}
Example #2
0
func init() {
	var err error
	nodeAuthToken := jwt.New(jwt.SigningMethodHS256)
	if nodeAuthString, err = nodeAuthToken.SignedString([]byte(conf.NodeAuth)); err != nil {
		log.Panicf("Failed to init node auth token: %s", err.Error())
	}

	checkNodeValidity()
	loadAllData()
	initNodeData()

	if !conf.IsMasterNode() {
		if err = slaveCheckMaster(); err != nil {
			log.Printf("slave node failed to check master: %s", err.Error())
			os.Exit(1)
		}

		go func() {
			for {
				time.Sleep(time.Duration(conf.CheckMasterInerval) * time.Second)
				go slaveCheckMaster()
			}
		}()
	}
}
Example #3
0
func init() {
	var dsn, driver string
	var err error

	if conf.IsMasterNode() {
		dsn = filepath.Join(conf.SqliteDir, conf.SqliteFileName)
	} else {
		dsn, err = getSlaveSqliteFile()
		if err != nil {
			log.Panicf("Failed to generate sqlie lite file: %s", err.Error())
		}
	}
	driver = "sqlite3"

	initDBEngine(driver, dsn)
}
Example #4
0
func checkNodeValidity() {
	nodes, err := models.GetAllNode(nil)
	if err != nil {
		log.Panicf("failed to check node validity: %s" + err.Error())
	}

	for _, node := range nodes {
		if conf.IsMasterNode() {
			// only one master in cluster
			if node.Type == models.NODE_TYPE_MASTER && node.URL != conf.ClientAddr {
				if err := models.DeleteDBModel(nil, node); err != nil {
					log.Panicf("failed to check node validity: %s" + err.Error())
				}
				break
			}
		}
	}
}
Example #5
0
func handleSlaveCheckMaster(c *gin.Context, data string) {
	if !conf.IsMasterNode() {
		Error(c, BAD_REQUEST, "invalid req type for slave node: "+NODE_REQUEST_TYPE_CHECKMASTER)
		return
	}

	node := &models.Node{}
	if err := json.Unmarshal([]byte(data), node); err != nil {
		Error(c, BAD_REQUEST, "bad req body format")
		return
	}

	confWriteMux.Lock()
	defer confWriteMux.Unlock()

	oldNode := memConfNodes[node.URL]
	node.LastCheckUTC = utils.GetNowSecond()
	if oldNode == nil {
		if err := models.InsertRow(nil, node); err != nil {
			Error(c, SERVER_ERROR, err.Error())
			return
		}
	} else {
		if err := models.UpdateDBModel(nil, node); err != nil {
			Error(c, SERVER_ERROR, err.Error())
			return
		}
	}

	memConfMux.Lock()
	memConfNodes[node.URL] = node
	bs, _ := json.Marshal(memConfDataVersion)
	memConfMux.Unlock()

	go masterSyncNodeToSlave(node)

	Success(c, string(bs))
}
Example #6
0
func ConfWriteCheck(c *gin.Context) {
	if !conf.IsMasterNode() {
		Error(c, NOT_PERMITTED, "You can not update config data as you connecting to slave node,")
		c.Abort()
	}
}
Example #7
0
func ClientConf(c *gin.Context) {
	clientData := &ClientData{
		AppKey:     c.Query("app_key"),
		OSType:     c.Query("os_type"),
		OSVersion:  c.Query("os_version"),
		AppVersion: c.Query("app_version"),
		Ip:         c.Query("ip"),
		Lang:       c.Query("lang"),
		DeviceId:   c.Query("device_id"),
		DataSign:   c.Query("data_sign"),
		TimeZone:   c.Query("timezone"),
		NetWork:    c.Query("network"),
	}

	if clientData.AppKey == "" {
		memConfMux.RLock()
		app := memConfAppsByName[c.Query("app")]
		memConfMux.RUnlock()

		if app != nil {
			clientData.AppKey = app.Key
			clientData.OSType = "ios"
			clientData.OSVersion = c.Query("osv")
			clientData.AppVersion = c.Query("v")
			clientData.DeviceId = c.Query("ida")
			clientData.Ip = c.Request.RemoteAddr
		}

		clientData = uniformClientParams(clientData)
		sendChanAsync(clientQueryParamCh, clientData)
		setClientData(c, clientData)

		c.JSON(http.StatusOK, getAppMatchConf(clientData.AppKey, clientData))
		return
	}

	clientData = uniformClientParams(clientData)
	sendChanAsync(clientQueryParamCh, clientData)
	setClientData(c, clientData)

	memConfMux.RLock()
	if !conf.IsMasterNode() && conf.DataExpires > 0 {
		if memConfNodes[conf.ClientAddr].LastCheckUTC < utils.GetNowSecond()-conf.DataExpires {
			memConfMux.RUnlock()
			Error(c, DATA_EXPIRED)
			return
		}
	}

	nodes := []string{}
	nodes = make([]string, len(memConfNodes))
	ix := 0
	for _, node := range memConfNodes {
		nodes[ix] = node.URL
		ix++
	}

	needConf := memConfApps[clientData.AppKey] != nil && clientData.DataSign != memConfApps[clientData.AppKey].DataSign
	memConfMux.RUnlock()

	if needConf {
		var dataSign string
		configs := getAppMatchConf(clientData.AppKey, clientData)
		if len(configs) > 0 {
			memConfMux.RLock()
			dataSign = memConfApps[clientData.AppKey].DataSign
			memConfMux.RUnlock()
		}

		Success(c, map[string]interface{}{
			"nodes":     nodes,
			"configs":   configs,
			"data_sign": dataSign,
		})
	} else {
		Success(c, map[string]interface{}{
			"data_sign": clientData.DataSign,
			"nodes":     nodes,
		})
	}

	return
}
Example #8
0
func handleSlaveSyncUpdateData(c *gin.Context, data string) {
	if conf.IsMasterNode() {
		Error(c, BAD_REQUEST, "invalid req type for master node: "+NODE_REQUEST_TYPE_SYNCSLAVE)
		return
	}

	syncData := &syncDataT{}
	err := json.Unmarshal([]byte(data), syncData)
	if err != nil {
		Error(c, BAD_REQUEST, "bad req body format")
		return
	}

	confWriteMux.Lock()
	defer confWriteMux.Unlock()

	if syncData.Kind != NODE_REQUEST_SYNC_TYPE_NODE {
		if memConfDataVersion.Version+1 != syncData.DataVersion.Version {
			Error(c, DATA_VERSION_ERROR, "slave node data version [%d] error for master data version [%d]", memConfDataVersion.Version, syncData.DataVersion.Version)
			return
		}
		if memConfDataVersion.Sign != syncData.DataVersion.OldSign {
			Error(c, DATA_VERSION_ERROR, "slave node's data sign [%s] not equal master node's old data sign [%s]", memConfDataVersion.Sign, syncData.DataVersion.OldSign)
			return
		}
	}

	switch syncData.Kind {
	case NODE_REQUEST_SYNC_TYPE_USER:
		user := &models.User{}
		if err = json.Unmarshal([]byte(syncData.Data), user); err != nil {
			Error(c, BAD_REQUEST, "bad data format for user model")
			return
		}
		if _, err = updateUser(user, syncData.DataVersion); err != nil {
			Error(c, SERVER_ERROR, err.Error())
			return
		}

	case NODE_REQUEST_SYNC_TYPE_APP:
		app := &models.App{}
		if err = json.Unmarshal([]byte(syncData.Data), app); err != nil {
			Error(c, BAD_REQUEST, "bad data format for app model")
			return
		}
		if _, err = updateApp(app, syncData.DataVersion, nil); err != nil {
			Error(c, SERVER_ERROR, err.Error())
			return
		}

	case NODE_REQUEST_SYNC_TYPE_WEBHOOK:
		hook := &models.WebHook{}
		if err = json.Unmarshal([]byte(syncData.Data), hook); err != nil {
			Error(c, BAD_REQUEST, "bad data format for webHook model")
			return
		}
		if _, err = updateWebHook(hook, syncData.DataVersion); err != nil {
			Error(c, SERVER_ERROR, err.Error())
			return
		}

	case NODE_REQUEST_SYNC_TYPE_CONFIG:
		config := &models.Config{}
		if err = json.Unmarshal([]byte(syncData.Data), config); err != nil {
			Error(c, BAD_REQUEST, "bad data format for user model")
			return
		}
		if _, err = updateConfig(config, syncData.OpUserKey, syncData.DataVersion, nil); err != nil {
			Error(c, SERVER_ERROR, err.Error())
			return
		}

	case NODE_REQUEST_SYNC_TYPE_NODE:
		node := &models.Node{}
		if err = json.Unmarshal([]byte(syncData.Data), node); err != nil {
			Error(c, BAD_REQUEST, "bad data format for node model")
			return
		}

		if memConfNodes[node.URL] == nil {
			if err := models.InsertRow(nil, node); err != nil {
				Error(c, SERVER_ERROR, err.Error())
				return
			}
		} else {
			if err := models.UpdateDBModel(nil, node); err != nil {
				Error(c, SERVER_ERROR, err.Error())
				return
			}
		}

		memConfMux.Lock()
		memConfNodes[node.URL] = node
		memConfMux.Unlock()

		Success(c, nil)
		return

	case NODE_REQUEST_SYNC_TYPE_CLONE:
		data := &cloneData{}
		if err := json.Unmarshal([]byte(syncData.Data), data); err != nil {
			Error(c, BAD_REQUEST, "bad data format for clone app")
			return
		}

		if err := cloneConfigs(data.App, data.Configs, syncData.OpUserKey); err != nil {
			Error(c, SERVER_ERROR, err.Error())
			return
		}

	default:
		Error(c, BAD_REQUEST, "unknown node data sync type: "+syncData.Kind)
		return
	}

	masterNode := getMasterNode()
	masterNode.DataVersion = syncData.DataVersion
	bs, _ := json.Marshal(syncData.DataVersion)
	masterNode.DataVersionStr = string(bs)
	if err = models.UpdateDBModel(nil, &masterNode); err != nil {
		memConfMux.Lock()
		memConfNodes[masterNode.URL] = &masterNode
		memConfMux.Unlock()
	}

	Success(c, nil)
}
Example #9
0
func main() {
	wd, _ := os.Getwd()
	pidFile, err := os.OpenFile(filepath.Join(wd, "instafig.pid"), os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		logger.Fatal(map[string]interface{}{
			"type":  "start_error",
			"error": fmt.Sprintf("failed to create pid file: %s", err.Error()),
		})
		log.Printf("failed to create pid file: %s", err.Error())
		os.Exit(1)
	}
	pidFile.WriteString(strconv.Itoa(os.Getpid()))
	pidFile.Close()

	if conf.DebugMode {
		gin.SetMode(gin.DebugMode)
	} else {
		gin.SetMode(gin.ReleaseMode)
	}

	ginIns := gin.New()
	ginIns.Use(gin.Recovery())
	if conf.DebugMode {
		ginIns.Use(gin.Logger())
	}

	if conf.RequestLogEnable {
		ginIns.Use(requestLoggerHandler())
	}

	if conf.WebDebugMode {
		// static
		ginIns.Static("/web", "./web")
	} else {
		// bin static
		ginIns.GET("/web/*file",
			func(c *gin.Context) {
				fileName := c.Param("file")
				if fileName == "/" {
					fileName = "/index.html"
				}
				data, err := Asset("web" + fileName)
				if err != nil {
					c.String(http.StatusNotFound, err.Error())
					return
				}

				switch {
				case strings.LastIndex(fileName, ".html") == len(fileName)-5:
					c.Header("Content-Type", "text/html; charset=utf-8")
				case strings.LastIndex(fileName, ".css") == len(fileName)-4:
					c.Header("Content-Type", "text/css")
				}
				c.String(http.StatusOK, string(data))
			})
	}

	// misc api
	miscAPIGroup := ginIns.Group("/misc")
	{
		miscAPIGroup.GET("/version", VersionHandler)
	}

	// client api
	clientAPIGroup := ginIns.Group("/client")
	{
		clientAPIGroup.GET("/config", StatisticHandler, ClientConf)
	}
	// compatible with old awconfig
	ginIns.GET("/conf", StatisticHandler, ClientConf)

	// op api
	opAPIGroup := ginIns.Group("/op")
	{
		opAPIGroup.POST("/login", Login)
		opAPIGroup.POST("/logout", OpAuth, Logout)

		opAPIGroup.GET("/users/:page/:count", InitUserCheck, OpAuth, GetUsers)
		opAPIGroup.POST("/user", OpAuth, ConfWriteCheck, NewUser, UpdateMasterLastDataUpdateUTC)
		opAPIGroup.PUT("/user", OpAuth, ConfWriteCheck, UpdateUser, UpdateMasterLastDataUpdateUTC)
		opAPIGroup.PUT("/user/status", OpAuth, ConfWriteCheck, UpdateUserStatus, UpdateMasterLastDataUpdateUTC)
		opAPIGroup.PUT("/user/passcode", OpAuth, ConfWriteCheck, UpdateUserPassCode, UpdateMasterLastDataUpdateUTC)
		opAPIGroup.POST("/user/init", ConfWriteCheck, InitUser, UpdateMasterLastDataUpdateUTC)
		opAPIGroup.GET("/user/info", OpAuth, GetLoginUserInfo)

		opAPIGroup.GET("/apps/user/:user_key", OpAuth, GetApps)
		opAPIGroup.GET("/apps/all/:page/:count", OpAuth, GetAllApps)
		opAPIGroup.GET("/app/:app_key", OpAuth, GetApp)
		if conf.IsMasterNode() {
			opAPIGroup.GET("/apps/search", OpAuth, SearchApps)
			opAPIGroup.GET("/apps/search/hint", OpAuth, SearchAppsHint)
		}
		opAPIGroup.POST("/app", OpAuth, ConfWriteCheck, NewApp, UpdateMasterLastDataUpdateUTC)
		opAPIGroup.PUT("/app", OpAuth, ConfWriteCheck, UpdateApp, UpdateMasterLastDataUpdateUTC)
		opAPIGroup.POST("/app/clone", OpAuth, ConfWriteCheck, CloneAppConfigs, UpdateMasterLastDataUpdateUTC)

		opAPIGroup.GET("/webhooks/global", OpAuth, GetGlobalWebHooks)
		opAPIGroup.GET("/webhooks/app/:app_key", OpAuth, GetAppWebHooks)
		opAPIGroup.POST("/webhook", OpAuth, ConfWriteCheck, NewWebHook, UpdateMasterLastDataUpdateUTC)
		opAPIGroup.PUT("/webhook", OpAuth, ConfWriteCheck, UpdateWebHook)

		opAPIGroup.GET("/configs/:app_key", OpAuth, GetConfigs)
		opAPIGroup.POST("/config", OpAuth, ConfWriteCheck, NewConfig, UpdateMasterLastDataUpdateUTC)
		opAPIGroup.PUT("/config", OpAuth, ConfWriteCheck, UpdateConfig, UpdateMasterLastDataUpdateUTC)
		opAPIGroup.GET("/config/history/:config_key", OpAuth, GetConfigUpdateHistory)
		opAPIGroup.GET("/config/apphistory/:app_key/:page/:count", OpAuth, GetAppConfigUpdateHistory)
		opAPIGroup.GET("/config/userhistory/:user_key/:page/:count", OpAuth, GetConfigUpdateHistoryOfUser)
		opAPIGroup.GET("/config/by/:config_key", OpAuth, GetConfigByKey)

		opAPIGroup.GET("/nodes", OpAuth, GetNodes)

		opAPIGroup.GET("/client/params/:symbol", OpAuth, GetClientSymbols)

		// for statistics
		opAPIGroup.GET("/stat/latest-config-device-count/:app_key", OpAuth, StatCheck, GetDeviceCountOfAppLatestConfig)
		opAPIGroup.GET("/stat/app-config-response/:app_key", OpAuth, StatCheck, GetAppConfigResponseData)
		opAPIGroup.GET("/stat/node-config-response/:node_url", OpAuth, StatCheck, GetNodeConfigResponseData)
	}

	ginInsNode := gin.New()
	if conf.DebugMode {
		ginInsNode.Use(gin.Logger())
	}
	ginInsNode.Use(gin.Recovery())
	ginInsNode.POST("/node/req/:req_type", NodeRequestHandler)

	err = gracehttp.Serve(
		&http.Server{Addr: fmt.Sprintf(":%d", conf.Port), Handler: ginIns},
		&http.Server{Addr: conf.NodeAddr, Handler: ginInsNode})
	if err != nil {
		logger.Fatal(map[string]interface{}{
			"type":  "start_error",
			"error": err.Error(),
		})
		log.Printf("fatal error: %s", err.Error())
	}
}