mirror of
https://github.com/grafana/grafana.git
synced 2024-12-26 17:01:09 -06:00
more macaroon stuff
This commit is contained in:
parent
222319d924
commit
e84f06b503
BIN
data/sessions/5/a/5a7a6d798450f878d373110c50ed07ae3bc99d63
Normal file
BIN
data/sessions/5/a/5a7a6d798450f878d373110c50ed07ae3bc99d63
Normal file
Binary file not shown.
BIN
data/sessions/7/a/7ad60c89b1bc7a310c66e59570df698fc75d28b3
Normal file
BIN
data/sessions/7/a/7ad60c89b1bc7a310c66e59570df698fc75d28b3
Normal file
Binary file not shown.
BIN
data/sessions/7/b/7b786a2d47bb26f2fce2d9aa874615c6428c55a3
Normal file
BIN
data/sessions/7/b/7b786a2d47bb26f2fce2d9aa874615c6428c55a3
Normal file
Binary file not shown.
BIN
data/sessions/b/7/b724e1a2a6d52de49c11d1d62d6e0d83cba2911a
Normal file
BIN
data/sessions/b/7/b724e1a2a6d52de49c11d1d62d6e0d83cba2911a
Normal file
Binary file not shown.
BIN
grafana-pro
BIN
grafana-pro
Binary file not shown.
@ -15,7 +15,6 @@ import (
|
||||
"github.com/torkelo/grafana-pro/pkg/log"
|
||||
"github.com/torkelo/grafana-pro/pkg/middleware"
|
||||
"github.com/torkelo/grafana-pro/pkg/routes"
|
||||
"github.com/torkelo/grafana-pro/pkg/routes/login"
|
||||
"github.com/torkelo/grafana-pro/pkg/setting"
|
||||
"github.com/torkelo/grafana-pro/pkg/stores/rethink"
|
||||
)
|
||||
@ -70,13 +69,7 @@ func runWeb(*cli.Context) {
|
||||
log.Info("Starting Grafana-Pro v.1-alpha")
|
||||
|
||||
m := newMacaron()
|
||||
|
||||
auth := middleware.Auth()
|
||||
|
||||
// index
|
||||
m.Get("/", auth, routes.Index)
|
||||
m.Get("/login", routes.Index)
|
||||
m.Post("/login", login.LoginPost)
|
||||
routes.Register(m)
|
||||
|
||||
var err error
|
||||
listenAddr := fmt.Sprintf("%s:%s", setting.HttpAddr, setting.HttpPort)
|
||||
|
69
pkg/components/renderer/renderer.go
Normal file
69
pkg/components/renderer/renderer.go
Normal file
@ -0,0 +1,69 @@
|
||||
package renderer
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/torkelo/grafana-pro/pkg/log"
|
||||
"github.com/torkelo/grafana-pro/pkg/setting"
|
||||
)
|
||||
|
||||
type RenderOpts struct {
|
||||
Url string
|
||||
Width string
|
||||
Height string
|
||||
}
|
||||
|
||||
func RenderToPng(params *RenderOpts) (string, error) {
|
||||
log.Info("PhantomRenderer::renderToPng url %v", params.Url)
|
||||
binPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, "phantomjs"))
|
||||
scriptPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, "render.js"))
|
||||
pngPath, _ := filepath.Abs(filepath.Join(setting.ImagesDir, getHash(params.Url)))
|
||||
pngPath = pngPath + ".png"
|
||||
|
||||
cmd := exec.Command(binPath, scriptPath, "url="+params.Url, "width="+params.Width, "height="+params.Height, "png="+pngPath)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
go io.Copy(os.Stdout, stdout)
|
||||
go io.Copy(os.Stdout, stderr)
|
||||
|
||||
done := make(chan error)
|
||||
go func() {
|
||||
cmd.Wait()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(10 * time.Second):
|
||||
if err := cmd.Process.Kill(); err != nil {
|
||||
log.Error(4, "failed to kill: %v", err)
|
||||
}
|
||||
case <-done:
|
||||
}
|
||||
|
||||
return pngPath, nil
|
||||
}
|
||||
|
||||
func getHash(text string) string {
|
||||
hasher := md5.New()
|
||||
hasher.Write([]byte(text))
|
||||
return hex.EncodeToString(hasher.Sum(nil))
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package components
|
||||
package renderer
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
@ -12,8 +12,7 @@ func TestPhantomRender(t *testing.T) {
|
||||
|
||||
Convey("Can render url", t, func() {
|
||||
tempDir, _ := ioutil.TempDir("", "img")
|
||||
renderer := &PhantomRenderer{ImagesDir: tempDir, PhantomDir: "../../_vendor/phantomjs/"}
|
||||
png, err := renderer.RenderToPng("http://www.google.com")
|
||||
png, err := RenderToPng("http://www.google.com")
|
||||
So(err, ShouldBeNil)
|
||||
So(exists(png), ShouldEqual, true)
|
||||
|
@ -21,6 +21,10 @@ type Context struct {
|
||||
IsSigned bool
|
||||
}
|
||||
|
||||
func (c *Context) GetAccountId() int {
|
||||
return c.Account.Id
|
||||
}
|
||||
|
||||
func GetContextHandler() macaron.Handler {
|
||||
return func(c *macaron.Context, sess session.Store) {
|
||||
ctx := &Context{
|
||||
@ -51,6 +55,30 @@ func (ctx *Context) Handle(status int, title string, err error) {
|
||||
ctx.HTML(status, "index")
|
||||
}
|
||||
|
||||
func (ctx *Context) ApiError(status int, message string, err error) {
|
||||
resp := make(map[string]interface{})
|
||||
|
||||
if err != nil {
|
||||
log.Error(4, "%s: %v", message, err)
|
||||
if macaron.Env != macaron.PROD {
|
||||
resp["error"] = err
|
||||
}
|
||||
}
|
||||
|
||||
switch status {
|
||||
case 404:
|
||||
resp["message"] = "Not Found"
|
||||
case 500:
|
||||
resp["message"] = "Internal Server Error"
|
||||
}
|
||||
|
||||
if message != "" {
|
||||
resp["message"] = message
|
||||
}
|
||||
|
||||
ctx.HTML(status, "index")
|
||||
}
|
||||
|
||||
func (ctx *Context) JsonBody(model interface{}) bool {
|
||||
b, _ := ioutil.ReadAll(ctx.Req.Body)
|
||||
err := json.Unmarshal(b, &model)
|
||||
|
@ -8,6 +8,13 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
GetDashboard func(slug string, accountId int) (*Dashboard, error)
|
||||
SaveDashboard func(dash *Dashboard) error
|
||||
DeleteDashboard func(slug string, accountId int) error
|
||||
SearchQuery func(query string, acccountId int) ([]*SearchResult, error)
|
||||
)
|
||||
|
||||
type Dashboard struct {
|
||||
Id string `gorethink:"id,omitempty"`
|
||||
Slug string
|
||||
|
82
pkg/routes/api/api_dashboard.go
Normal file
82
pkg/routes/api/api_dashboard.go
Normal file
@ -0,0 +1,82 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/torkelo/grafana-pro/pkg/middleware"
|
||||
"github.com/torkelo/grafana-pro/pkg/models"
|
||||
"github.com/torkelo/grafana-pro/pkg/routes/apimodel"
|
||||
)
|
||||
|
||||
func GetDashboard(c *middleware.Context) {
|
||||
slug := c.Params(":slug")
|
||||
|
||||
dash, err := models.GetDashboard(slug, c.GetAccountId())
|
||||
if err != nil {
|
||||
c.ApiError(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 := models.GetDashboard(slug, c.GetAccountId())
|
||||
if err != nil {
|
||||
c.ApiError(404, "Dashboard not found", nil)
|
||||
return
|
||||
}
|
||||
|
||||
err = models.DeleteDashboard(slug, c.GetAccountId())
|
||||
if err != nil {
|
||||
c.ApiError(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 := models.SearchQuery(query, c.GetAccountId())
|
||||
if err != nil {
|
||||
c.ApiError(500, "Search failed", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, results)
|
||||
}
|
||||
|
||||
func PostDashboard(c *middleware.Context) {
|
||||
var command apimodel.SaveDashboardCommand
|
||||
|
||||
if !c.JsonBody(&command) {
|
||||
c.ApiError(400, "bad request", nil)
|
||||
return
|
||||
}
|
||||
|
||||
dashboard := models.NewDashboard("test")
|
||||
dashboard.Data = command.Dashboard
|
||||
dashboard.Title = dashboard.Data["title"].(string)
|
||||
dashboard.AccountId = c.GetAccountId()
|
||||
dashboard.UpdateSlug()
|
||||
|
||||
if dashboard.Data["id"] != nil {
|
||||
dashboard.Id = dashboard.Data["id"].(string)
|
||||
}
|
||||
|
||||
err := models.SaveDashboard(dashboard)
|
||||
if err != nil {
|
||||
c.ApiError(500, "Failed to save dashboard", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{"status": "success", "slug": dashboard.Slug})
|
||||
}
|
30
pkg/routes/api/api_render.go
Normal file
30
pkg/routes/api/api_render.go
Normal file
@ -0,0 +1,30 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"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.Itoa(accountId) + "&" + c.Req.URL.RawQuery
|
||||
|
||||
renderOpts := &renderer.RenderOpts{
|
||||
Url: c.Params("url") + 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.ServeFile(pngPath)
|
||||
}
|
@ -34,3 +34,9 @@ func getGravatarUrl(text string) string {
|
||||
hasher.Write([]byte(strings.ToLower(text)))
|
||||
return fmt.Sprintf("https://secure.gravatar.com/avatar/%x?s=90&default=mm", hasher.Sum(nil))
|
||||
}
|
||||
|
||||
type SaveDashboardCommand struct {
|
||||
Id string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Dashboard map[string]interface{} `json:"dashboard"`
|
||||
}
|
||||
|
@ -1,10 +1,35 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/torkelo/grafana-pro/pkg/middleware"
|
||||
"github.com/torkelo/grafana-pro/pkg/routes/api"
|
||||
"github.com/torkelo/grafana-pro/pkg/routes/apimodel"
|
||||
"github.com/torkelo/grafana-pro/pkg/routes/login"
|
||||
)
|
||||
|
||||
func Register(m *macaron.Macaron) {
|
||||
auth := middleware.Auth()
|
||||
|
||||
// index
|
||||
m.Get("/", auth, Index)
|
||||
m.Post("/logout", login.LogoutPost)
|
||||
m.Post("/login", login.LoginPost)
|
||||
|
||||
// no auth
|
||||
m.Get("/login", Index)
|
||||
|
||||
// dashboards
|
||||
m.Get("/dashboard/*", auth, Index)
|
||||
m.Get("/api/dashboards/:slug", auth, api.GetDashboard)
|
||||
m.Get("/api/search/", auth, api.Search)
|
||||
m.Post("/api/dashboard/", auth, api.PostDashboard)
|
||||
m.Delete("/api/dashboard/:slug", auth, api.DeleteDashboard)
|
||||
|
||||
// rendering
|
||||
m.Get("/render/*url", auth, api.RenderToPng)
|
||||
}
|
||||
|
||||
func Index(ctx *middleware.Context) {
|
||||
ctx.Data["User"] = apimodel.NewCurrentUserDto(ctx.UserAccount)
|
||||
ctx.HTML(200, "index")
|
||||
|
@ -58,6 +58,10 @@ var (
|
||||
ProdMode bool
|
||||
RunUser string
|
||||
IsWindows bool
|
||||
|
||||
// PhantomJs Rendering
|
||||
ImagesDir string
|
||||
PhantomDir string
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -140,6 +144,10 @@ func NewConfigContext() {
|
||||
|
||||
StaticRootPath = Cfg.MustValue("server", "static_root_path", workDir)
|
||||
RouterLogging = Cfg.MustBool("server", "router_logging", false)
|
||||
|
||||
// PhantomJS rendering
|
||||
ImagesDir = "data/png"
|
||||
PhantomDir = "_vendor/phantomjs"
|
||||
}
|
||||
|
||||
func initSessionService() {
|
||||
|
@ -34,6 +34,11 @@ func Init() {
|
||||
|
||||
models.GetAccount = GetAccount
|
||||
models.GetAccountByLogin = GetAccountByLogin
|
||||
|
||||
models.GetDashboard = GetDashboard
|
||||
models.SearchQuery = SearchQuery
|
||||
models.DeleteDashboard = DeleteDashboard
|
||||
models.SaveDashboard = SaveDashboard
|
||||
}
|
||||
|
||||
func createRethinkDBTablesAndIndices() {
|
||||
|
80
pkg/stores/rethink/rethink_dashboards.go
Normal file
80
pkg/stores/rethink/rethink_dashboards.go
Normal file
@ -0,0 +1,80 @@
|
||||
package rethink
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
r "github.com/dancannon/gorethink"
|
||||
|
||||
"github.com/torkelo/grafana-pro/pkg/log"
|
||||
"github.com/torkelo/grafana-pro/pkg/models"
|
||||
)
|
||||
|
||||
func SaveDashboard(dash *models.Dashboard) error {
|
||||
resp, err := r.Table("dashboards").Insert(dash, r.InsertOpts{Conflict: "update"}).RunWrite(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 GetDashboard(slug string, accountId int) (*models.Dashboard, error) {
|
||||
resp, err := r.Table("dashboards").
|
||||
GetAllByIndex("AccountIdSlug", []interface{}{accountId, slug}).
|
||||
Run(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 DeleteDashboard(slug string, accountId int) error {
|
||||
resp, err := r.Table("dashboards").
|
||||
GetAllByIndex("AccountIdSlug", []interface{}{accountId, slug}).
|
||||
Delete().RunWrite(session)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.Deleted != 1 {
|
||||
return errors.New("Did not find dashboard to delete")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func SearchQuery(query string, accountId int) ([]*models.SearchResult, error) {
|
||||
docs, err := r.Table("dashboards").
|
||||
GetAllByIndex("AccountId", []interface{}{accountId}).
|
||||
Filter(r.Row.Field("Title").Match(".*")).Run(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
|
||||
}
|
Loading…
Reference in New Issue
Block a user