mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
removed old code
This commit is contained in:
parent
fcdcd63dc7
commit
e750080f00
2
grafana
2
grafana
@ -1 +1 @@
|
|||||||
Subproject commit 4b5eadf7b59898e6622a75e0a57081103dd78b2a
|
Subproject commit 79beefe57c608b3cd933c5b1f772c8707731a64c
|
@ -1,84 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"html/template"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/gorilla/sessions"
|
|
||||||
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/components"
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/configuration"
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/log"
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/models"
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/setting"
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/stores"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HttpServer struct {
|
|
||||||
port string
|
|
||||||
shutdown chan bool
|
|
||||||
store stores.Store
|
|
||||||
renderer *components.PhantomRenderer
|
|
||||||
router *gin.Engine
|
|
||||||
cfg *configuration.Cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
var sessionStore = sessions.NewCookieStore([]byte("something-very-secret"))
|
|
||||||
|
|
||||||
func NewHttpServer(cfg *configuration.Cfg, store stores.Store) *HttpServer {
|
|
||||||
self := &HttpServer{}
|
|
||||||
self.cfg = cfg
|
|
||||||
self.port = cfg.Http.Port
|
|
||||||
self.store = store
|
|
||||||
self.renderer = &components.PhantomRenderer{ImagesDir: "data/png", PhantomDir: "_vendor/phantomjs"}
|
|
||||||
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) ListenAndServe() {
|
|
||||||
defer func() { self.shutdown <- true }()
|
|
||||||
|
|
||||||
gin.SetMode(gin.ReleaseMode)
|
|
||||||
self.router = gin.New()
|
|
||||||
self.router.Use(gin.Recovery(), apiLogger(), CacheHeadersMiddleware())
|
|
||||||
|
|
||||||
self.router.Static("/public", "./public")
|
|
||||||
self.router.Static("/app", "./public/app")
|
|
||||||
self.router.Static("/img", "./public/img")
|
|
||||||
|
|
||||||
// register & parse templates
|
|
||||||
templates := template.New("templates")
|
|
||||||
templates.Delims("[[", "]]")
|
|
||||||
templates.ParseFiles("./views/index.html")
|
|
||||||
self.router.SetHTMLTemplate(templates)
|
|
||||||
|
|
||||||
for _, fn := range routeHandlers {
|
|
||||||
fn(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
// register default route
|
|
||||||
self.router.GET("/", self.auth(), self.index)
|
|
||||||
self.router.GET("/dashboard/*_", self.auth(), self.index)
|
|
||||||
self.router.GET("/admin/*_", self.auth(), self.index)
|
|
||||||
self.router.GET("/account/*_", self.auth(), self.index)
|
|
||||||
|
|
||||||
listenAddr := fmt.Sprintf("%s:%s", setting.HttpAddr, setting.HttpPort)
|
|
||||||
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubUrl)
|
|
||||||
self.router.Run(listenAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) index(c *gin.Context) {
|
|
||||||
viewModel := &IndexDto{}
|
|
||||||
userAccount, _ := c.Get("userAccount")
|
|
||||||
account, _ := userAccount.(*models.Account)
|
|
||||||
initCurrentUserDto(&viewModel.User, account)
|
|
||||||
|
|
||||||
c.HTML(200, "index.html", viewModel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func CacheHeadersMiddleware() gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
c.Writer.Header().Add("Cache-Control", "max-age=0, public, must-revalidate, proxy-revalidate")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,147 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
addRoutes(func(self *HttpServer) {
|
|
||||||
self.addRoute("POST", "/api/account/collaborators/add", self.addCollaborator)
|
|
||||||
self.addRoute("POST", "/api/account/collaborators/remove", self.removeCollaborator)
|
|
||||||
self.addRoute("GET", "/api/account/", self.getAccount)
|
|
||||||
self.addRoute("GET", "/api/account/others", self.getOtherAccounts)
|
|
||||||
self.addRoute("POST", "/api/account/using/:id", self.setUsingAccount)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) getAccount(c *gin.Context, auth *authContext) {
|
|
||||||
var account = auth.userAccount
|
|
||||||
|
|
||||||
model := accountInfoDto{
|
|
||||||
Name: account.Name,
|
|
||||||
Email: account.Email,
|
|
||||||
AccountName: account.AccountName,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, collaborator := range account.Collaborators {
|
|
||||||
model.Collaborators = append(model.Collaborators, &collaboratorInfoDto{
|
|
||||||
AccountId: collaborator.AccountId,
|
|
||||||
Role: collaborator.Role,
|
|
||||||
Email: collaborator.Email,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(200, model)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) getOtherAccounts(c *gin.Context, auth *authContext) {
|
|
||||||
var account = auth.userAccount
|
|
||||||
|
|
||||||
otherAccounts, err := self.store.GetOtherAccountsFor(account.Id)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(500, gin.H{"message": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var result []*otherAccountDto
|
|
||||||
result = append(result, &otherAccountDto{
|
|
||||||
Id: account.Id,
|
|
||||||
Role: "owner",
|
|
||||||
IsUsing: account.Id == account.UsingAccountId,
|
|
||||||
Name: account.Email,
|
|
||||||
})
|
|
||||||
|
|
||||||
for _, other := range otherAccounts {
|
|
||||||
result = append(result, &otherAccountDto{
|
|
||||||
Id: other.Id,
|
|
||||||
Role: other.Role,
|
|
||||||
Name: other.Name,
|
|
||||||
IsUsing: other.Id == account.UsingAccountId,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(200, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) addCollaborator(c *gin.Context, auth *authContext) {
|
|
||||||
var model addCollaboratorDto
|
|
||||||
|
|
||||||
if !c.EnsureBody(&model) {
|
|
||||||
c.JSON(400, gin.H{"message": "Invalid request"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
collaborator, err := self.store.GetAccountByLogin(model.Email)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(404, gin.H{"message": "Collaborator not found"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
userAccount := auth.userAccount
|
|
||||||
|
|
||||||
if collaborator.Id == userAccount.Id {
|
|
||||||
c.JSON(400, gin.H{"message": "Cannot add yourself as collaborator"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = userAccount.AddCollaborator(collaborator)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(400, gin.H{"message": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = self.store.UpdateAccount(userAccount)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(500, gin.H{"message": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Abort(204)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) removeCollaborator(c *gin.Context, auth *authContext) {
|
|
||||||
var model removeCollaboratorDto
|
|
||||||
if !c.EnsureBody(&model) {
|
|
||||||
c.JSON(400, gin.H{"message": "Invalid request"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
account := auth.userAccount
|
|
||||||
account.RemoveCollaborator(model.AccountId)
|
|
||||||
|
|
||||||
err := self.store.UpdateAccount(account)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(500, gin.H{"message": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Abort(204)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) setUsingAccount(c *gin.Context, auth *authContext) {
|
|
||||||
idString := c.Params.ByName("id")
|
|
||||||
id, _ := strconv.Atoi(idString)
|
|
||||||
|
|
||||||
account := auth.userAccount
|
|
||||||
otherAccount, err := self.store.GetAccount(id)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(500, gin.H{"message": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if otherAccount.Id != account.Id && !otherAccount.HasCollaborator(account.Id) {
|
|
||||||
c.Abort(401)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
account.UsingAccountId = otherAccount.Id
|
|
||||||
err = self.store.UpdateAccount(account)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(500, gin.H{"message": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Abort(204)
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/models"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/gorilla/sessions"
|
|
||||||
)
|
|
||||||
|
|
||||||
type authContext struct {
|
|
||||||
account *models.Account
|
|
||||||
userAccount *models.Account
|
|
||||||
}
|
|
||||||
|
|
||||||
func (auth *authContext) getAccountId() int {
|
|
||||||
return auth.account.Id
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) authDenied(c *gin.Context) {
|
|
||||||
c.Writer.Header().Set("Location", "/login")
|
|
||||||
c.Abort(302)
|
|
||||||
}
|
|
||||||
|
|
||||||
func authGetRequestAccountId(c *gin.Context, session *sessions.Session) (int, error) {
|
|
||||||
accountId := session.Values["accountId"]
|
|
||||||
|
|
||||||
urlQuery := c.Request.URL.Query()
|
|
||||||
if len(urlQuery["render"]) > 0 {
|
|
||||||
accId, _ := strconv.Atoi(urlQuery["accountId"][0])
|
|
||||||
session.Values["accountId"] = accId
|
|
||||||
accountId = accId
|
|
||||||
}
|
|
||||||
|
|
||||||
if accountId == nil {
|
|
||||||
return -1, errors.New("Auth: session account id not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return accountId.(int), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) auth() gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
session, _ := sessionStore.Get(c.Request, "grafana-session")
|
|
||||||
accountId, err := authGetRequestAccountId(c, session)
|
|
||||||
|
|
||||||
if err != nil && c.Request.URL.Path != "/login" {
|
|
||||||
self.authDenied(c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
account, err := self.store.GetAccount(accountId)
|
|
||||||
if err != nil {
|
|
||||||
self.authDenied(c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
usingAccount, err := self.store.GetAccount(account.UsingAccountId)
|
|
||||||
if err != nil {
|
|
||||||
self.authDenied(c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Set("userAccount", account)
|
|
||||||
c.Set("usingAccount", usingAccount)
|
|
||||||
session.Save(c.Request, c.Writer)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
log "github.com/alecthomas/log4go"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
addRoutes(func(self *HttpServer) {
|
|
||||||
self.addRoute("GET", "/api/dashboards/:slug", self.getDashboard)
|
|
||||||
self.addRoute("GET", "/api/search/", self.search)
|
|
||||||
self.addRoute("POST", "/api/dashboard/", self.postDashboard)
|
|
||||||
self.addRoute("DELETE", "/api/dashboard/:slug", self.deleteDashboard)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) getDashboard(c *gin.Context, auth *authContext) {
|
|
||||||
slug := c.Params.ByName("slug")
|
|
||||||
|
|
||||||
dash, err := self.store.GetDashboard(slug, auth.getAccountId())
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(404, newErrorResponse("Dashboard not found"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
dash.Data["id"] = dash.Id
|
|
||||||
|
|
||||||
c.JSON(200, dash.Data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) deleteDashboard(c *gin.Context, auth *authContext) {
|
|
||||||
slug := c.Params.ByName("slug")
|
|
||||||
|
|
||||||
dash, err := self.store.GetDashboard(slug, auth.getAccountId())
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(404, newErrorResponse("Dashboard not found"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = self.store.DeleteDashboard(slug, auth.getAccountId())
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(500, newErrorResponse("Failed to delete dashboard: "+err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var resp = map[string]interface{}{"title": dash.Title}
|
|
||||||
|
|
||||||
c.JSON(200, resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) search(c *gin.Context, auth *authContext) {
|
|
||||||
query := c.Params.ByName("q")
|
|
||||||
|
|
||||||
results, err := self.store.Query(query, auth.getAccountId())
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Store query error: %v", err)
|
|
||||||
c.JSON(500, newErrorResponse("Failed"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(200, results)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) postDashboard(c *gin.Context, auth *authContext) {
|
|
||||||
var command saveDashboardCommand
|
|
||||||
|
|
||||||
if c.EnsureBody(&command) {
|
|
||||||
dashboard := models.NewDashboard("test")
|
|
||||||
dashboard.Data = command.Dashboard
|
|
||||||
dashboard.Title = dashboard.Data["title"].(string)
|
|
||||||
dashboard.AccountId = auth.getAccountId()
|
|
||||||
dashboard.UpdateSlug()
|
|
||||||
|
|
||||||
if dashboard.Data["id"] != nil {
|
|
||||||
dashboard.Id = dashboard.Data["id"].(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := self.store.SaveDashboard(dashboard)
|
|
||||||
if err == nil {
|
|
||||||
c.JSON(200, gin.H{"status": "success", "slug": dashboard.Slug})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(500, gin.H{"error": "bad request"})
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
type accountInfoDto struct {
|
|
||||||
Email string `json:"email"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
AccountName string `json:"accountName"`
|
|
||||||
Collaborators []*collaboratorInfoDto `json:"collaborators"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type collaboratorInfoDto struct {
|
|
||||||
AccountId int `json:"accountId"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
Role string `json:"role"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type addCollaboratorDto struct {
|
|
||||||
Email string `json:"email" binding:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type removeCollaboratorDto struct {
|
|
||||||
AccountId int `json:"accountId" binding:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type otherAccountDto struct {
|
|
||||||
Id int `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Role string `json:"role"`
|
|
||||||
IsUsing bool `json:"isUsing"`
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
green = string([]byte{27, 91, 57, 55, 59, 52, 50, 109})
|
|
||||||
white = string([]byte{27, 91, 57, 48, 59, 52, 55, 109})
|
|
||||||
yellow = string([]byte{27, 91, 57, 55, 59, 52, 51, 109})
|
|
||||||
red = string([]byte{27, 91, 57, 55, 59, 52, 49, 109})
|
|
||||||
reset = string([]byte{27, 91, 48, 109})
|
|
||||||
)
|
|
||||||
|
|
||||||
func ignoreLoggingRequest(code int, contentType string) bool {
|
|
||||||
return code == 304 ||
|
|
||||||
strings.HasPrefix(contentType, "application/javascript") ||
|
|
||||||
strings.HasPrefix(contentType, "text/") ||
|
|
||||||
strings.HasPrefix(contentType, "application/x-font-woff")
|
|
||||||
}
|
|
||||||
|
|
||||||
func apiLogger() gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
// Start timer
|
|
||||||
start := time.Now()
|
|
||||||
|
|
||||||
// Process request
|
|
||||||
c.Next()
|
|
||||||
|
|
||||||
code := c.Writer.Status()
|
|
||||||
contentType := c.Writer.Header().Get("Content-Type")
|
|
||||||
|
|
||||||
// ignore logging some requests
|
|
||||||
if ignoreLoggingRequest(code, contentType) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// save the IP of the requester
|
|
||||||
requester := c.Request.Header.Get("X-Real-IP")
|
|
||||||
// if the requester-header is empty, check the forwarded-header
|
|
||||||
if len(requester) == 0 {
|
|
||||||
requester = c.Request.Header.Get("X-Forwarded-For")
|
|
||||||
}
|
|
||||||
// if the requester is still empty, use the hard-coded address from the socket
|
|
||||||
if len(requester) == 0 {
|
|
||||||
requester = c.Request.RemoteAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
end := time.Now()
|
|
||||||
latency := end.Sub(start)
|
|
||||||
log.Info("[http] %s %s %3d %12v %s",
|
|
||||||
c.Request.Method, c.Request.URL.Path,
|
|
||||||
code,
|
|
||||||
latency,
|
|
||||||
c.Errors.String(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/models"
|
|
||||||
|
|
||||||
log "github.com/alecthomas/log4go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
addRoutes(func(self *HttpServer) {
|
|
||||||
self.router.GET("/login", self.index)
|
|
||||||
self.router.POST("/login", self.loginPost)
|
|
||||||
self.router.POST("/logout", self.logoutPost)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type loginJsonModel struct {
|
|
||||||
Email string `json:"email" binding:"required"`
|
|
||||||
Password string `json:"password" binding:"required"`
|
|
||||||
Remember bool `json:"remember"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) loginPost(c *gin.Context) {
|
|
||||||
var loginModel loginJsonModel
|
|
||||||
|
|
||||||
if !c.EnsureBody(&loginModel) {
|
|
||||||
c.JSON(400, gin.H{"status": "bad request"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
account, err := self.store.GetAccountByLogin(loginModel.Email)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(400, gin.H{"status": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if loginModel.Password != account.Password {
|
|
||||||
c.JSON(401, gin.H{"status": "unauthorized"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
loginUserWithAccount(account, c)
|
|
||||||
|
|
||||||
var resp = &LoginResultDto{}
|
|
||||||
resp.Status = "Logged in"
|
|
||||||
resp.User.Login = account.Login
|
|
||||||
|
|
||||||
c.JSON(200, resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loginUserWithAccount(account *models.Account, c *gin.Context) {
|
|
||||||
if account == nil {
|
|
||||||
log.Error("Account login with nil account")
|
|
||||||
}
|
|
||||||
session, err := sessionStore.Get(c.Request, "grafana-session")
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to get session %v", err)
|
|
||||||
}
|
|
||||||
session.Values["accountId"] = account.Id
|
|
||||||
session.Save(c.Request, c.Writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) logoutPost(c *gin.Context) {
|
|
||||||
session, _ := sessionStore.Get(c.Request, "grafana-session")
|
|
||||||
session.Values = nil
|
|
||||||
session.Save(c.Request, c.Writer)
|
|
||||||
|
|
||||||
c.JSON(200, gin.H{"status": "logged out"})
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/md5"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
type saveDashboardCommand struct {
|
|
||||||
Id string `json:"id"`
|
|
||||||
Title string `json:"title"`
|
|
||||||
Dashboard map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type errorResponse struct {
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type IndexDto struct {
|
|
||||||
User CurrentUserDto
|
|
||||||
}
|
|
||||||
|
|
||||||
type CurrentUserDto struct {
|
|
||||||
Login string `json:"login"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
GravatarUrl string `json:"gravatarUrl"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type LoginResultDto struct {
|
|
||||||
Status string `json:"status"`
|
|
||||||
User CurrentUserDto `json:"user"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func newErrorResponse(message string) *errorResponse {
|
|
||||||
return &errorResponse{Message: message}
|
|
||||||
}
|
|
||||||
|
|
||||||
func initCurrentUserDto(userDto *CurrentUserDto, account *models.Account) {
|
|
||||||
if account != nil {
|
|
||||||
userDto.Login = account.Login
|
|
||||||
userDto.Email = account.Email
|
|
||||||
userDto.GravatarUrl = getGravatarUrl(account.Email)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getGravatarUrl(text string) string {
|
|
||||||
hasher := md5.New()
|
|
||||||
hasher.Write([]byte(strings.ToLower(text)))
|
|
||||||
return fmt.Sprintf("https://secure.gravatar.com/avatar/%x?s=90&default=mm", hasher.Sum(nil))
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
package api
|
|
@ -1,112 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
log "github.com/alecthomas/log4go"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/golang/oauth2"
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/models"
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/stores"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
githubOAuthConfig *oauth2.Config
|
|
||||||
githubRedirectUrl string = "http://localhost:3000/oauth2/github/callback"
|
|
||||||
githubAuthUrl string = "https://github.com/login/oauth/authorize"
|
|
||||||
githubTokenUrl string = "https://github.com/login/oauth/access_token"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
addRoutes(func(self *HttpServer) {
|
|
||||||
if !self.cfg.Http.GithubOAuth.Enabled {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.router.GET("/oauth2/github", self.oauthGithub)
|
|
||||||
self.router.GET("/oauth2/github/callback", self.oauthGithubCallback)
|
|
||||||
|
|
||||||
options := &oauth2.Options{
|
|
||||||
ClientID: self.cfg.Http.GithubOAuth.ClientId,
|
|
||||||
ClientSecret: self.cfg.Http.GithubOAuth.ClientSecret,
|
|
||||||
RedirectURL: githubRedirectUrl,
|
|
||||||
Scopes: []string{"user:email"},
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg, err := oauth2.NewConfig(options, githubAuthUrl, githubTokenUrl)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to init github auth %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
githubOAuthConfig = cfg
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) oauthGithub(c *gin.Context) {
|
|
||||||
url := githubOAuthConfig.AuthCodeURL("", "online", "auto")
|
|
||||||
c.Redirect(302, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
type githubUserInfoDto struct {
|
|
||||||
Login string `json:"login"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
Company string `json:"company"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) oauthGithubCallback(c *gin.Context) {
|
|
||||||
code := c.Request.URL.Query()["code"][0]
|
|
||||||
log.Info("OAuth code: %v", code)
|
|
||||||
|
|
||||||
transport, err := githubOAuthConfig.NewTransportWithCode(code)
|
|
||||||
if err != nil {
|
|
||||||
c.String(500, "Failed to exchange oauth token: "+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client := http.Client{Transport: transport}
|
|
||||||
resp, err := client.Get("https://api.github.com/user")
|
|
||||||
if err != nil {
|
|
||||||
c.String(500, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var userInfo githubUserInfoDto
|
|
||||||
decoder := json.NewDecoder(resp.Body)
|
|
||||||
err = decoder.Decode(&userInfo)
|
|
||||||
if err != nil {
|
|
||||||
c.String(500, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(userInfo.Email) < 5 {
|
|
||||||
c.String(500, "Invalid email")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// try find existing account
|
|
||||||
account, err := self.store.GetAccountByLogin(userInfo.Email)
|
|
||||||
|
|
||||||
// create account if missing
|
|
||||||
if err == stores.ErrAccountNotFound {
|
|
||||||
account = &models.Account{
|
|
||||||
Login: userInfo.Login,
|
|
||||||
Email: userInfo.Email,
|
|
||||||
Name: userInfo.Name,
|
|
||||||
Company: userInfo.Company,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = self.store.CreateAccount(account); err != nil {
|
|
||||||
log.Error("Failed to create account %v", err)
|
|
||||||
c.String(500, "Failed to create account")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// login
|
|
||||||
loginUserWithAccount(account, c)
|
|
||||||
|
|
||||||
c.Redirect(302, "/")
|
|
||||||
}
|
|
@ -1,113 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
log "github.com/alecthomas/log4go"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/golang/oauth2"
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/models"
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/stores"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
googleOAuthConfig *oauth2.Config
|
|
||||||
googleRedirectUrl string = "http://localhost:3000/oauth2/google/callback"
|
|
||||||
googleAuthUrl string = "https://accounts.google.com/o/oauth2/auth"
|
|
||||||
googleTokenUrl string = "https://accounts.google.com/o/oauth2/token"
|
|
||||||
googleScopeProfile string = "https://www.googleapis.com/auth/userinfo.profile"
|
|
||||||
googleScopeEmail string = "https://www.googleapis.com/auth/userinfo.email"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
addRoutes(func(self *HttpServer) {
|
|
||||||
if !self.cfg.Http.GoogleOAuth.Enabled {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.router.GET("/oauth2/google", self.oauthGoogle)
|
|
||||||
self.router.GET("/oauth2/google/callback", self.oauthGoogleCallback)
|
|
||||||
|
|
||||||
options := &oauth2.Options{
|
|
||||||
ClientID: self.cfg.Http.GoogleOAuth.ClientId,
|
|
||||||
ClientSecret: self.cfg.Http.GoogleOAuth.ClientSecret,
|
|
||||||
RedirectURL: googleRedirectUrl,
|
|
||||||
Scopes: []string{googleScopeEmail, googleScopeProfile},
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg, err := oauth2.NewConfig(options, googleAuthUrl, googleTokenUrl)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to init google auth %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
googleOAuthConfig = cfg
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) oauthGoogle(c *gin.Context) {
|
|
||||||
url := googleOAuthConfig.AuthCodeURL("", "online", "auto")
|
|
||||||
c.Redirect(302, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
type googleUserInfoDto struct {
|
|
||||||
Email string `json:"email"`
|
|
||||||
GivenName string `json:"givenName"`
|
|
||||||
FamilyName string `json:"familyName"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) oauthGoogleCallback(c *gin.Context) {
|
|
||||||
code := c.Request.URL.Query()["code"][0]
|
|
||||||
log.Info("OAuth code: %v", code)
|
|
||||||
|
|
||||||
transport, err := googleOAuthConfig.NewTransportWithCode(code)
|
|
||||||
if err != nil {
|
|
||||||
c.String(500, "Failed to exchange oauth token: "+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client := http.Client{Transport: transport}
|
|
||||||
resp, err := client.Get("https://www.googleapis.com/oauth2/v1/userinfo?alt=json")
|
|
||||||
if err != nil {
|
|
||||||
c.String(500, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var userInfo googleUserInfoDto
|
|
||||||
decoder := json.NewDecoder(resp.Body)
|
|
||||||
err = decoder.Decode(&userInfo)
|
|
||||||
if err != nil {
|
|
||||||
c.String(500, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(userInfo.Email) < 5 {
|
|
||||||
c.String(500, "Invalid email")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// try find existing account
|
|
||||||
account, err := self.store.GetAccountByLogin(userInfo.Email)
|
|
||||||
|
|
||||||
// create account if missing
|
|
||||||
if err == stores.ErrAccountNotFound {
|
|
||||||
account = &models.Account{
|
|
||||||
Login: userInfo.Email,
|
|
||||||
Email: userInfo.Email,
|
|
||||||
Name: userInfo.Name,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = self.store.CreateAccount(account); err != nil {
|
|
||||||
log.Error("Failed to create account %v", err)
|
|
||||||
c.String(500, "Failed to create account")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// login
|
|
||||||
loginUserWithAccount(account, c)
|
|
||||||
|
|
||||||
c.Redirect(302, "/")
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
log "github.com/alecthomas/log4go"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
addRoutes(func(self *HttpServer) {
|
|
||||||
self.router.GET("/register/*_", self.index)
|
|
||||||
self.router.POST("/api/register/user", self.registerUserPost)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type registerAccountJsonModel struct {
|
|
||||||
Email string `json:"email" binding:"required"`
|
|
||||||
Password string `json:"password" binding:"required"`
|
|
||||||
Password2 bool `json:"remember2"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) registerUserPost(c *gin.Context) {
|
|
||||||
var registerModel registerAccountJsonModel
|
|
||||||
|
|
||||||
if !c.EnsureBody(®isterModel) {
|
|
||||||
c.JSON(400, gin.H{"status": "bad request"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
account := models.Account{
|
|
||||||
Login: registerModel.Email,
|
|
||||||
Email: registerModel.Email,
|
|
||||||
Password: registerModel.Password,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := self.store.CreateAccount(&account)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to create user account, email: %v, error: %v", registerModel.Email, err)
|
|
||||||
c.JSON(500, gin.H{"status": "failed to create account"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(200, gin.H{"status": "ok"})
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/components"
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
addRoutes(func(self *HttpServer) {
|
|
||||||
self.addRoute("GET", "/render/*url", self.renderToPng)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) renderToPng(c *gin.Context, auth *authContext) {
|
|
||||||
accountId := auth.getAccountId()
|
|
||||||
queryReader := utils.NewUrlQueryReader(c.Request.URL)
|
|
||||||
queryParams := "?render&accountId=" + strconv.Itoa(accountId) + "&" + c.Request.URL.RawQuery
|
|
||||||
|
|
||||||
renderOpts := &components.RenderOpts{
|
|
||||||
Url: c.Params.ByName("url") + queryParams,
|
|
||||||
Width: queryReader.Get("width", "800"),
|
|
||||||
Height: queryReader.Get("height", "400"),
|
|
||||||
}
|
|
||||||
|
|
||||||
renderOpts.Url = "http://localhost:3000" + renderOpts.Url
|
|
||||||
|
|
||||||
pngPath, err := self.renderer.RenderToPng(renderOpts)
|
|
||||||
if err != nil {
|
|
||||||
c.HTML(500, "error.html", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.File(pngPath)
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
type routeHandlerRegisterFn func(self *HttpServer)
|
|
||||||
type routeHandlerFn func(c *gin.Context, auth *authContext)
|
|
||||||
|
|
||||||
var routeHandlers = make([]routeHandlerRegisterFn, 0)
|
|
||||||
|
|
||||||
func getRouteHandlerWrapper(handler routeHandlerFn) gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
authContext := authContext{
|
|
||||||
account: c.MustGet("usingAccount").(*models.Account),
|
|
||||||
userAccount: c.MustGet("userAccount").(*models.Account),
|
|
||||||
}
|
|
||||||
handler(c, &authContext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HttpServer) addRoute(method string, path string, handler routeHandlerFn) {
|
|
||||||
switch method {
|
|
||||||
case "GET":
|
|
||||||
self.router.GET(path, self.auth(), getRouteHandlerWrapper(handler))
|
|
||||||
case "POST":
|
|
||||||
self.router.POST(path, self.auth(), getRouteHandlerWrapper(handler))
|
|
||||||
case "DELETE":
|
|
||||||
self.router.DELETE(path, self.auth(), getRouteHandlerWrapper(handler))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func addRoutes(fn routeHandlerRegisterFn) {
|
|
||||||
routeHandlers = append(routeHandlers, fn)
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
package api
|
|
@ -87,3 +87,29 @@ func GetOtherAccounts(c *middleware.Context) {
|
|||||||
|
|
||||||
c.JSON(200, result)
|
c.JSON(200, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func SetUsingAccount(c *middleware.Context) {
|
||||||
|
// idString := c.Params.ByName("id")
|
||||||
|
// id, _ := strconv.Atoi(idString)
|
||||||
|
//
|
||||||
|
// account := auth.userAccount
|
||||||
|
// otherAccount, err := self.store.GetAccount(id)
|
||||||
|
// if err != nil {
|
||||||
|
// c.JSON(500, gin.H{"message": err.Error()})
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if otherAccount.Id != account.Id && !otherAccount.HasCollaborator(account.Id) {
|
||||||
|
// c.Abort(401)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// account.UsingAccountId = otherAccount.Id
|
||||||
|
// err = self.store.UpdateAccount(account)
|
||||||
|
// if err != nil {
|
||||||
|
// c.JSON(500, gin.H{"message": err.Error()})
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// c.Abort(204)
|
||||||
|
// }
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
package stores
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
r "github.com/dancannon/gorethink"
|
|
||||||
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type rethinkStore struct {
|
|
||||||
session *r.Session
|
|
||||||
}
|
|
||||||
|
|
||||||
type RethinkCfg struct {
|
|
||||||
DatabaseName string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Account struct {
|
|
||||||
Id int `gorethink:"id"`
|
|
||||||
NextDashboardId int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRethinkStore(config *RethinkCfg) *rethinkStore {
|
|
||||||
log.Info("Initializing rethink storage")
|
|
||||||
|
|
||||||
session, err := r.Connect(r.ConnectOpts{
|
|
||||||
Address: "localhost:28015",
|
|
||||||
Database: config.DatabaseName,
|
|
||||||
MaxIdle: 10,
|
|
||||||
IdleTimeout: time.Second * 10,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Error(3, "Failed to connect to rethink database %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
createRethinkDBTablesAndIndices(config, session)
|
|
||||||
|
|
||||||
return &rethinkStore{
|
|
||||||
session: session,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *rethinkStore) Close() {}
|
|
@ -1,136 +0,0 @@
|
|||||||
package stores
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
r "github.com/dancannon/gorethink"
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (self *rethinkStore) getNextAccountId() (int, error) {
|
|
||||||
resp, err := r.Table("master").Get("ids").Update(map[string]interface{}{
|
|
||||||
"NextAccountId": r.Row.Field("NextAccountId").Add(1),
|
|
||||||
}, r.UpdateOpts{ReturnChanges: true}).RunWrite(self.session)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
change := resp.Changes[0]
|
|
||||||
|
|
||||||
if change.NewValue == nil {
|
|
||||||
return 0, errors.New("Failed to get new value after incrementing account id")
|
|
||||||
}
|
|
||||||
|
|
||||||
return int(change.NewValue.(map[string]interface{})["NextAccountId"].(float64)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *rethinkStore) CreateAccount(account *models.Account) error {
|
|
||||||
accountId, err := self.getNextAccountId()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
account.Id = accountId
|
|
||||||
account.UsingAccountId = accountId
|
|
||||||
|
|
||||||
resp, err := r.Table("accounts").Insert(account).RunWrite(self.session)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.Inserted == 0 {
|
|
||||||
return errors.New("Failed to insert acccount")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *rethinkStore) GetAccountByLogin(emailOrName string) (*models.Account, error) {
|
|
||||||
resp, err := r.Table("accounts").GetAllByIndex("Login", emailOrName).Run(self.session)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var account models.Account
|
|
||||||
err = resp.One(&account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, ErrAccountNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return &account, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *rethinkStore) GetAccount(id int) (*models.Account, error) {
|
|
||||||
resp, err := r.Table("accounts").Get(id).Run(self.session)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var account models.Account
|
|
||||||
err = resp.One(&account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("Not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &account, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *rethinkStore) UpdateAccount(account *models.Account) error {
|
|
||||||
resp, err := r.Table("accounts").Update(account).RunWrite(self.session)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.Replaced == 0 && resp.Unchanged == 0 {
|
|
||||||
return errors.New("Could not find account to update")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *rethinkStore) getNextDashboardNumber(accountId int) (int, error) {
|
|
||||||
resp, err := r.Table("accounts").Get(accountId).Update(map[string]interface{}{
|
|
||||||
"NextDashboardId": r.Row.Field("NextDashboardId").Add(1),
|
|
||||||
}, r.UpdateOpts{ReturnChanges: true}).RunWrite(self.session)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
change := resp.Changes[0]
|
|
||||||
|
|
||||||
if change.NewValue == nil {
|
|
||||||
return 0, errors.New("Failed to get next dashboard id, no new value after update")
|
|
||||||
}
|
|
||||||
|
|
||||||
return int(change.NewValue.(map[string]interface{})["NextDashboardId"].(float64)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *rethinkStore) GetOtherAccountsFor(accountId int) ([]*models.OtherAccount, error) {
|
|
||||||
resp, err := r.Table("accounts").
|
|
||||||
GetAllByIndex("CollaboratorAccountId", accountId).
|
|
||||||
Map(func(row r.Term) interface{} {
|
|
||||||
return map[string]interface{}{
|
|
||||||
"id": row.Field("id"),
|
|
||||||
"Name": row.Field("Email"),
|
|
||||||
"Role": row.Field("Collaborators").Filter(map[string]interface{}{
|
|
||||||
"AccountId": accountId,
|
|
||||||
}).Nth(0).Field("Role"),
|
|
||||||
}
|
|
||||||
}).Run(self.session)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var list []*models.OtherAccount
|
|
||||||
err = resp.All(&list)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("Failed to read available accounts")
|
|
||||||
}
|
|
||||||
|
|
||||||
return list, nil
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
package stores
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
log "github.com/alecthomas/log4go"
|
|
||||||
r "github.com/dancannon/gorethink"
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (self *rethinkStore) SaveDashboard(dash *models.Dashboard) error {
|
|
||||||
resp, err := r.Table("dashboards").Insert(dash, r.InsertOpts{Conflict: "update"}).RunWrite(self.session)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Inserted: %v, Errors: %v, Updated: %v", resp.Inserted, resp.Errors, resp.Updated)
|
|
||||||
log.Info("First error:", resp.FirstError)
|
|
||||||
if len(resp.GeneratedKeys) > 0 {
|
|
||||||
dash.Id = resp.GeneratedKeys[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *rethinkStore) GetDashboard(slug string, accountId int) (*models.Dashboard, error) {
|
|
||||||
resp, err := r.Table("dashboards").
|
|
||||||
GetAllByIndex("AccountIdSlug", []interface{}{accountId, slug}).
|
|
||||||
Run(self.session)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var dashboard models.Dashboard
|
|
||||||
err = resp.One(&dashboard)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &dashboard, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *rethinkStore) DeleteDashboard(slug string, accountId int) error {
|
|
||||||
resp, err := r.Table("dashboards").
|
|
||||||
GetAllByIndex("AccountIdSlug", []interface{}{accountId, slug}).
|
|
||||||
Delete().RunWrite(self.session)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.Deleted != 1 {
|
|
||||||
return errors.New("Did not find dashboard to delete")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *rethinkStore) Query(query string, accountId int) ([]*models.SearchResult, error) {
|
|
||||||
docs, err := r.Table("dashboards").
|
|
||||||
GetAllByIndex("AccountId", []interface{}{accountId}).
|
|
||||||
Filter(r.Row.Field("Title").Match(".*")).Run(self.session)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
results := make([]*models.SearchResult, 0, 50)
|
|
||||||
var dashboard models.Dashboard
|
|
||||||
for docs.Next(&dashboard) {
|
|
||||||
results = append(results, &models.SearchResult{
|
|
||||||
Title: dashboard.Title,
|
|
||||||
Id: dashboard.Slug,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return results, nil
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package stores
|
|
||||||
|
|
||||||
import (
|
|
||||||
log "github.com/alecthomas/log4go"
|
|
||||||
r "github.com/dancannon/gorethink"
|
|
||||||
)
|
|
||||||
|
|
||||||
func createRethinkDBTablesAndIndices(config *RethinkCfg, session *r.Session) {
|
|
||||||
|
|
||||||
r.DbCreate(config.DatabaseName).Exec(session)
|
|
||||||
|
|
||||||
// create tables
|
|
||||||
r.Db(config.DatabaseName).TableCreate("dashboards").Exec(session)
|
|
||||||
r.Db(config.DatabaseName).TableCreate("accounts").Exec(session)
|
|
||||||
r.Db(config.DatabaseName).TableCreate("master").Exec(session)
|
|
||||||
|
|
||||||
// create dashboard accountId + slug index
|
|
||||||
r.Db(config.DatabaseName).Table("dashboards").IndexCreateFunc("AccountIdSlug", func(row r.Term) interface{} {
|
|
||||||
return []interface{}{row.Field("AccountId"), row.Field("Slug")}
|
|
||||||
}).Exec(session)
|
|
||||||
|
|
||||||
r.Db(config.DatabaseName).Table("dashboards").IndexCreate("AccountId").Exec(session)
|
|
||||||
r.Db(config.DatabaseName).Table("accounts").IndexCreate("Login").Exec(session)
|
|
||||||
|
|
||||||
// create account collaborator index
|
|
||||||
r.Db(config.DatabaseName).Table("accounts").
|
|
||||||
IndexCreateFunc("CollaboratorAccountId", func(row r.Term) interface{} {
|
|
||||||
return row.Field("Collaborators").Map(func(row r.Term) interface{} {
|
|
||||||
return row.Field("AccountId")
|
|
||||||
})
|
|
||||||
}, r.IndexCreateOpts{Multi: true}).Exec(session)
|
|
||||||
|
|
||||||
// make sure master ids row exists
|
|
||||||
_, err := r.Table("master").Insert(map[string]interface{}{"id": "ids", "NextAccountId": 0}).RunWrite(session)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to insert master ids row", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
package stores
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/dancannon/gorethink"
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRethinkStore(t *testing.T) {
|
|
||||||
store := NewRethinkStore(&RethinkCfg{DatabaseName: "tests"})
|
|
||||||
defer gorethink.DbDrop("tests").Exec(store.session)
|
|
||||||
|
|
||||||
Convey("Insert dashboard", t, func() {
|
|
||||||
dashboard := models.NewDashboard("test")
|
|
||||||
dashboard.AccountId = 1
|
|
||||||
|
|
||||||
err := store.SaveDashboard(dashboard)
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
So(dashboard.Id, ShouldNotBeEmpty)
|
|
||||||
|
|
||||||
read, err := store.GetDashboard("test", 1)
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
So(read, ShouldNotBeNil)
|
|
||||||
})
|
|
||||||
|
|
||||||
Convey("can get next account id", t, func() {
|
|
||||||
id, err := store.getNextAccountId()
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
So(id, ShouldNotEqual, 0)
|
|
||||||
|
|
||||||
id2, err := store.getNextAccountId()
|
|
||||||
So(id2, ShouldEqual, id+1)
|
|
||||||
})
|
|
||||||
|
|
||||||
Convey("can create account", t, func() {
|
|
||||||
account := &models.Account{UserName: "torkelo", Email: "mupp", Login: "test@test.com"}
|
|
||||||
err := store.CreateAccount(account)
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
So(account.Id, ShouldNotEqual, 0)
|
|
||||||
|
|
||||||
read, err := store.GetUserAccountLogin("test@test.com")
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
So(read.Id, ShouldEqual, account.DatabaseId)
|
|
||||||
})
|
|
||||||
|
|
||||||
Convey("can get next dashboard id", t, func() {
|
|
||||||
account := &models.Account{UserName: "torkelo", Email: "mupp"}
|
|
||||||
err := store.CreateAccount(account)
|
|
||||||
dashId, err := store.getNextDashboardNumber(account.Id)
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
So(dashId, ShouldEqual, 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package stores
|
|
||||||
|
|
||||||
import "github.com/torkelo/grafana-pro/pkg/models"
|
|
||||||
|
|
||||||
type Store interface {
|
|
||||||
GetDashboard(slug string, accountId int) (*models.Dashboard, error)
|
|
||||||
SaveDashboard(dash *models.Dashboard) error
|
|
||||||
DeleteDashboard(slug string, accountId int) error
|
|
||||||
Query(query string, acccountId int) ([]*models.SearchResult, error)
|
|
||||||
CreateAccount(acccount *models.Account) error
|
|
||||||
UpdateAccount(acccount *models.Account) error
|
|
||||||
GetAccountByLogin(emailOrName string) (*models.Account, error)
|
|
||||||
GetAccount(accountId int) (*models.Account, error)
|
|
||||||
GetOtherAccountsFor(accountId int) ([]*models.OtherAccount, error)
|
|
||||||
Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func New() Store {
|
|
||||||
return NewRethinkStore(&RethinkCfg{DatabaseName: "grafana"})
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user