mirror of https://github.com/grafana/grafana
Merge branch 'master' of https://github.com/grafana/grafana into docs-refac
commit
cd060c64d5
@ -0,0 +1,7 @@ |
||||
.DS_Store |
||||
|
||||
node_modules |
||||
tmp/* |
||||
npm-debug.log |
||||
dist/* |
||||
|
@ -0,0 +1,13 @@ |
||||
{ |
||||
"disallowImplicitTypeConversion": ["string"], |
||||
"disallowKeywords": ["with"], |
||||
"disallowMultipleLineBreaks": true, |
||||
"disallowMixedSpacesAndTabs": true, |
||||
"disallowTrailingWhitespace": true, |
||||
"requireSpacesInFunctionExpression": { |
||||
"beforeOpeningCurlyBrace": true |
||||
}, |
||||
"disallowSpacesInsideArrayBrackets": true, |
||||
"disallowSpacesInsideParentheses": true, |
||||
"validateIndentation": 2 |
||||
} |
@ -0,0 +1,36 @@ |
||||
{ |
||||
"browser": true, |
||||
"esnext": true, |
||||
|
||||
"bitwise":false, |
||||
"curly": true, |
||||
"eqnull": true, |
||||
"devel": true, |
||||
"eqeqeq": true, |
||||
"forin": false, |
||||
"immed": true, |
||||
"supernew": true, |
||||
"expr": true, |
||||
"indent": 2, |
||||
"latedef": true, |
||||
"newcap": true, |
||||
"noarg": true, |
||||
"noempty": true, |
||||
"undef": true, |
||||
"boss": true, |
||||
"trailing": true, |
||||
"laxbreak": true, |
||||
"laxcomma": true, |
||||
"sub": true, |
||||
"unused": true, |
||||
"maxdepth": 6, |
||||
"maxlen": 140, |
||||
|
||||
"globals": { |
||||
"System": true, |
||||
"define": true, |
||||
"require": true, |
||||
"Chromath": false, |
||||
"setImmediate": true |
||||
} |
||||
} |
@ -0,0 +1,54 @@ |
||||
module.exports = function(grunt) { |
||||
|
||||
require('load-grunt-tasks')(grunt); |
||||
|
||||
grunt.loadNpmTasks('grunt-execute'); |
||||
grunt.loadNpmTasks('grunt-contrib-clean'); |
||||
|
||||
grunt.initConfig({ |
||||
|
||||
clean: ["dist"], |
||||
|
||||
copy: { |
||||
src_to_dist: { |
||||
cwd: 'src', |
||||
expand: true, |
||||
src: ['**/*', '!**/*.js', '!**/*.scss'], |
||||
dest: 'dist' |
||||
}, |
||||
pluginDef: { |
||||
expand: true, |
||||
src: 'plugin.json', |
||||
dest: 'dist', |
||||
} |
||||
}, |
||||
|
||||
watch: { |
||||
rebuild_all: { |
||||
files: ['src/**/*', 'plugin.json'], |
||||
tasks: ['default'], |
||||
options: {spawn: false} |
||||
}, |
||||
}, |
||||
|
||||
babel: { |
||||
options: { |
||||
sourceMap: true, |
||||
presets: ["es2015"], |
||||
plugins: ['transform-es2015-modules-systemjs', "transform-es2015-for-of"], |
||||
}, |
||||
dist: { |
||||
files: [{ |
||||
cwd: 'src', |
||||
expand: true, |
||||
src: ['**/*.js'], |
||||
dest: 'dist', |
||||
ext:'.js' |
||||
}] |
||||
}, |
||||
}, |
||||
|
||||
}); |
||||
|
||||
grunt.registerTask('default', ['clean', 'copy:src_to_dist', 'copy:pluginDef', 'babel']); |
||||
}; |
@ -1,20 +0,0 @@ |
||||
define([ |
||||
], function() { |
||||
'use strict'; |
||||
|
||||
function StreamPageCtrl() {} |
||||
StreamPageCtrl.templateUrl = 'partials/stream.html'; |
||||
|
||||
function LogsPageCtrl() {} |
||||
LogsPageCtrl.templateUrl = 'partials/logs.html'; |
||||
|
||||
function NginxConfigCtrl() {} |
||||
NginxConfigCtrl.templateUrl = 'partials/config.html'; |
||||
|
||||
return { |
||||
ConfigCtrl: NginxConfigCtrl, |
||||
StreamPageCtrl: StreamPageCtrl, |
||||
LogsPageCtrl: LogsPageCtrl, |
||||
}; |
||||
|
||||
}); |
@ -0,0 +1,37 @@ |
||||
{ |
||||
"name": "kentik-app", |
||||
"private": true, |
||||
"version": "1.0.0", |
||||
"description": "", |
||||
"main": "index.js", |
||||
"scripts": { |
||||
"test": "echo \"Error: no test specified\" && exit 1" |
||||
}, |
||||
"repository": { |
||||
"type": "git", |
||||
"url": "git+https://github.com/raintank/kentik-app-poc.git" |
||||
}, |
||||
"author": "", |
||||
"license": "ISC", |
||||
"bugs": { |
||||
"url": "https://github.com/raintank/kentik-app-poc/issues" |
||||
}, |
||||
"devDependencies": { |
||||
"grunt": "~0.4.5", |
||||
"babel": "~6.5.1", |
||||
"grunt-babel": "~6.0.0", |
||||
"grunt-contrib-copy": "~0.8.2", |
||||
"grunt-contrib-watch": "^0.6.1", |
||||
"grunt-contrib-uglify": "~0.11.0", |
||||
"grunt-systemjs-builder": "^0.2.5", |
||||
"load-grunt-tasks": "~3.2.0", |
||||
"grunt-execute": "~0.2.2", |
||||
"grunt-contrib-clean": "~0.6.0" |
||||
}, |
||||
"dependencies": { |
||||
"babel-plugin-transform-es2015-modules-systemjs": "^6.5.0", |
||||
"babel-preset-es2015": "^6.5.0", |
||||
"lodash": "~4.0.0" |
||||
}, |
||||
"homepage": "https://github.com/raintank/kentik-app-poc#readme" |
||||
} |
@ -1,21 +0,0 @@ |
||||
define([ |
||||
'app/plugins/sdk' |
||||
], function(sdk) { |
||||
'use strict'; |
||||
|
||||
var NginxPanel = (function(_super) { |
||||
function NginxPanel($scope, $injector) { |
||||
_super.call(this, $scope, $injector); |
||||
} |
||||
|
||||
NginxPanel.template = '<h2>nginx!</h2>'; |
||||
NginxPanel.prototype = Object.create(_super.prototype); |
||||
NginxPanel.prototype.constructor = NginxPanel; |
||||
|
||||
return NginxPanel; |
||||
})(sdk.PanelCtrl); |
||||
|
||||
return { |
||||
PanelCtrl: NginxPanel |
||||
}; |
||||
}); |
@ -1 +0,0 @@ |
||||
<h2>nginx config</h2> |
@ -1,2 +0,0 @@ |
||||
|
||||
Logs! |
@ -1 +0,0 @@ |
||||
streams! |
@ -0,0 +1,3 @@ |
||||
<h3> |
||||
Nginx config! |
||||
</h3> |
@ -0,0 +1,6 @@ |
||||
|
||||
export class NginxAppConfigCtrl { |
||||
} |
||||
NginxAppConfigCtrl.templateUrl = 'components/config.html'; |
||||
|
||||
|
@ -0,0 +1,3 @@ |
||||
<h3> |
||||
Logs page! |
||||
</h3> |
@ -0,0 +1,6 @@ |
||||
|
||||
export class LogsPageCtrl { |
||||
} |
||||
LogsPageCtrl.templateUrl = 'components/logs.html'; |
||||
|
||||
|
@ -0,0 +1,3 @@ |
||||
<h3> |
||||
Stream page! |
||||
</h3> |
@ -0,0 +1,6 @@ |
||||
|
||||
export class StreamPageCtrl { |
||||
} |
||||
StreamPageCtrl.templateUrl = 'components/stream.html'; |
||||
|
||||
|
@ -0,0 +1,17 @@ |
||||
require([ |
||||
], function () { |
||||
|
||||
function Dashboard() { |
||||
|
||||
this.getInputs = function() { |
||||
|
||||
}; |
||||
|
||||
this.buildDashboard = function() { |
||||
|
||||
}; |
||||
} |
||||
|
||||
return Dashboard; |
||||
}); |
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
@ -0,0 +1,9 @@ |
||||
import {LogsPageCtrl} from './components/logs'; |
||||
import {StreamPageCtrl} from './components/stream'; |
||||
import {NginxAppConfigCtrl} from './components/config'; |
||||
|
||||
export { |
||||
NginxAppConfigCtrl as ConfigCtrl, |
||||
StreamPageCtrl, |
||||
LogsPageCtrl |
||||
}; |
@ -0,0 +1,15 @@ |
||||
import {PanelCtrl} from 'app/plugins/sdk'; |
||||
|
||||
class NginxPanelCtrl extends PanelCtrl { |
||||
|
||||
constructor($scope, $injector) { |
||||
super($scope, $injector); |
||||
} |
||||
|
||||
} |
||||
NginxPanelCtrl.template = '<h2>nginx!</h2>'; |
||||
|
||||
export { |
||||
NginxPanelCtrl as PanelCtrl |
||||
}; |
||||
|
@ -0,0 +1,99 @@ |
||||
package pluginproxy |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/json" |
||||
"errors" |
||||
"fmt" |
||||
"net/http" |
||||
"net/http/httputil" |
||||
"net/url" |
||||
"text/template" |
||||
|
||||
"github.com/grafana/grafana/pkg/bus" |
||||
"github.com/grafana/grafana/pkg/log" |
||||
"github.com/grafana/grafana/pkg/middleware" |
||||
m "github.com/grafana/grafana/pkg/models" |
||||
"github.com/grafana/grafana/pkg/plugins" |
||||
"github.com/grafana/grafana/pkg/util" |
||||
) |
||||
|
||||
type templateData struct { |
||||
JsonData map[string]interface{} |
||||
SecureJsonData map[string]string |
||||
} |
||||
|
||||
func getHeaders(route *plugins.AppPluginRoute, orgId int64, appId string) (http.Header, error) { |
||||
result := http.Header{} |
||||
|
||||
query := m.GetAppSettingByAppIdQuery{OrgId: orgId, AppId: appId} |
||||
|
||||
if err := bus.Dispatch(&query); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
data := templateData{ |
||||
JsonData: query.Result.JsonData, |
||||
SecureJsonData: query.Result.SecureJsonData.Decrypt(), |
||||
} |
||||
|
||||
for _, header := range route.Headers { |
||||
var contentBuf bytes.Buffer |
||||
t, err := template.New("content").Parse(header.Content) |
||||
if err != nil { |
||||
return nil, errors.New(fmt.Sprintf("could not parse header content template for header %s.", header.Name)) |
||||
} |
||||
|
||||
err = t.Execute(&contentBuf, data) |
||||
if err != nil { |
||||
return nil, errors.New(fmt.Sprintf("failed to execute header content template for header %s.", header.Name)) |
||||
} |
||||
|
||||
log.Trace("Adding header to proxy request. %s: %s", header.Name, contentBuf.String()) |
||||
result.Add(header.Name, contentBuf.String()) |
||||
} |
||||
|
||||
return result, nil |
||||
} |
||||
|
||||
func NewApiPluginProxy(ctx *middleware.Context, proxyPath string, route *plugins.AppPluginRoute, appId string) *httputil.ReverseProxy { |
||||
targetUrl, _ := url.Parse(route.Url) |
||||
|
||||
director := func(req *http.Request) { |
||||
|
||||
req.URL.Scheme = targetUrl.Scheme |
||||
req.URL.Host = targetUrl.Host |
||||
req.Host = targetUrl.Host |
||||
|
||||
req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath) |
||||
|
||||
// clear cookie headers
|
||||
req.Header.Del("Cookie") |
||||
req.Header.Del("Set-Cookie") |
||||
|
||||
//Create a HTTP header with the context in it.
|
||||
ctxJson, err := json.Marshal(ctx.SignedInUser) |
||||
if err != nil { |
||||
ctx.JsonApiErr(500, "failed to marshal context to json.", err) |
||||
return |
||||
} |
||||
|
||||
req.Header.Add("X-Grafana-Context", string(ctxJson)) |
||||
|
||||
if len(route.Headers) > 0 { |
||||
headers, err := getHeaders(route, ctx.OrgId, appId) |
||||
if err != nil { |
||||
ctx.JsonApiErr(500, "Could not generate plugin route header", err) |
||||
return |
||||
} |
||||
|
||||
for key, value := range headers { |
||||
log.Info("setting key %v value %v", key, value[0]) |
||||
req.Header.Set(key, value[0]) |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
return &httputil.ReverseProxy{Director: director} |
||||
} |
@ -0,0 +1,42 @@ |
||||
package pluginproxy |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/grafana/grafana/pkg/bus" |
||||
m "github.com/grafana/grafana/pkg/models" |
||||
"github.com/grafana/grafana/pkg/plugins" |
||||
"github.com/grafana/grafana/pkg/setting" |
||||
"github.com/grafana/grafana/pkg/util" |
||||
. "github.com/smartystreets/goconvey/convey" |
||||
) |
||||
|
||||
func TestPluginProxy(t *testing.T) { |
||||
|
||||
Convey("When getting proxy headers", t, func() { |
||||
route := &plugins.AppPluginRoute{ |
||||
Headers: []plugins.AppPluginRouteHeader{ |
||||
{Name: "x-header", Content: "my secret {{.SecureJsonData.key}}"}, |
||||
}, |
||||
} |
||||
|
||||
setting.SecretKey = "password" |
||||
|
||||
bus.AddHandler("test", func(query *m.GetAppSettingByAppIdQuery) error { |
||||
query.Result = &m.AppSettings{ |
||||
SecureJsonData: map[string][]byte{ |
||||
"key": util.Encrypt([]byte("123"), "password"), |
||||
}, |
||||
} |
||||
return nil |
||||
}) |
||||
|
||||
header, err := getHeaders(route, 1, "my-app") |
||||
So(err, ShouldBeNil) |
||||
|
||||
Convey("Should render header template", func() { |
||||
So(header.Get("x-header"), ShouldEqual, "my secret 123") |
||||
}) |
||||
}) |
||||
|
||||
} |
@ -0,0 +1,78 @@ |
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
var waitSeconds = 100; |
||||
var head = document.getElementsByTagName('head')[0]; |
||||
|
||||
// get all link tags in the page
|
||||
var links = document.getElementsByTagName('link'); |
||||
var linkHrefs = []; |
||||
for (var i = 0; i < links.length; i++) { |
||||
linkHrefs.push(links[i].href); |
||||
} |
||||
|
||||
var isWebkit = !!window.navigator.userAgent.match(/AppleWebKit\/([^ ;]*)/); |
||||
var webkitLoadCheck = function(link, callback) { |
||||
setTimeout(function() { |
||||
for (var i = 0; i < document.styleSheets.length; i++) { |
||||
var sheet = document.styleSheets[i]; |
||||
if (sheet.href === link.href) { |
||||
return callback(); |
||||
} |
||||
} |
||||
webkitLoadCheck(link, callback); |
||||
}, 10); |
||||
}; |
||||
|
||||
var noop = function() {}; |
||||
|
||||
var loadCSS = function(url) { |
||||
return new Promise(function(resolve, reject) { |
||||
var link = document.createElement('link'); |
||||
var timeout = setTimeout(function() { |
||||
reject('Unable to load CSS'); |
||||
}, waitSeconds * 1000); |
||||
|
||||
var _callback = function(error) { |
||||
clearTimeout(timeout); |
||||
link.onload = link.onerror = noop; |
||||
setTimeout(function() { |
||||
if (error) { |
||||
reject(error); |
||||
} else { |
||||
resolve(''); |
||||
} |
||||
}, 7); |
||||
}; |
||||
|
||||
link.type = 'text/css'; |
||||
link.rel = 'stylesheet'; |
||||
link.href = url; |
||||
|
||||
if (!isWebkit) { |
||||
link.onload = function() { _callback(undefined); }; |
||||
} else { |
||||
webkitLoadCheck(link, _callback); |
||||
} |
||||
|
||||
link.onerror = function(evt: any) { |
||||
_callback(evt.error || new Error('Error loading CSS file.')); |
||||
}; |
||||
|
||||
head.appendChild(link); |
||||
}); |
||||
}; |
||||
|
||||
export function fetch(load): any { |
||||
if (typeof window === 'undefined') { |
||||
return ''; |
||||
} |
||||
|
||||
// dont reload styles loaded in the head
|
||||
for (var i = 0; i < linkHrefs.length; i++) { |
||||
if (load.address === linkHrefs[i]) { |
||||
return ''; |
||||
} |
||||
} |
||||
return loadCSS(load.address); |
||||
} |
||||
|
@ -1,36 +0,0 @@ |
||||
define([ |
||||
'angular', |
||||
'lodash', |
||||
], |
||||
function (angular) { |
||||
'use strict'; |
||||
|
||||
var module = angular.module('grafana.controllers'); |
||||
|
||||
module.controller('DataSourcesCtrl', function($scope, $http, backendSrv, datasourceSrv) { |
||||
|
||||
$scope.init = function() { |
||||
$scope.datasources = []; |
||||
$scope.getDatasources(); |
||||
}; |
||||
|
||||
$scope.getDatasources = function() { |
||||
backendSrv.get('/api/datasources').then(function(results) { |
||||
$scope.datasources = results; |
||||
}); |
||||
}; |
||||
|
||||
$scope.remove = function(ds) { |
||||
backendSrv.delete('/api/datasources/' + ds.id).then(function() { |
||||
$scope.getDatasources(); |
||||
|
||||
backendSrv.get('/api/frontend/settings').then(function(settings) { |
||||
datasourceSrv.init(settings.datasources); |
||||
}); |
||||
}); |
||||
}; |
||||
|
||||
$scope.init(); |
||||
|
||||
}); |
||||
}); |
@ -0,0 +1,52 @@ |
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
import angular from 'angular'; |
||||
import _ from 'lodash'; |
||||
import coreModule from '../../core/core_module'; |
||||
|
||||
export class DataSourcesCtrl { |
||||
datasources: any; |
||||
|
||||
/** @ngInject */ |
||||
constructor(private $scope, private $location, private $http, private backendSrv, private datasourceSrv) { |
||||
backendSrv.get('/api/datasources') |
||||
.then((result) => { |
||||
this.datasources = result; |
||||
}); |
||||
} |
||||
|
||||
removeDataSourceConfirmed(ds) { |
||||
|
||||
this.backendSrv.delete('/api/datasources/' + ds.id) |
||||
.then(() => { |
||||
this.$scope.appEvent('alert-success', ['Datasource deleted', '']); |
||||
}, () => { |
||||
this.$scope.appEvent('alert-error', ['Unable to delete datasource', '']); |
||||
}).then(() => { |
||||
this.backendSrv.get('/api/datasources') |
||||
.then((result) => { |
||||
this.datasources = result; |
||||
}); |
||||
this.backendSrv.get('/api/frontend/settings') |
||||
.then((settings) => { |
||||
this.datasourceSrv.init(settings.datasources); |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
removeDataSource(ds) { |
||||
|
||||
this.$scope.appEvent('confirm-modal', { |
||||
title: 'Confirm delete datasource', |
||||
text: 'Are you sure you want to delete datasource ' + ds.name + '?', |
||||
yesText: "Delete", |
||||
icon: "fa-warning", |
||||
onConfirm: () => { |
||||
this.removeDataSourceConfirmed(ds); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
} |
||||
|
||||
coreModule.controller('DataSourcesCtrl', DataSourcesCtrl); |
@ -0,0 +1,72 @@ |
||||
"use strict"; |
||||
|
||||
if (typeof window !== 'undefined') { |
||||
var waitSeconds = 100; |
||||
|
||||
var head = document.getElementsByTagName('head')[0]; |
||||
|
||||
// get all link tags in the page
|
||||
var links = document.getElementsByTagName('link'); |
||||
var linkHrefs = []; |
||||
for (var i = 0; i < links.length; i++) { |
||||
linkHrefs.push(links[i].href); |
||||
} |
||||
|
||||
var isWebkit = !!window.navigator.userAgent.match(/AppleWebKit\/([^ ;]*)/); |
||||
var webkitLoadCheck = function(link, callback) { |
||||
setTimeout(function() { |
||||
for (var i = 0; i < document.styleSheets.length; i++) { |
||||
var sheet = document.styleSheets[i]; |
||||
if (sheet.href === link.href) { |
||||
return callback(); |
||||
} |
||||
} |
||||
webkitLoadCheck(link, callback); |
||||
}, 10); |
||||
}; |
||||
|
||||
var noop = function() {}; |
||||
|
||||
var loadCSS = function(url) { |
||||
return new Promise(function(resolve, reject) { |
||||
var timeout = setTimeout(function() { |
||||
reject('Unable to load CSS'); |
||||
}, waitSeconds * 1000); |
||||
var _callback = function(error) { |
||||
clearTimeout(timeout); |
||||
link.onload = link.onerror = noop; |
||||
setTimeout(function() { |
||||
if (error) { |
||||
reject(error); |
||||
} |
||||
else { |
||||
resolve(''); |
||||
} |
||||
}, 7); |
||||
}; |
||||
var link = document.createElement('link'); |
||||
link.type = 'text/css'; |
||||
link.rel = 'stylesheet'; |
||||
link.href = url; |
||||
if (!isWebkit) { |
||||
link.onload = function() { |
||||
_callback(); |
||||
} |
||||
} else { |
||||
webkitLoadCheck(link, _callback); |
||||
} |
||||
link.onerror = function(event) { |
||||
_callback(event.error || new Error('Error loading CSS file.')); |
||||
}; |
||||
head.appendChild(link); |
||||
}); |
||||
}; |
||||
|
||||
exports.fetch = function(load) { |
||||
// dont reload styles loaded in the head
|
||||
for (var i = 0; i < linkHrefs.length; i++) |
||||
if (load.address == linkHrefs[i]) |
||||
return ''; |
||||
return loadCSS(load.address); |
||||
}; |
||||
} |
@ -0,0 +1,16 @@ |
||||
/* |
||||
Text plugin |
||||
*/ |
||||
exports.translate = function(load) { |
||||
load.metadata.format = 'amd'; |
||||
return 'def' + 'ine(function() {\nreturn "' + load.source |
||||
.replace(/(["\\])/g, '\\$1') |
||||
.replace(/[\f]/g, "\\f") |
||||
.replace(/[\b]/g, "\\b") |
||||
.replace(/[\n]/g, "\\n") |
||||
.replace(/[\t]/g, "\\t") |
||||
.replace(/[\r]/g, "\\r") |
||||
.replace(/[\u2028]/g, "\\u2028") |
||||
.replace(/[\u2029]/g, "\\u2029") |
||||
+ '";\n});'; |
||||
} |
Loading…
Reference in new issue