Exemplo n.º 1
0
func Main() {
	// Read in any environment variables we care about
	readEnv()

	var err error

	AWS_AUTH, err = aws.EnvAuth()
	if err != nil {
		panic(err.Error())
	}

	// Open database connection
	db, err := models.OpenDB("postgres", IRLMOJI_DBURI)
	if err != nil {
		log.Fatalf("Error opening database connection: %v", err.Error())
		return
	}
	if err = createAllTables(db); err != nil {
		log.Fatalf("Error creating database table: %v", err.Error())
	}

	// Start setting up martini
	m := martini.Classic()

	// Set up the middleware
	m.Use(gzip.All())
	m.Use(render.Renderer())
	m.Use(BackchannelAuth(IRLMOJI_API_BASIC_USER))

	// Inject the database
	m.Map(db)

	// Map the URL routes
	m.Get("/", HandleIndex)

	// User routes (see handlers_user.go)
	m.Get("/api/v1/users/current.json", HandleGetCurrentUser)
	m.Post("/api/v1/users/twitter.json", binding.Json(models.UserForm{}), HandleCreateUserByTwitter)

	// IRLMoji routes (see handlers_irlmoji.go)
	m.Get("/api/v1/timelines/home.json", binding.Form(Limit{}), HandleGetHomeTimeline)
	m.Get("/api/v1/timelines/user/username/:username.json", binding.Form(Limit{}), HandleGetUserTimeline)
	m.Get("/api/v1/timelines/emoji/:emoji.json", binding.Form(Limit{}), HandleGetEmojiTimeline)
	m.Get("/api/v1/irlmoji/id/:irlmojiId.json", binding.Form(Limit{}), HandleGetIRLMoji)
	m.Delete("/api/v1/irlmoji/id/:irlmojiId.json", HandleDeleteIRLMoji)
	m.Post("/api/v1/irlmoji.json", binding.Json(models.IRLMoji{}), HandleCreateIRLMoji)
	m.Post("/api/v1/irlmoji/id/:irlmojiId/heart.json", binding.Json(models.Heart{}), HandleToggleHeart)

	m.Post("/upload", HandleUpload)

	m.NotFound(HandleNotFound)

	m.Run()
}
Exemplo n.º 2
0
func NewServer(databaseName string) *martini.ClassicMartini {

	m := martini.Classic()
	c := config.GetConfig()

	// Setup middleware
	m.Use(db.DB(databaseName))
	m.Use(render.Renderer())
	m.Use(cors.Allow(&cors.Options{
		AllowOrigins:     []string{"http://localhost*"},
		AllowMethods:     []string{"POST", "GET"},
		AllowHeaders:     []string{"Origin"},
		ExposeHeaders:    []string{"Content-Length"},
		AllowCredentials: true,
	}))

	// Google OAuth
	m.Use(sessions.Sessions("my_session", sessions.NewCookieStore([]byte(c.Cookie_Auth),
		[]byte(c.Cookie_Enc))))

	m.Use(oauth2.Google(
		goauth2.Client(c.Client_Id, c.Client_Secret),
		goauth2.RedirectURL(c.OAuth_Callback),
		goauth2.Scope("email"),
	))

	// Static Assets
	m.Use(martini.Static("frontend/dist"))

	// Setup event routes
	m.Get(`/events`, controllers.GetAllEvents)
	m.Get(`/events/:id`, controllers.GetEvent)
	m.Post(`/events`, binding.Json(models.Event{}), binding.ErrorHandler, controllers.AddEvent)

	// Setup comment routes
	m.Get(`/events/:event_id/comments`, controllers.GetAllComments)
	m.Post(`/events/:event_id/comments`, binding.Json(models.Comment{}), binding.ErrorHandler, controllers.AddComment)

	// User route for Oauth
	m.Get(`/users`, oauth2.LoginRequired, controllers.GetLoggedInUser)

	// TODO Update, Delete for events
	//m.Put(`/events/:id`, UpdateEvent)
	//m.Delete(`/events/:id`, DeleteEvent)

	// Add the router action

	return m
}
Exemplo n.º 3
0
func runWebserver(c *cli.Context) {
	pipelinesConfigPath := c.String("config")
	var err error
	webserverConfig, err = config.LoadConfigFromYAMLFile(pipelinesConfigPath)
	if err != nil {
		log.Fatalln(err)
	}

	m := martini.Classic()
	m.Use(render.Renderer())
	m.Use(auth.Basic(webserverConfig.Auth.Username, webserverConfig.Auth.Password))
	m.Get("/", dashboardShowAll)
	m.Get("/tag/:filter", dashboardFilterByTag)
	m.Get("/db", getDatabase)
	m.Post("/upload", binding.Json(upload.BOSH{}), updateBOSH)
	m.Post("/upload/:reallyuuid/deployments/:name", binding.Json(upload.BOSHDeployment{}), updateDeployment)
	m.Post("/upload/:reallyuuid/deployments/:name/data/:label", binding.Json(upload.DeploymentData{}), updateDeploymentExtraData)
	m.Run()
}
func main() {
	m := martini.Classic()
	m.Use(Mongo())
	// add middle ware here for rendering the html
	m.Use(render.Renderer())

	m.Get("/attributes/:resource", getAttributes)
	m.Post("/attributes/:resource", binding.Json(attribute{}), addAttribute)

	service := utils.ServiceDescription{"attributes", "http://localhost:3000", "Service that manages the attributes available for each resource type"}
	fmt.Println(config.RegisterService(service))

	m.Run()
}
Exemplo n.º 5
0
func runDashboard(addr string, httpLogFile string) {
	log.Info("dashboard listening on addr: ", addr)
	m := martini.Classic()
	f, err := os.OpenFile(httpLogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		Fatal(err)
	}
	defer f.Close()

	m.Map(stdlog.New(f, "[martini]", stdlog.LstdFlags))
	binRoot, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		Fatal(err)
	}

	m.Use(martini.Static(filepath.Join(binRoot, "assets/statics")))
	m.Use(render.Renderer(render.Options{
		Directory:  filepath.Join(binRoot, "assets/template"),
		Extensions: []string{".tmpl", ".html"},
		Charset:    "UTF-8",
		IndentJSON: true,
	}))

	m.Use(cors.Allow(&cors.Options{
		AllowOrigins:     []string{"*"},
		AllowMethods:     []string{"POST", "GET", "DELETE", "PUT"},
		AllowHeaders:     []string{"Origin", "x-requested-with", "Content-Type", "Content-Range", "Content-Disposition", "Content-Description"},
		ExposeHeaders:    []string{"Content-Length"},
		AllowCredentials: false,
	}))

	m.Get("/api/server_groups", apiGetServerGroupList)
	m.Get("/api/overview", apiOverview)

	m.Get("/api/redis/:addr/stat", apiRedisStat)
	m.Get("/api/redis/:addr/:id/slotinfo", apiGetRedisSlotInfo)
	m.Get("/api/redis/group/:group_id/:slot_id/slotinfo", apiGetRedisSlotInfoFromGroupId)

	m.Put("/api/server_groups", binding.Json(models.ServerGroup{}), apiAddServerGroup)
	m.Put("/api/server_group/(?P<id>[0-9]+)/addServer", binding.Json(models.Server{}), apiAddServerToGroup)
	m.Delete("/api/server_group/(?P<id>[0-9]+)", apiRemoveServerGroup)

	m.Put("/api/server_group/(?P<id>[0-9]+)/removeServer", binding.Json(models.Server{}), apiRemoveServerFromGroup)
	m.Get("/api/server_group/(?P<id>[0-9]+)", apiGetServerGroup)
	m.Post("/api/server_group/(?P<id>[0-9]+)/promote", binding.Json(models.Server{}), apiPromoteServer)

	m.Get("/api/migrate/status", apiMigrateStatus)
	m.Get("/api/migrate/tasks", apiGetMigrateTasks)
	m.Post("/api/migrate", binding.Json(migrateTaskForm{}), apiDoMigrate)

	m.Post("/api/rebalance", apiRebalance)

	m.Get("/api/slot/list", apiGetSlots)
	m.Get("/api/slot/:id", apiGetSingleSlot)
	m.Post("/api/slots/init", apiInitSlots)
	m.Get("/api/slots", apiGetSlots)
	m.Post("/api/slot", binding.Json(RangeSetTask{}), apiSlotRangeSet)
	m.Get("/api/proxy/list", apiGetProxyList)
	m.Get("/api/proxy/debug/vars", apiGetProxyDebugVars)
	m.Post("/api/proxy", binding.Json(models.ProxyInfo{}), apiSetProxyStatus)

	m.Get("/api/action/gc", apiActionGC)
	m.Get("/api/force_remove_locks", apiForceRemoveLocks)
	m.Get("/api/remove_fence", apiRemoveFence)

	m.Get("/slots", pageSlots)
	m.Get("/", func(r render.Render) {
		r.Redirect("/admin")
	})
	zkBuilder := utils.NewConnBuilder(globalEnv.NewZkConn)
	safeZkConn = zkBuilder.GetSafeConn()
	unsafeZkConn = zkBuilder.GetUnsafeConn()

	// create temp node in ZK
	if err := createDashboardNode(); err != nil {
		log.Fatal(err) // do not release dashborad node here
	}

	// create long live migrate manager
	globalMigrateManager = NewMigrateManager(safeZkConn, globalEnv.ProductName())

	go func() {
		c := getProxySpeedChan()
		for {
			atomic.StoreInt64(&proxiesSpeed, <-c)
		}
	}()

	m.RunOnAddr(addr)
}
Exemplo n.º 6
0
func main() {
	m := martini.Classic()

	m.Use(render.Renderer())

	log.Println("Tentando se concectar na base")
	//Configure aqui seu usuário e senha do Mysql (Deve ter permissão para crear shchema e tables,
	//para inicar a base aconselho usar root )
	db, err := sql.Open("mysql", "root:root@tcp(localhost:3306)/")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	log.Println("Criando database crud caso não exista")
	_, err = db.Exec("CREATE DATABASE IF NOT EXISTS crud")
	if err != nil {
		panic(err)
	}

	log.Println("Usando schema crud")
	_, err = db.Exec("USE crud")
	if err != nil {
		panic(err)
	}

	log.Println("Criando tabela Product caso não exita")
	_, err = db.Exec(`CREATE TABLE IF NOT EXISTS crud.product (
						id_product INT NOT NULL AUTO_INCREMENT,
				  		name VARCHAR(200) NULL,
				  		price DECIMAL(10,2) NULL,
				  		expiration_date DATE NULL,
				  		PRIMARY KEY (id_product));`)
	if err != nil {
		log.Fatal(err)
	}

	//Controller para adicionar produto
	m.Post("/product/add/data.json", binding.Json(Product{}), func(product Product, r render.Render) {
		if err != nil {
			log.Println(err.Error())
			r.JSON(500, map[string]interface{}{"status": "error", "message": err.Error()})
			return
		}
		stmt, err := db.Prepare(`Insert Into product
					 (name, price, expiration_date)
					 Values (?, ?, ?)`)
		if err != nil {
			r.JSON(500, map[string]interface{}{"status": "error", "message": err.Error()})
			return
		}
		defer stmt.Close()

		_, err = stmt.Exec(product.Name, product.Price, product.Expiration_date)

		if err != nil {
			r.JSON(500, map[string]interface{}{"status": "error", "message": err.Error()})
			return
		}

		r.JSON(200, map[string]interface{}{"status": "success"})
	})

	//Controler para listar todos os produtos
	m.Get("/product/list", func(r render.Render) {
		rows, err := db.Query(`Select id_product, name, price, expiration_date
				       From product`)
		if err != nil {
			r.JSON(500, map[string]interface{}{"status": "error", "message": err.Error()})
			return
		}
		defer rows.Close()

		res := []Product{}
		for rows.Next() {
			p := Product{}
			rows.Scan(&p.Id_product, &p.Name, &p.Price, &p.Expiration_date)
			res = append(res, p)
		}

		r.JSON(200, map[string]interface{}{"status": "success", "results": res})
	})

	//Controller para procurar por ID
	m.Get("/product/search/:id", func(params martini.Params, r render.Render) {

		stmt, err := db.Prepare(`Select id_product, name, price, expiration_date
					 From product
					 Where id_product = ?`)
		if err != nil {
			r.JSON(500, map[string]interface{}{"status": "error", "message": err.Error()})
			return
		}
		defer stmt.Close()

		p := Product{}
		err = stmt.QueryRow(params["id"]).Scan(&p.Id_product, &p.Name, &p.Price, &p.Expiration_date)

		if err != nil {
			r.JSON(500, map[string]interface{}{"status": "error", "message": err.Error()})
			return
		}

		r.JSON(200, map[string]interface{}{"status": "success", "results": p})
	})

	//Controller para buscar por Nome do produto
	m.Get("/product/searchByName/:name", func(params martini.Params, r render.Render) {

		stmt, err := db.Prepare(`Select id_product, name, price, expiration_date
					 From product
					 Where name like ?`)
		if err != nil {
			log.Println(err.Error())
			r.JSON(500, map[string]interface{}{"status": "error", "message": err.Error()})
			return
		}
		defer stmt.Close()

		rows, err := stmt.Query(params["name"])

		res := []Product{}

		if err != nil {
			log.Println(err.Error())
			r.JSON(500, map[string]interface{}{"status": "error", "message": err.Error()})
			return
		}

		for rows.Next() {
			p := Product{}
			rows.Scan(&p.Id_product, &p.Name, &p.Price, &p.Expiration_date)
			res = append(res, p)
		}

		r.JSON(200, map[string]interface{}{"status": "success", "results": res})
	})

	//Controller para editar produto
	m.Put("/product/:id/data.json", binding.Json(Product{}), func(params martini.Params, product Product, r render.Render) {
		stmt, err := db.Prepare(`Update product
					 Set name=?, price=?, expiration_date=?
					 Where id_product=?`)
		if err != nil {
			r.JSON(500, map[string]interface{}{"status": "error", "message": err.Error()})
			return
		}
		defer stmt.Close()

		_, err = stmt.Exec(product.Name, product.Price, product.Expiration_date, params["id"])

		if err != nil {
			r.JSON(500, map[string]interface{}{"status": "error", "message": err.Error()})
			return
		}
		r.JSON(200, map[string]interface{}{"status": "success"})
	})

	//Controller para excluir produto
	m.Delete("/product/delete/:id", func(params martini.Params, r render.Render) {
		stmt, err := db.Prepare(`Delete From product
					 Where id_product = ?`)
		if err != nil {
			r.JSON(500, map[string]interface{}{"status": "error", "message": err.Error()})
			return
		}
		defer stmt.Close()
		_, err = stmt.Exec(params["id"])
		if err != nil {
			r.JSON(500, map[string]interface{}{"status": "error", "message": err.Error()})
			return
		}

		r.JSON(200, map[string]interface{}{"status": "success"})
	})

	//Mapeamento de arquivos estáticos(HTML's, JS, CSS...)
	m.Use(martini.Static("static/"))

	m.Run()

}
Exemplo n.º 7
0
func main() {
	// load env
	err := godotenv.Load()
	if err != nil {
		panic(err)
	}

	// startup server
	m := martini.Classic()
	m.Use(render.Renderer())
	m.Use(auth.Basic(os.Getenv("USERNAME"), os.Getenv("PASSWORD")))
	m.Use(dbConnect())

	// index route
	m.Get("/entries", func(r render.Render) {
		r.JSON(200, entry.All(dbMap))
	})

	// get latests entry
	m.Get("/entries/latest", binding.Json(entry.Entry{}),
		func(params martini.Params, e entry.Entry, r render.Render) {
			r.JSON(200, entry.Latest(dbMap))
		})

	// index route
	m.Get("/entries/:id", func(params martini.Params, r render.Render) {
		e, err := dbMap.Get(entry.Entry{}, params["id"])
		if err != nil || e == nil {
			r.JSON(404, "Entry not found")
			return
		}
		r.JSON(200, e)
	})

	// add route
	m.Post("/entries", binding.Json(entry.Entry{}), func(params martini.Params, e entry.Entry, r render.Render) {
		err := dbMap.Insert(&e)
		if err != nil {
			r.JSON(404, "Unable to update entry.")
			return
		}
		r.JSON(200, e)
	})

	// add route
	m.Delete("/entries", func(r render.Render) {
		err := dbMap.TruncateTables()
		if err != nil {
			r.JSON(404, "Unable to remove all entries.")
			return
		}
		r.JSON(202, nil)
	})

	// replace route
	m.Put("/entries/:id", binding.Json(entry.Entry{}), func(params martini.Params, e entry.Entry, r render.Render) {
		en, err := dbMap.Get(entry.Entry{}, params["id"])

		if err != nil || en == nil {
			r.JSON(404, "Entry not found")
			return
		}
		//replace existing
		_, err = dbMap.Update(&e)
		if err != nil {
			r.JSON(404, "Unable to update entry.")
			return
		}
		r.JSON(200, e)
	})

	// initialize server
	m.Run()
}
Exemplo n.º 8
0
func runDashboard(addr string, httpLogFile string) {
	log.Infof("dashboard listening on addr: %s", addr)
	m := martini.Classic()
	f, err := os.OpenFile(httpLogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		log.PanicErrorf(err, "open http log file failed")
	}
	defer f.Close()

	m.Map(stdlog.New(f, "[martini]", stdlog.LstdFlags))
	binRoot, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		log.PanicErrorf(err, "get binroot path failed")
	}

	m.Use(martini.Static(filepath.Join(binRoot, "assets/statics")))
	m.Use(render.Renderer(render.Options{
		Directory:  filepath.Join(binRoot, "assets/template"),
		Extensions: []string{".tmpl", ".html"},
		Charset:    "UTF-8",
		IndentJSON: true,
	}))

	m.Use(cors.Allow(&cors.Options{
		AllowOrigins:     []string{"*"},
		AllowMethods:     []string{"POST", "GET", "DELETE", "PUT"},
		AllowHeaders:     []string{"Origin", "x-requested-with", "Content-Type", "Content-Range", "Content-Disposition", "Content-Description"},
		ExposeHeaders:    []string{"Content-Length"},
		AllowCredentials: false,
	}))

	m.Get("/api/server_groups", apiGetServerGroupList)
	m.Get("/api/overview", apiOverview)

	m.Get("/api/redis/:addr/stat", apiRedisStat)
	m.Get("/api/redis/:addr/:id/slotinfo", apiGetRedisSlotInfo)
	m.Get("/api/redis/group/:group_id/:slot_id/slotinfo", apiGetRedisSlotInfoFromGroupId)

	m.Put("/api/server_groups", binding.Json(models.ServerGroup{}), apiAddServerGroup)
	m.Put("/api/server_group/(?P<id>[0-9]+)/addServer", binding.Json(models.Server{}), apiAddServerToGroup)
	m.Delete("/api/server_group/(?P<id>[0-9]+)", apiRemoveServerGroup)

	m.Put("/api/server_group/(?P<id>[0-9]+)/removeServer", binding.Json(models.Server{}), apiRemoveServerFromGroup)
	m.Get("/api/server_group/(?P<id>[0-9]+)", apiGetServerGroup)
	m.Post("/api/server_group/(?P<id>[0-9]+)/promote", binding.Json(models.Server{}), apiPromoteServer)

	m.Get("/api/migrate/status", apiMigrateStatus)
	m.Get("/api/migrate/tasks", apiGetMigrateTasks)
	m.Post("/api/migrate", binding.Json(migrateTaskForm{}), apiDoMigrate)

	m.Post("/api/rebalance", apiRebalance)

	m.Get("/api/slot/list", apiGetSlots)
	m.Get("/api/slot/:id", apiGetSingleSlot)
	m.Post("/api/slots/init", apiInitSlots)
	m.Get("/api/slots", apiGetSlots)
	m.Post("/api/slot", binding.Json(RangeSetTask{}), apiSlotRangeSet)
	m.Get("/api/proxy/list", apiGetProxyList)
	m.Get("/api/proxy/debug/vars", apiGetProxyDebugVars)
	m.Post("/api/proxy", binding.Json(models.ProxyInfo{}), apiSetProxyStatus)

	m.Get("/api/action/gc", apiActionGC)
	m.Get("/api/force_remove_locks", apiForceRemoveLocks)
	m.Get("/api/remove_fence", apiRemoveFence)
	//m.Get("/api/action/gc", apiActionGC)
	m.Get("/slots", pageSlots)
	m.Get("/", func(r render.Render) {
		r.Redirect("/admin")
	})
	//check key slot correspondence
	m.Get("/api/keyslot/(?P<key>.+)", apiKeySlot)
	m.Get("/api/remove_migration", apiRemoveMigration)
	m.Get("/api/remove_migration_fail", apiRemoveMigrationForFail)
	m.Get("/api/proxy/slowop", apiGetProxySlowop)
	zkBuilder := utils.NewConnBuilder(globalEnv.NewZkConn)
	safeZkConn = zkBuilder.GetSafeConn()
	unsafeZkConn = zkBuilder.GetUnsafeConn()

	// create temp node in ZK
	if err := createDashboardNode(); err != nil {
		log.WarnErrorf(err, "create zk node failed") // do not release dashborad node here
	}

	// create long live migrate manager
	globalMigrateManager = NewMigrateManager(safeZkConn, globalEnv.ProductName())

	go func() {
		tick := time.Tick(time.Second)
		var lastCnt, qps int64
		for _ = range tick {
			cnt := getAllProxyOps()
			if cnt > 0 {
				qps = cnt - lastCnt
				lastCnt = cnt
			} else {
				qps = 0
			}
			atomic.StoreInt64(&proxiesSpeed, qps)
		}
	}()

	go func() {
		for {
			err := models.ActionGC(safeZkConn, globalEnv.ProductName(), models.GC_TYPE_SEC, 60*60*24)
			if err != nil {
				log.Warnf("clean actions failed %+v", err)
			}
			time.Sleep(60 * 60 * 24 * time.Second)
		}
	}()

	m.RunOnAddr(addr)
}
Exemplo n.º 9
0
func runDashboard(addr string, httpLogFile string) {
	log.Info("dashboard start listen in addr:", addr)
	m := martini.Classic()
	f, err := os.OpenFile(httpLogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		log.Fatalf("error opening file: %v", err)
	}
	defer f.Close()
	m.Map(stdlog.New(f, "[martini]", stdlog.LstdFlags))

	m.Use(martini.Static("assets/statics"))
	m.Use(render.Renderer(render.Options{
		Directory:  "assets/template",
		Extensions: []string{".tmpl", ".html"},
		Charset:    "UTF-8",
		IndentJSON: true,
	}))

	m.Use(cors.Allow(&cors.Options{
		AllowOrigins:     []string{"*"},
		AllowMethods:     []string{"POST", "GET", "DELETE", "PUT"},
		AllowHeaders:     []string{"Origin", "x-requested-with", "Content-Type", "Content-Range", "Content-Disposition", "Content-Description"},
		ExposeHeaders:    []string{"Content-Length"},
		AllowCredentials: false,
	}))

	m.Get("/api/server_groups", apiGetServerGroupList)
	m.Get("/api/overview", apiOverview)

	m.Get("/api/redis/:addr/stat", apiRedisStat)
	m.Get("/api/redis/:addr/:id/slotinfo", apiGetRedisSlotInfo)
	m.Get("/api/redis/group/:group_id/:slot_id/slotinfo", apiGetRedisSlotInfoFromGroupId)

	m.Put("/api/server_groups", binding.Json(models.ServerGroup{}), apiAddServerGroup)
	m.Put("/api/server_group/(?P<id>[0-9]+)/addServer", binding.Json(models.Server{}), apiAddServerToGroup)
	m.Delete("/api/server_group/(?P<id>[0-9]+)", apiRemoveServerGroup)

	m.Put("/api/server_group/(?P<id>[0-9]+)/removeServer", binding.Json(models.Server{}), apiRemoveServerFromGroup)
	m.Get("/api/server_group/(?P<id>[0-9]+)", apiGetServerGroup)
	m.Post("/api/server_group/(?P<id>[0-9]+)/promote", binding.Json(models.Server{}), apiPromoteServer)

	m.Get("/api/migrate/status", apiMigrateStatus)
	m.Get("/api/migrate/tasks", apiGetMigrateTasks)
	m.Delete("/api/migrate/pending_task/:id/remove", apiRemovePendingMigrateTask)
	m.Delete("/api/migrate/task/:id/stop", apiStopMigratingTask)
	m.Post("/api/migrate", binding.Json(MigrateTaskForm{}), apiDoMigrate)

	m.Post("/api/rebalance", apiRebalance)
	m.Get("/api/rebalance/status", apiRebalanceStatus)

	m.Get("/api/slot/list", apiGetSlots)
	m.Get("/api/slots", apiGetSlots)
	m.Post("/api/slot", binding.Json(RangeSetTask{}), apiSlotRangeSet)
	m.Get("/api/proxy/list", apiGetProxyList)
	m.Get("/api/proxy/debug/vars", apiGetProxyDebugVars)
	m.Post("/api/proxy", binding.Json(models.ProxyInfo{}), apiSetProxyStatus)

	m.Get("/slots", pageSlots)
	m.Get("/", func(r render.Render) {
		r.Redirect("/admin")
	})

	go func() {
		c := getProxySpeedChan()
		for {
			atomic.StoreInt64(&proxiesSpeed, <-c)
		}
	}()

	go migrateTaskWorker()

	m.RunOnAddr(addr)
}