mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Merge pull request #5422 from spalger/backport/5337
Backport 5337 and 5410
This commit is contained in:
commit
81cd36e319
23 changed files with 226 additions and 23 deletions
|
@ -4,6 +4,10 @@
|
|||
# The host to bind the server to.
|
||||
# server.host: "0.0.0.0"
|
||||
|
||||
# If you are running kibana behind a proxy, and want to mount it at a path,
|
||||
# specify that path here. The basePath can't end in a slash.
|
||||
# server.basePath: ""
|
||||
|
||||
# A value to use as a XSRF token. This token is sent back to the server on each request
|
||||
# and required if you want to execute requests from other clients (like curl).
|
||||
# server.xsrf.token: ""
|
||||
|
|
|
@ -16,6 +16,7 @@ let babelExclude = [/[\/\\](webpackShims|node_modules|bower_components)[\/\\]/];
|
|||
class BaseOptimizer {
|
||||
constructor(opts) {
|
||||
this.env = opts.env;
|
||||
this.urlBasePath = opts.urlBasePath;
|
||||
this.bundles = opts.bundles;
|
||||
this.profile = opts.profile || false;
|
||||
|
||||
|
@ -74,7 +75,7 @@ class BaseOptimizer {
|
|||
path: this.env.workingDir,
|
||||
filename: '[name].bundle.js',
|
||||
sourceMapFilename: '[file].map',
|
||||
publicPath: '/bundles/',
|
||||
publicPath: `${this.urlBasePath || ''}/bundles/`,
|
||||
devtoolModuleFilenameTemplate: '[absolute-resource-path]'
|
||||
},
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ module.exports = async (kbnServer, server, config) => {
|
|||
env: bundles.env,
|
||||
bundles: bundles,
|
||||
profile: config.get('optimize.profile'),
|
||||
urlBasePath: config.get('server.basePath'),
|
||||
sourceMaps: config.get('optimize.sourceMaps'),
|
||||
unsafeCache: config.get('optimize.unsafeCache'),
|
||||
});
|
||||
|
|
|
@ -15,6 +15,7 @@ module.exports = async (kbnServer, kibanaHapiServer, config) => {
|
|||
profile: config.get('optimize.profile'),
|
||||
sourceMaps: config.get('optimize.sourceMaps'),
|
||||
prebuild: config.get('optimize.lazyPrebuild'),
|
||||
urlBasePath: config.get('server.basePath'),
|
||||
unsafeCache: config.get('optimize.unsafeCache'),
|
||||
})
|
||||
);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div class="container-fluid">
|
||||
<ul class="nav navbar-nav">
|
||||
<li ng-repeat="section in sections" ng-class="section.class">
|
||||
<a class="navbar-link" ng-href="{{section.url}}">{{section.display}}</a>
|
||||
<a class="navbar-link" kbn-href="{{section.url}}">{{section.display}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div class="kbn-settings-about container" ng-controller="settingsAbout">
|
||||
<div class="col-md-4 col-md-offset-4 jumbotron">
|
||||
<center>
|
||||
<img src='/plugins/kibana/settings/sections/about/barcode.svg' alt="Kibana Barcode Logo" width="128" height="128"><br>
|
||||
<img kbn-src="/plugins/kibana/settings/sections/about/barcode.svg" alt="Kibana Barcode Logo" width="128" height="128"><br>
|
||||
<h1>Kibana</h1>
|
||||
<p>
|
||||
<table class="table table-condensed kbn-settings-about-versions">
|
||||
|
|
|
@ -5,7 +5,7 @@ var notify = require('ui/notify');
|
|||
require('plugins/statusPage/statusPageMetric');
|
||||
require('plugins/statusPage/statusPage.less');
|
||||
|
||||
require('ui/chrome')
|
||||
var chrome = require('ui/chrome')
|
||||
.setTabs([
|
||||
{
|
||||
id: '',
|
||||
|
@ -23,7 +23,7 @@ require('ui/chrome')
|
|||
|
||||
// go ahead and get the info you want
|
||||
return $http
|
||||
.get('/api/status')
|
||||
.get(chrome.addBasePath('/api/status'))
|
||||
.then(function (resp) {
|
||||
|
||||
if (ui.fetchError) {
|
||||
|
|
|
@ -83,4 +83,18 @@ module.exports = class KbnServer {
|
|||
async close() {
|
||||
await fromNode(cb => this.server.stop(cb));
|
||||
}
|
||||
|
||||
async inject(opts) {
|
||||
if (!this.server) await this.ready();
|
||||
|
||||
return await fromNode(cb => {
|
||||
try {
|
||||
this.server.inject(opts, (resp) => {
|
||||
cb(null, resp);
|
||||
});
|
||||
} catch (err) {
|
||||
cb(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
38
src/server/__tests__/basePath.js
Normal file
38
src/server/__tests__/basePath.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { resolve } from 'path';
|
||||
import { fromNode as fn } from 'bluebird';
|
||||
import expect from 'expect.js';
|
||||
|
||||
import KbnServer from '../KbnServer';
|
||||
|
||||
const src = resolve.bind(__dirname, '../../');
|
||||
const basePath = '/kibana';
|
||||
|
||||
describe('Server basePath config', function () {
|
||||
this.slow(10000);
|
||||
this.timeout(60000);
|
||||
|
||||
let kbnServer;
|
||||
before(async function () {
|
||||
kbnServer = new KbnServer({
|
||||
server: { autoListen: false, basePath },
|
||||
plugins: { scanDirs: [src('plugins')] },
|
||||
logging: { quiet: true },
|
||||
optimize: { enabled: false },
|
||||
});
|
||||
await kbnServer.ready();
|
||||
return kbnServer;
|
||||
});
|
||||
|
||||
after(async function () {
|
||||
await kbnServer.close();
|
||||
});
|
||||
|
||||
it('appends the basePath to root redirect', async function () {
|
||||
const response = await kbnServer.inject({
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
});
|
||||
|
||||
expect(response.payload).to.match(/defaultRoute = '\/kibana\/app\/kibana'/);
|
||||
});
|
||||
});
|
38
src/server/config/__tests__/schema.js
Normal file
38
src/server/config/__tests__/schema.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
import schemaProvider from '../schema';
|
||||
import expect from 'expect.js';
|
||||
import Joi from 'joi';
|
||||
|
||||
describe('Config schema', function () {
|
||||
let schema;
|
||||
beforeEach(() => schema = schemaProvider());
|
||||
|
||||
function validate(data, options) {
|
||||
return Joi.validate(data, schema, options);
|
||||
}
|
||||
|
||||
describe('server', function () {
|
||||
describe('basePath', function () {
|
||||
it('accepts empty strings', function () {
|
||||
const { error } = validate({ server: { basePath: '' }});
|
||||
expect(error == null).to.be.ok();
|
||||
});
|
||||
|
||||
it('accepts strings with leading slashes', function () {
|
||||
const { error } = validate({ server: { basePath: '/path' }});
|
||||
expect(error == null).to.be.ok();
|
||||
});
|
||||
|
||||
it('rejects strings with trailing slashes', function () {
|
||||
const { error } = validate({ server: { basePath: '/path/' }});
|
||||
expect(error).to.have.property('details');
|
||||
expect(error.details[0]).to.have.property('path', 'server.basePath');
|
||||
});
|
||||
|
||||
it('rejects strings without leading slashes', function () {
|
||||
const { error } = validate({ server: { basePath: 'path' }});
|
||||
expect(error).to.have.property('details');
|
||||
expect(error.details[0]).to.have.property('path', 'server.basePath');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -30,6 +30,7 @@ module.exports = () => Joi.object({
|
|||
port: Joi.number().default(5601),
|
||||
autoListen: Joi.boolean().default(true),
|
||||
defaultRoute: Joi.string(),
|
||||
basePath: Joi.string().default('').allow('').regex(/(^$|^\/.*[^\/]$)/, `start with a slash, don't end with one`),
|
||||
ssl: Joi.object({
|
||||
cert: Joi.string(),
|
||||
key: Joi.string()
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
let _ = require('lodash');
|
||||
|
||||
module.exports = _.once(function (kbnServer) {
|
||||
const { uiExports, config } = kbnServer;
|
||||
// user configured default route
|
||||
let defaultConfig = kbnServer.config.get('server.defaultRoute');
|
||||
let defaultConfig = config.get('server.defaultRoute');
|
||||
if (defaultConfig) return defaultConfig;
|
||||
|
||||
// redirect to the single app
|
||||
let apps = kbnServer.uiExports.apps.toArray();
|
||||
return '/app/kibana';
|
||||
return `${config.get('server.basePath')}/app/kibana`;
|
||||
});
|
||||
|
|
|
@ -97,7 +97,7 @@ module.exports = function (kbnServer, server, config) {
|
|||
method: 'GET',
|
||||
handler: function (req, reply) {
|
||||
return reply.view('rootRedirect', {
|
||||
hashRoute: '/app/kibana',
|
||||
hashRoute: `${config.get('server.basePath')}/app/kibana`,
|
||||
defaultRoute: getDefaultRoute(kbnServer),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ class UiApp {
|
|||
this.hidden = this.spec.hidden;
|
||||
this.autoloadOverrides = this.spec.autoload;
|
||||
this.templateName = this.spec.templateName || 'uiApp';
|
||||
this.url = this.spec.url || '/app/' + this.id;
|
||||
this.url = `${spec.urlBasePath || ''}${this.spec.url || `/app/${this.id}`}`;
|
||||
|
||||
// once this resolves, no reason to run it again
|
||||
this.getModules = _.once(this.getModules);
|
||||
|
|
|
@ -4,9 +4,10 @@ var minimatch = require('minimatch');
|
|||
var UiAppCollection = require('./UiAppCollection');
|
||||
|
||||
class UiExports {
|
||||
constructor(kbnServer) {
|
||||
constructor({ urlBasePath }) {
|
||||
this.apps = new UiAppCollection(this);
|
||||
this.aliases = {};
|
||||
this.urlBasePath = urlBasePath;
|
||||
this.exportConsumer = _.memoize(this.exportConsumer);
|
||||
this.consumers = [];
|
||||
this.bundleProviders = [];
|
||||
|
@ -48,7 +49,10 @@ class UiExports {
|
|||
case 'apps':
|
||||
return (plugin, specs) => {
|
||||
for (let spec of [].concat(specs || [])) {
|
||||
let app = this.apps.new(_.defaults({}, spec, { id: plugin.id }));
|
||||
let app = this.apps.new(_.defaults({}, spec, {
|
||||
id: plugin.id,
|
||||
urlBasePath: this.urlBasePath
|
||||
}));
|
||||
plugin.apps.add(app);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -12,10 +12,13 @@ module.exports = async (kbnServer, server, config) => {
|
|||
let UiBundlerEnv = require('./UiBundlerEnv');
|
||||
let loadingGif = readFile(fromRoot('src/ui/public/loading.gif'), { encoding: 'base64'});
|
||||
|
||||
let uiExports = kbnServer.uiExports = new UiExports(kbnServer);
|
||||
let uiExports = kbnServer.uiExports = new UiExports({
|
||||
urlBasePath: config.get('server.basePath')
|
||||
});
|
||||
|
||||
let bundlerEnv = new UiBundlerEnv(config.get('optimize.bundleDir'));
|
||||
bundlerEnv.addContext('env', config.get('env.name'));
|
||||
bundlerEnv.addContext('urlBasePath', config.get('server.basePath'));
|
||||
bundlerEnv.addContext('sourceMaps', config.get('optimize.sourceMaps'));
|
||||
bundlerEnv.addContext('kbnVersion', config.get('pkg.version'));
|
||||
bundlerEnv.addContext('buildNum', config.get('pkg.buildNum'));
|
||||
|
@ -70,6 +73,7 @@ module.exports = async (kbnServer, server, config) => {
|
|||
version: kbnServer.version,
|
||||
buildNum: config.get('pkg.buildNum'),
|
||||
buildSha: config.get('pkg.buildSha'),
|
||||
basePath: config.get('server.basePath'),
|
||||
vars: defaults(app.getInjectedVars(), defaultInjectedVars),
|
||||
xsrfToken: this.issueXsrfToken(),
|
||||
};
|
||||
|
@ -77,7 +81,8 @@ module.exports = async (kbnServer, server, config) => {
|
|||
return this.view(app.templateName, {
|
||||
app: app,
|
||||
loadingGif: loadingGif,
|
||||
kibanaPayload: payload
|
||||
kibanaPayload: payload,
|
||||
bundlePath: `${config.get('server.basePath')}/bundles`,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
40
src/ui/public/chrome/api/__tests__/nav.js
Normal file
40
src/ui/public/chrome/api/__tests__/nav.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
import expect from 'expect.js';
|
||||
|
||||
import initChromeNavApi from 'ui/chrome/api/nav';
|
||||
|
||||
const basePath = '/someBasePath';
|
||||
|
||||
function getChrome(customInternals = { basePath }) {
|
||||
const chrome = {};
|
||||
initChromeNavApi(chrome, {
|
||||
nav: [],
|
||||
...customInternals,
|
||||
});
|
||||
return chrome;
|
||||
}
|
||||
|
||||
describe('chrome nav apis', function () {
|
||||
describe('#getBasePath()', function () {
|
||||
it('returns the basePath', function () {
|
||||
const chrome = getChrome();
|
||||
expect(chrome.getBasePath()).to.be(basePath);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#addBasePath()', function () {
|
||||
it('returns undefined when nothing is passed', function () {
|
||||
const chrome = getChrome();
|
||||
expect(chrome.addBasePath()).to.be(undefined);
|
||||
});
|
||||
|
||||
it('prepends the base path when the input is a path', function () {
|
||||
const chrome = getChrome();
|
||||
expect(chrome.addBasePath('/other/path')).to.be(`${basePath}/other/path`);
|
||||
});
|
||||
|
||||
it('ignores non-path urls', function () {
|
||||
const chrome = getChrome();
|
||||
expect(chrome.addBasePath('http://github.com/elastic/kibana')).to.be('http://github.com/elastic/kibana');
|
||||
});
|
||||
});
|
||||
});
|
2
src/ui/public/chrome/api/angular.js
vendored
2
src/ui/public/chrome/api/angular.js
vendored
|
@ -21,7 +21,7 @@ module.exports = function (chrome, internals) {
|
|||
.value('sessionId', Date.now())
|
||||
.value('esUrl', (function () {
|
||||
var a = document.createElement('a');
|
||||
a.href = '/elasticsearch';
|
||||
a.href = chrome.addBasePath('/elasticsearch');
|
||||
return a.href;
|
||||
}()))
|
||||
.config(chrome.$setupXsrfRequestInterceptor)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
module.exports = function (chrome, internals) {
|
||||
const { startsWith } = require('lodash');
|
||||
const { startsWith, isString } = require('lodash');
|
||||
import { parse, format } from 'url';
|
||||
|
||||
export default function (chrome, internals) {
|
||||
chrome.getNavLinks = function () {
|
||||
return internals.nav;
|
||||
};
|
||||
|
@ -9,6 +10,30 @@ module.exports = function (chrome, internals) {
|
|||
return internals.appUrlStore.getItem(`lastSubUrl:${url}`);
|
||||
};
|
||||
|
||||
chrome.getBasePath = function () {
|
||||
return internals.basePath || '';
|
||||
};
|
||||
|
||||
chrome.addBasePath = function (url) {
|
||||
var isUrl = url && isString(url);
|
||||
if (!isUrl) return url;
|
||||
|
||||
var parsed = parse(url);
|
||||
if (!parsed.host && parsed.pathname) {
|
||||
if (parsed.pathname[0] === '/') {
|
||||
parsed.pathname = chrome.getBasePath() + parsed.pathname;
|
||||
}
|
||||
}
|
||||
|
||||
return format({
|
||||
protocol: parsed.protocol,
|
||||
host: parsed.host,
|
||||
pathname: parsed.pathname,
|
||||
query: parsed.query,
|
||||
hash: parsed.hash,
|
||||
});
|
||||
};
|
||||
|
||||
internals.trackPossibleSubUrl = function (url) {
|
||||
for (const link of internals.nav) {
|
||||
if (startsWith(url, link.url)) {
|
||||
|
|
|
@ -9,12 +9,12 @@ require('ui/private');
|
|||
require('ui/promises');
|
||||
|
||||
var metadata = require('ui/metadata');
|
||||
var TabCollection = require('ui/chrome/TabCollection');
|
||||
|
||||
var chrome = {};
|
||||
var internals = _.defaults(
|
||||
_.cloneDeep(metadata),
|
||||
{
|
||||
basePath: '',
|
||||
rootController: null,
|
||||
rootTemplate: null,
|
||||
showAppsLink: null,
|
||||
|
|
23
src/ui/public/directives/kbnHref.js
Normal file
23
src/ui/public/directives/kbnHref.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
import UiModules from 'ui/modules';
|
||||
import chrome from 'ui/chrome';
|
||||
import { words, camelCase, kebabCase } from 'lodash';
|
||||
|
||||
export function kbnUrlDirective(name) {
|
||||
const srcAttr = kebabCase(name);
|
||||
const attr = kebabCase(words(name).slice(1));
|
||||
|
||||
UiModules
|
||||
.get('kibana')
|
||||
.directive(name, function (Private) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function ($scope, $el, $attr) {
|
||||
$attr.$observe(name, function (val) {
|
||||
$attr.$set(attr, chrome.addBasePath(val));
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
kbnUrlDirective('kbnHref');
|
3
src/ui/public/directives/kbnSrc.js
Normal file
3
src/ui/public/directives/kbnSrc.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { kbnUrlDirective } from './kbnHref';
|
||||
|
||||
kbnUrlDirective('kbnSrc');
|
|
@ -38,11 +38,17 @@ block content
|
|||
loading.removeChild(loading.lastChild);
|
||||
}
|
||||
|
||||
function bundleFile(filename) {
|
||||
var anchor = document.createElement('a');
|
||||
anchor.setAttribute('href', !{JSON.stringify(bundlePath)} + '/' + filename);
|
||||
return anchor.href;
|
||||
}
|
||||
|
||||
var files = [
|
||||
'/bundles/commons.style.css',
|
||||
'/bundles/#{app.id}.style.css',
|
||||
'/bundles/commons.bundle.js',
|
||||
'/bundles/#{app.id}.bundle.js'
|
||||
bundleFile('commons.style.css'),
|
||||
bundleFile('#{app.id}.style.css'),
|
||||
bundleFile('commons.bundle.js'),
|
||||
bundleFile('#{app.id}.bundle.js')
|
||||
];
|
||||
|
||||
(function next() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue