mirror of https://github.com/grafana/grafana.git
				
				
				
			InfluxDB now works in proxy mode, influxdb username and password is added in the backend and never exposed to frontend, #8
This commit is contained in:
		
							parent
							
								
									27b11b1d79
								
							
						
					
					
						commit
						164d11c816
					
				|  | @ -0,0 +1,123 @@ | |||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/torkelo/grafana-pro/pkg/bus" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/middleware" | ||||
| 	m "github.com/torkelo/grafana-pro/pkg/models" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/utils" | ||||
| ) | ||||
| 
 | ||||
| func GetAccount(c *middleware.Context) { | ||||
| 	query := m.GetAccountInfoQuery{Id: c.UserAccount.Id} | ||||
| 	err := bus.Dispatch(&query) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		c.JsonApiErr(500, "Failed to fetch collaboratos", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	c.JSON(200, query.Result) | ||||
| } | ||||
| 
 | ||||
| func AddCollaborator(c *middleware.Context) { | ||||
| 	var cmd m.AddCollaboratorCommand | ||||
| 
 | ||||
| 	if !c.JsonBody(&cmd) { | ||||
| 		c.JsonApiErr(400, "Invalid request", nil) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	userQuery := m.GetAccountByLoginQuery{Login: cmd.Email} | ||||
| 	err := bus.Dispatch(&userQuery) | ||||
| 	if err != nil { | ||||
| 		c.JsonApiErr(404, "Collaborator not found", nil) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	accountToAdd := userQuery.Result | ||||
| 
 | ||||
| 	if accountToAdd.Id == c.UserAccount.Id { | ||||
| 		c.JsonApiErr(400, "Cannot add yourself as collaborator", nil) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	cmd.AccountId = accountToAdd.Id | ||||
| 	cmd.ForAccountId = c.UserAccount.Id | ||||
| 	cmd.Role = m.ROLE_READ_WRITE | ||||
| 
 | ||||
| 	err = bus.Dispatch(&cmd) | ||||
| 	if err != nil { | ||||
| 		c.JsonApiErr(500, "Could not add collaborator", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	c.JsonOK("Collaborator added") | ||||
| } | ||||
| 
 | ||||
| func GetOtherAccounts(c *middleware.Context) { | ||||
| 	query := m.GetOtherAccountsQuery{AccountId: c.UserAccount.Id} | ||||
| 	err := bus.Dispatch(&query) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		c.JSON(500, utils.DynMap{"message": err.Error()}) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	result := append(query.Result, &m.OtherAccountDTO{ | ||||
| 		Id:    c.UserAccount.Id, | ||||
| 		Role:  "owner", | ||||
| 		Email: c.UserAccount.Email, | ||||
| 	}) | ||||
| 
 | ||||
| 	for _, ac := range result { | ||||
| 		if ac.Id == c.UserAccount.UsingAccountId { | ||||
| 			ac.IsUsing = true | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	c.JSON(200, result) | ||||
| } | ||||
| 
 | ||||
| func validateUsingAccount(accountId int64, otherId int64) bool { | ||||
| 	if accountId == otherId { | ||||
| 		return true | ||||
| 	} | ||||
| 
 | ||||
| 	query := m.GetOtherAccountsQuery{AccountId: accountId} | ||||
| 	err := bus.Dispatch(&query) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	// validate that the account id in the list
 | ||||
| 	valid := false | ||||
| 	for _, other := range query.Result { | ||||
| 		if other.Id == otherId { | ||||
| 			valid = true | ||||
| 		} | ||||
| 	} | ||||
| 	return valid | ||||
| } | ||||
| 
 | ||||
| func SetUsingAccount(c *middleware.Context) { | ||||
| 	usingAccountId := c.ParamsInt64(":id") | ||||
| 
 | ||||
| 	if !validateUsingAccount(c.UserAccount.Id, usingAccountId) { | ||||
| 		c.JsonApiErr(401, "Not a valid account", nil) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	cmd := m.SetUsingAccountCommand{ | ||||
| 		AccountId:      c.UserAccount.Id, | ||||
| 		UsingAccountId: usingAccountId, | ||||
| 	} | ||||
| 
 | ||||
| 	err := bus.Dispatch(&cmd) | ||||
| 	if err != nil { | ||||
| 		c.JsonApiErr(500, "Failed to update account", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	c.JsonOK("Active account changed") | ||||
| } | ||||
|  | @ -0,0 +1,73 @@ | |||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/torkelo/grafana-pro/pkg/bus" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/middleware" | ||||
| 	m "github.com/torkelo/grafana-pro/pkg/models" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/utils" | ||||
| ) | ||||
| 
 | ||||
| func GetDashboard(c *middleware.Context) { | ||||
| 	slug := c.Params(":slug") | ||||
| 
 | ||||
| 	dash, err := m.GetDashboard(slug, c.GetAccountId()) | ||||
| 	if err != nil { | ||||
| 		c.JsonApiErr(404, "Dashboard not found", nil) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	dash.Data["id"] = dash.Id | ||||
| 
 | ||||
| 	c.JSON(200, dash.Data) | ||||
| } | ||||
| 
 | ||||
| func DeleteDashboard(c *middleware.Context) { | ||||
| 	slug := c.Params(":slug") | ||||
| 
 | ||||
| 	dash, err := m.GetDashboard(slug, c.GetAccountId()) | ||||
| 	if err != nil { | ||||
| 		c.JsonApiErr(404, "Dashboard not found", nil) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	err = m.DeleteDashboard(slug, c.GetAccountId()) | ||||
| 	if err != nil { | ||||
| 		c.JsonApiErr(500, "Failed to delete dashboard", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	var resp = map[string]interface{}{"title": dash.Title} | ||||
| 
 | ||||
| 	c.JSON(200, resp) | ||||
| } | ||||
| 
 | ||||
| func Search(c *middleware.Context) { | ||||
| 	query := c.Query("q") | ||||
| 
 | ||||
| 	results, err := m.SearchQuery(query, c.GetAccountId()) | ||||
| 	if err != nil { | ||||
| 		c.JsonApiErr(500, "Search failed", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	c.JSON(200, results) | ||||
| } | ||||
| 
 | ||||
| func PostDashboard(c *middleware.Context) { | ||||
| 	var cmd m.SaveDashboardCommand | ||||
| 
 | ||||
| 	if !c.JsonBody(&cmd) { | ||||
| 		c.JsonApiErr(400, "bad request", nil) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	cmd.AccountId = c.GetAccountId() | ||||
| 
 | ||||
| 	err := bus.Dispatch(&cmd) | ||||
| 	if err != nil { | ||||
| 		c.JsonApiErr(500, "Failed to save dashboard", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	c.JSON(200, utils.DynMap{"status": "success", "slug": cmd.Result.Slug}) | ||||
| } | ||||
|  | @ -0,0 +1,53 @@ | |||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"net/http/httputil" | ||||
| 	"net/url" | ||||
| 
 | ||||
| 	"github.com/torkelo/grafana-pro/pkg/bus" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/middleware" | ||||
| 	m "github.com/torkelo/grafana-pro/pkg/models" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/utils" | ||||
| ) | ||||
| 
 | ||||
| func NewReverseProxy(ds *m.DataSource, proxyPath string) *httputil.ReverseProxy { | ||||
| 	target, _ := url.Parse(ds.Url) | ||||
| 
 | ||||
| 	director := func(req *http.Request) { | ||||
| 		req.URL.Scheme = target.Scheme | ||||
| 		req.URL.Host = target.Host | ||||
| 
 | ||||
| 		reqQueryVals := req.URL.Query() | ||||
| 
 | ||||
| 		if ds.Type == m.DS_INFLUXDB { | ||||
| 			req.URL.Path = utils.JoinUrlFragments(target.Path, "db/"+ds.Database+"/"+proxyPath) | ||||
| 			reqQueryVals.Add("u", ds.User) | ||||
| 			reqQueryVals.Add("p", ds.Password) | ||||
| 			req.URL.RawQuery = reqQueryVals.Encode() | ||||
| 		} else { | ||||
| 			req.URL.Path = utils.JoinUrlFragments(target.Path, proxyPath) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return &httputil.ReverseProxy{Director: director} | ||||
| } | ||||
| 
 | ||||
| // TODO: need to cache datasources
 | ||||
| func ProxyDataSourceRequest(c *middleware.Context) { | ||||
| 	id := c.ParamsInt64(":id") | ||||
| 
 | ||||
| 	query := m.GetDataSourceByIdQuery{ | ||||
| 		Id:        id, | ||||
| 		AccountId: c.GetAccountId(), | ||||
| 	} | ||||
| 
 | ||||
| 	err := bus.Dispatch(&query) | ||||
| 	if err != nil { | ||||
| 		c.JsonApiErr(500, "Unable to load datasource meta data", err) | ||||
| 	} | ||||
| 
 | ||||
| 	proxyPath := c.Params("*") | ||||
| 	proxy := NewReverseProxy(&query.Result, proxyPath) | ||||
| 	proxy.ServeHTTP(c.RW(), c.Req.Request) | ||||
| } | ||||
|  | @ -0,0 +1,58 @@ | |||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	. "github.com/smartystreets/goconvey/convey" | ||||
| 
 | ||||
| 	m "github.com/torkelo/grafana-pro/pkg/models" | ||||
| ) | ||||
| 
 | ||||
| func TestAccountDataAccess(t *testing.T) { | ||||
| 
 | ||||
| 	Convey("When getting graphite datasource proxy", t, func() { | ||||
| 		ds := m.DataSource{Url: "htttp://graphite:8080", Type: m.DS_GRAPHITE} | ||||
| 		proxy := NewReverseProxy(&ds, "/render") | ||||
| 
 | ||||
| 		requestUrl, _ := url.Parse("http://grafana.com/sub") | ||||
| 		req := http.Request{URL: requestUrl} | ||||
| 
 | ||||
| 		proxy.Director(&req) | ||||
| 
 | ||||
| 		Convey("Can translate request url and path", func() { | ||||
| 			So(req.URL.Host, ShouldEqual, "graphite:8080") | ||||
| 			So(req.URL.Path, ShouldEqual, "/render") | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	Convey("When getting influxdb datasource proxy", t, func() { | ||||
| 		ds := m.DataSource{ | ||||
| 			Type:     m.DS_INFLUXDB, | ||||
| 			Url:      "http://influxdb:8083", | ||||
| 			Database: "site", | ||||
| 			User:     "user", | ||||
| 			Password: "password", | ||||
| 		} | ||||
| 
 | ||||
| 		proxy := NewReverseProxy(&ds, "") | ||||
| 
 | ||||
| 		requestUrl, _ := url.Parse("http://grafana.com/sub") | ||||
| 		req := http.Request{URL: requestUrl} | ||||
| 
 | ||||
| 		proxy.Director(&req) | ||||
| 
 | ||||
| 		Convey("Should add db to url", func() { | ||||
| 			So(req.URL.Path, ShouldEqual, "/db/site/") | ||||
| 		}) | ||||
| 
 | ||||
| 		Convey("Should add username and password", func() { | ||||
| 			queryVals := req.URL.Query() | ||||
| 			So(queryVals["u"][0], ShouldEqual, "user") | ||||
| 			So(queryVals["p"][0], ShouldEqual, "password") | ||||
| 		}) | ||||
| 
 | ||||
| 	}) | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,93 @@ | |||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/torkelo/grafana-pro/pkg/api/dtos" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/bus" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/middleware" | ||||
| 	m "github.com/torkelo/grafana-pro/pkg/models" | ||||
| ) | ||||
| 
 | ||||
| func GetDataSources(c *middleware.Context) { | ||||
| 	query := m.GetDataSourcesQuery{AccountId: c.Account.Id} | ||||
| 	err := bus.Dispatch(&query) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		c.JsonApiErr(500, "Failed to query datasources", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	result := make([]*dtos.DataSource, len(query.Result)) | ||||
| 	for i, ds := range query.Result { | ||||
| 		result[i] = &dtos.DataSource{ | ||||
| 			Id:        ds.Id, | ||||
| 			AccountId: ds.AccountId, | ||||
| 			Name:      ds.Name, | ||||
| 			Url:       ds.Url, | ||||
| 			Type:      ds.Type, | ||||
| 			Access:    ds.Access, | ||||
| 			Password:  ds.Password, | ||||
| 			Database:  ds.Database, | ||||
| 			User:      ds.User, | ||||
| 			BasicAuth: ds.BasicAuth, | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	c.JSON(200, result) | ||||
| } | ||||
| 
 | ||||
| func DeleteDataSource(c *middleware.Context) { | ||||
| 	id := c.ParamsInt64(":id") | ||||
| 
 | ||||
| 	if id <= 0 { | ||||
| 		c.JsonApiErr(400, "Missing valid datasource id", nil) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	cmd := &m.DeleteDataSourceCommand{Id: id, AccountId: c.UserAccount.Id} | ||||
| 
 | ||||
| 	err := bus.Dispatch(cmd) | ||||
| 	if err != nil { | ||||
| 		c.JsonApiErr(500, "Failed to delete datasource", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	c.JsonOK("Data source deleted") | ||||
| } | ||||
| 
 | ||||
| func AddDataSource(c *middleware.Context) { | ||||
| 	cmd := m.AddDataSourceCommand{} | ||||
| 
 | ||||
| 	if !c.JsonBody(&cmd) { | ||||
| 		c.JsonApiErr(400, "Validation failed", nil) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	cmd.AccountId = c.Account.Id | ||||
| 
 | ||||
| 	err := bus.Dispatch(&cmd) | ||||
| 	if err != nil { | ||||
| 		c.JsonApiErr(500, "Failed to add datasource", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	c.JsonOK("Datasource added") | ||||
| } | ||||
| 
 | ||||
| func UpdateDataSource(c *middleware.Context) { | ||||
| 	cmd := m.UpdateDataSourceCommand{} | ||||
| 
 | ||||
| 	if !c.JsonBody(&cmd) { | ||||
| 		c.JsonApiErr(400, "Validation failed", nil) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	cmd.AccountId = c.Account.Id | ||||
| 
 | ||||
| 	err := bus.Dispatch(&cmd) | ||||
| 	if err != nil { | ||||
| 		c.JsonApiErr(500, "Failed to update datasource", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	c.JsonOK("Datasource updated") | ||||
| } | ||||
|  | @ -0,0 +1,68 @@ | |||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	"github.com/torkelo/grafana-pro/pkg/bus" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/middleware" | ||||
| 	m "github.com/torkelo/grafana-pro/pkg/models" | ||||
| ) | ||||
| 
 | ||||
| func getFrontendSettings(c *middleware.Context) (map[string]interface{}, error) { | ||||
| 	accountDataSources := make([]*m.DataSource, 0) | ||||
| 
 | ||||
| 	if c.Account != nil { | ||||
| 		query := m.GetDataSourcesQuery{AccountId: c.Account.Id} | ||||
| 		err := bus.Dispatch(&query) | ||||
| 
 | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 
 | ||||
| 		accountDataSources = query.Result | ||||
| 	} | ||||
| 
 | ||||
| 	datasources := make(map[string]interface{}) | ||||
| 
 | ||||
| 	for i, ds := range accountDataSources { | ||||
| 		url := ds.Url | ||||
| 
 | ||||
| 		if ds.Access == m.DS_ACCESS_PROXY { | ||||
| 			url = "/api/datasources/proxy/" + strconv.FormatInt(ds.Id, 10) | ||||
| 		} | ||||
| 
 | ||||
| 		var dsMap = map[string]interface{}{ | ||||
| 			"type": ds.Type, | ||||
| 			"url":  url, | ||||
| 		} | ||||
| 
 | ||||
| 		if ds.Type == m.DS_INFLUXDB { | ||||
| 			if ds.Access == m.DS_ACCESS_DIRECT { | ||||
| 				dsMap["username"] = ds.User | ||||
| 				dsMap["password"] = ds.Password | ||||
| 				dsMap["url"] = url + "/db/" + ds.Database | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// temp hack, first is always default
 | ||||
| 		// TODO: implement default ds account setting
 | ||||
| 		if i == 0 { | ||||
| 			dsMap["default"] = true | ||||
| 		} | ||||
| 
 | ||||
| 		datasources[ds.Name] = dsMap | ||||
| 	} | ||||
| 
 | ||||
| 	// add grafana backend data source
 | ||||
| 	datasources["grafana"] = map[string]interface{}{ | ||||
| 		"type":      "grafana", | ||||
| 		"url":       "", | ||||
| 		"grafanaDB": true, | ||||
| 	} | ||||
| 
 | ||||
| 	jsonObj := map[string]interface{}{ | ||||
| 		"datasources": datasources, | ||||
| 	} | ||||
| 
 | ||||
| 	return jsonObj, nil | ||||
| } | ||||
|  | @ -0,0 +1,61 @@ | |||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/torkelo/grafana-pro/pkg/api/dtos" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/bus" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/log" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/middleware" | ||||
| 	m "github.com/torkelo/grafana-pro/pkg/models" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/utils" | ||||
| ) | ||||
| 
 | ||||
| type loginJsonModel struct { | ||||
| 	Email    string `json:"email" binding:"required"` | ||||
| 	Password string `json:"password" binding:"required"` | ||||
| 	Remember bool   `json:"remember"` | ||||
| } | ||||
| 
 | ||||
| func LoginPost(c *middleware.Context) { | ||||
| 	var loginModel loginJsonModel | ||||
| 
 | ||||
| 	if !c.JsonBody(&loginModel) { | ||||
| 		c.JSON(400, utils.DynMap{"status": "bad request"}) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	userQuery := m.GetAccountByLoginQuery{Login: loginModel.Email} | ||||
| 	err := bus.Dispatch(&userQuery) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		c.JsonApiErr(401, "Invalid username or password", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	account := userQuery.Result | ||||
| 
 | ||||
| 	if loginModel.Password != account.Password { | ||||
| 		c.JsonApiErr(401, "Invalid username or password", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	loginUserWithAccount(account, c) | ||||
| 
 | ||||
| 	var resp = &dtos.LoginResult{} | ||||
| 	resp.Status = "Logged in" | ||||
| 	resp.User.Login = account.Login | ||||
| 
 | ||||
| 	c.JSON(200, resp) | ||||
| } | ||||
| 
 | ||||
| func loginUserWithAccount(account *m.Account, c *middleware.Context) { | ||||
| 	if account == nil { | ||||
| 		log.Error(3, "Account login with nil account") | ||||
| 	} | ||||
| 
 | ||||
| 	c.Session.Set("accountId", account.Id) | ||||
| } | ||||
| 
 | ||||
| func LogoutPost(c *middleware.Context) { | ||||
| 	c.Session.Delete("accountId") | ||||
| 	c.JSON(200, utils.DynMap{"status": "logged out"}) | ||||
| } | ||||
|  | @ -0,0 +1,78 @@ | |||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/torkelo/grafana-pro/pkg/bus" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/log" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/middleware" | ||||
| 	m "github.com/torkelo/grafana-pro/pkg/models" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/setting" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/social" | ||||
| ) | ||||
| 
 | ||||
| func OAuthLogin(ctx *middleware.Context) { | ||||
| 	if setting.OAuthService == nil { | ||||
| 		ctx.Handle(404, "login.OAuthLogin(oauth service not enabled)", nil) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	name := ctx.Params(":name") | ||||
| 	connect, ok := social.SocialMap[name] | ||||
| 	if !ok { | ||||
| 		ctx.Handle(404, "login.OAuthLogin(social login not enabled)", errors.New(name)) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	code := ctx.Query("code") | ||||
| 	if code == "" { | ||||
| 		ctx.Redirect(connect.AuthCodeURL("", "online", "auto")) | ||||
| 		return | ||||
| 	} | ||||
| 	log.Info("code: %v", code) | ||||
| 
 | ||||
| 	// handle call back
 | ||||
| 	transport, err := connect.NewTransportFromCode(code) | ||||
| 	if err != nil { | ||||
| 		ctx.Handle(500, "login.OAuthLogin(NewTransportWithCode)", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	log.Trace("login.OAuthLogin(Got token)") | ||||
| 
 | ||||
| 	userInfo, err := connect.UserInfo(transport) | ||||
| 	if err != nil { | ||||
| 		ctx.Handle(500, fmt.Sprintf("login.OAuthLogin(get info from %s)", name), err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	log.Info("login.OAuthLogin(social login): %s", userInfo) | ||||
| 
 | ||||
| 	userQuery := m.GetAccountByLoginQuery{Login: userInfo.Email} | ||||
| 	err = bus.Dispatch(&userQuery) | ||||
| 
 | ||||
| 	// create account if missing
 | ||||
| 	if err == m.ErrAccountNotFound { | ||||
| 		cmd := &m.CreateAccountCommand{ | ||||
| 			Login:   userInfo.Email, | ||||
| 			Email:   userInfo.Email, | ||||
| 			Name:    userInfo.Name, | ||||
| 			Company: userInfo.Company, | ||||
| 		} | ||||
| 
 | ||||
| 		if err = bus.Dispatch(&cmd); err != nil { | ||||
| 			ctx.Handle(500, "Failed to create account", err) | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		userQuery.Result = &cmd.Result | ||||
| 	} else if err != nil { | ||||
| 		ctx.Handle(500, "Unexpected error", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// login
 | ||||
| 	loginUserWithAccount(userQuery.Result, ctx) | ||||
| 
 | ||||
| 	ctx.Redirect("/") | ||||
| } | ||||
|  | @ -0,0 +1,26 @@ | |||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/torkelo/grafana-pro/pkg/bus" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/middleware" | ||||
| 	m "github.com/torkelo/grafana-pro/pkg/models" | ||||
| ) | ||||
| 
 | ||||
| func CreateAccount(c *middleware.Context) { | ||||
| 	var cmd m.CreateAccountCommand | ||||
| 
 | ||||
| 	if !c.JsonBody(&cmd) { | ||||
| 		c.JsonApiErr(400, "Validation error", nil) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	cmd.Login = cmd.Email | ||||
| 	err := bus.Dispatch(&cmd) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		c.JsonApiErr(500, "failed to create account", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	c.JsonOK("Account created") | ||||
| } | ||||
|  | @ -0,0 +1,32 @@ | |||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	"github.com/torkelo/grafana-pro/pkg/components/renderer" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/middleware" | ||||
| 	"github.com/torkelo/grafana-pro/pkg/utils" | ||||
| ) | ||||
| 
 | ||||
| func RenderToPng(c *middleware.Context) { | ||||
| 	accountId := c.GetAccountId() | ||||
| 	queryReader := utils.NewUrlQueryReader(c.Req.URL) | ||||
| 	queryParams := "?render&accountId=" + strconv.FormatInt(accountId, 10) + "&" + c.Req.URL.RawQuery | ||||
| 
 | ||||
| 	renderOpts := &renderer.RenderOpts{ | ||||
| 		Url:    c.Params("*") + queryParams, | ||||
| 		Width:  queryReader.Get("width", "800"), | ||||
| 		Height: queryReader.Get("height", "400"), | ||||
| 	} | ||||
| 
 | ||||
| 	renderOpts.Url = "http://localhost:3000/" + renderOpts.Url | ||||
| 
 | ||||
| 	pngPath, err := renderer.RenderToPng(renderOpts) | ||||
| 	if err != nil { | ||||
| 		c.HTML(500, "error.html", nil) | ||||
| 	} | ||||
| 
 | ||||
| 	c.Resp.Header().Set("Content-Type", "image/png") | ||||
| 	http.ServeFile(c.Resp, c.Req.Request, pngPath) | ||||
| } | ||||
		Loading…
	
		Reference in New Issue