feat: new Web UI build system
- use generator-gulp-angular by @swiip - remove old static file
This commit is contained in:
parent
587b17c120
commit
b7a71edfcb
83 changed files with 1069 additions and 1028 deletions
3
webui/.bowerrc
Normal file
3
webui/.bowerrc
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"directory": "bower_components"
|
||||
}
|
13
webui/.editorconfig
Normal file
13
webui/.editorconfig
Normal file
|
@ -0,0 +1,13 @@
|
|||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
13
webui/.eslintrc
Normal file
13
webui/.eslintrc
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"extends": "eslint:recommended",
|
||||
"plugins": ["angular"],
|
||||
"env": {
|
||||
"browser": true,
|
||||
"jasmine": true
|
||||
},
|
||||
"globals": {
|
||||
"angular": true,
|
||||
"module": true,
|
||||
"inject": true
|
||||
}
|
||||
}
|
5
webui/.gitignore
vendored
Normal file
5
webui/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
node_modules/
|
||||
bower_components/
|
||||
coverage/
|
||||
.sass-cache/
|
||||
.tmp/
|
74
webui/.yo-rc.json
Normal file
74
webui/.yo-rc.json
Normal file
|
@ -0,0 +1,74 @@
|
|||
{
|
||||
"generator-gulp-angular": {
|
||||
"version": "1.0.2",
|
||||
"props": {
|
||||
"angularVersion": "~1.4.2",
|
||||
"angularModules": [
|
||||
{
|
||||
"key": "animate",
|
||||
"module": "ngAnimate"
|
||||
},
|
||||
{
|
||||
"key": "cookies",
|
||||
"module": "ngCookies"
|
||||
},
|
||||
{
|
||||
"key": "sanitize",
|
||||
"module": "ngSanitize"
|
||||
},
|
||||
{
|
||||
"key": "messages",
|
||||
"module": "ngMessages"
|
||||
},
|
||||
{
|
||||
"key": "aria",
|
||||
"module": "ngAria"
|
||||
}
|
||||
],
|
||||
"jQuery": {
|
||||
"key": "jquery2"
|
||||
},
|
||||
"resource": {
|
||||
"key": "angular-resource",
|
||||
"module": "ngResource"
|
||||
},
|
||||
"router": {
|
||||
"key": "ui-router",
|
||||
"module": "ui.router"
|
||||
},
|
||||
"ui": {
|
||||
"key": "bootstrap",
|
||||
"module": null
|
||||
},
|
||||
"bootstrapComponents": {
|
||||
"key": "ui-bootstrap",
|
||||
"module": "ui.bootstrap"
|
||||
},
|
||||
"cssPreprocessor": {
|
||||
"key": "node-sass",
|
||||
"extension": "scss"
|
||||
},
|
||||
"jsPreprocessor": {
|
||||
"key": "noJsPrepro",
|
||||
"extension": "js",
|
||||
"srcExtension": "js"
|
||||
},
|
||||
"htmlPreprocessor": {
|
||||
"key": "noHtmlPrepro",
|
||||
"extension": "html"
|
||||
},
|
||||
"foundationComponents": {
|
||||
"name": null,
|
||||
"version": null,
|
||||
"key": null,
|
||||
"module": null
|
||||
},
|
||||
"paths": {
|
||||
"src": "src",
|
||||
"dist": "../static",
|
||||
"e2e": "e2e",
|
||||
"tmp": ".tmp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
45
webui/bower.json
Normal file
45
webui/bower.json
Normal file
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"name": "traefik",
|
||||
"version": "2.0.0",
|
||||
"homepage": "http://traefik.io",
|
||||
"authors": [
|
||||
"Fernandez Ludovic <lfernandez.dev@gmail.com>"
|
||||
],
|
||||
"description": "Front end for Træfɪk",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"angular-animate": "~1.4.2",
|
||||
"angular-cookies": "~1.4.2",
|
||||
"angular-sanitize": "~1.4.2",
|
||||
"angular-messages": "~1.4.2",
|
||||
"angular-aria": "~1.4.2",
|
||||
"jquery": "~2.1.4",
|
||||
"angular-resource": "~1.4.2",
|
||||
"angular-ui-router": "~0.2.15",
|
||||
"bootstrap-sass": "~3.3.5",
|
||||
"angular-bootstrap": "~0.13.4",
|
||||
"moment": "~2.10.6",
|
||||
"animate.css": "~3.4.0",
|
||||
"angular": "~1.4.2",
|
||||
"angular-nvd3": "~1.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-mocks": "~1.4.2"
|
||||
},
|
||||
"overrides": {
|
||||
"bootstrap-sass": {
|
||||
"main": [
|
||||
"assets/stylesheets/_bootstrap.scss",
|
||||
"assets/fonts/bootstrap/glyphicons-halflings-regular.eot",
|
||||
"assets/fonts/bootstrap/glyphicons-halflings-regular.svg",
|
||||
"assets/fonts/bootstrap/glyphicons-halflings-regular.ttf",
|
||||
"assets/fonts/bootstrap/glyphicons-halflings-regular.woff",
|
||||
"assets/fonts/bootstrap/glyphicons-halflings-regular.woff2"
|
||||
]
|
||||
}
|
||||
},
|
||||
"resolutions": {
|
||||
"jquery": "~2.1.4",
|
||||
"angular": "~1.4.2"
|
||||
}
|
||||
}
|
9
webui/e2e/.eslintrc
Normal file
9
webui/e2e/.eslintrc
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"globals": {
|
||||
"browser": false,
|
||||
"element": false,
|
||||
"by": false,
|
||||
"$": false,
|
||||
"$$": false
|
||||
}
|
||||
}
|
5
webui/gulp/.eslintrc
Normal file
5
webui/gulp/.eslintrc
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"env": {
|
||||
"node": true
|
||||
}
|
||||
}
|
98
webui/gulp/build.js
Normal file
98
webui/gulp/build.js
Normal file
|
@ -0,0 +1,98 @@
|
|||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
var gulp = require('gulp');
|
||||
var conf = require('./conf');
|
||||
|
||||
var $ = require('gulp-load-plugins')({
|
||||
pattern: ['gulp-*', 'main-bower-files', 'uglify-save-license', 'del']
|
||||
});
|
||||
|
||||
gulp.task('partials', function () {
|
||||
return gulp.src([
|
||||
path.join(conf.paths.src, '/app/**/*.html'),
|
||||
path.join(conf.paths.tmp, '/serve/app/**/*.html')
|
||||
])
|
||||
.pipe($.minifyHtml({
|
||||
empty: true,
|
||||
spare: true,
|
||||
quotes: true
|
||||
}))
|
||||
.pipe($.angularTemplatecache('templateCacheHtml.js', {
|
||||
module: 'traefik',
|
||||
root: 'app'
|
||||
}))
|
||||
.pipe(gulp.dest(conf.paths.tmp + '/partials/'));
|
||||
});
|
||||
|
||||
gulp.task('html', ['inject', 'partials'], function () {
|
||||
var partialsInjectFile = gulp.src(path.join(conf.paths.tmp, '/partials/templateCacheHtml.js'), { read: false });
|
||||
var partialsInjectOptions = {
|
||||
starttag: '<!-- inject:partials -->',
|
||||
ignorePath: path.join(conf.paths.tmp, '/partials'),
|
||||
addRootSlash: false
|
||||
};
|
||||
|
||||
var htmlFilter = $.filter('*.html', { restore: true });
|
||||
var jsFilter = $.filter('**/*.js', { restore: true });
|
||||
var cssFilter = $.filter('**/*.css', { restore: true });
|
||||
var assets;
|
||||
|
||||
return gulp.src(path.join(conf.paths.tmp, '/serve/*.html'))
|
||||
.pipe($.inject(partialsInjectFile, partialsInjectOptions))
|
||||
.pipe(assets = $.useref.assets())
|
||||
.pipe($.rev())
|
||||
.pipe(jsFilter)
|
||||
.pipe($.sourcemaps.init())
|
||||
.pipe($.ngAnnotate())
|
||||
.pipe($.uglify({ preserveComments: $.uglifySaveLicense })).on('error', conf.errorHandler('Uglify'))
|
||||
.pipe($.sourcemaps.write('maps'))
|
||||
.pipe(jsFilter.restore)
|
||||
.pipe(cssFilter)
|
||||
.pipe($.sourcemaps.init())
|
||||
.pipe($.replace('../../bower_components/bootstrap-sass/assets/fonts/bootstrap/', '../fonts/'))
|
||||
.pipe($.minifyCss({ processImport: false }))
|
||||
.pipe($.sourcemaps.write('maps'))
|
||||
.pipe(cssFilter.restore)
|
||||
.pipe(assets.restore())
|
||||
.pipe($.useref())
|
||||
.pipe($.revReplace())
|
||||
.pipe(htmlFilter)
|
||||
.pipe($.minifyHtml({
|
||||
empty: true,
|
||||
spare: true,
|
||||
quotes: true,
|
||||
conditionals: true
|
||||
}))
|
||||
.pipe(htmlFilter.restore)
|
||||
.pipe(gulp.dest(path.join(conf.paths.dist, '/')))
|
||||
.pipe($.size({ title: path.join(conf.paths.dist, '/'), showFiles: true }));
|
||||
});
|
||||
|
||||
// Only applies for fonts from bower dependencies
|
||||
// Custom fonts are handled by the "other" task
|
||||
gulp.task('fonts', function () {
|
||||
return gulp.src($.mainBowerFiles())
|
||||
.pipe($.filter('**/*.{eot,svg,ttf,woff,woff2}'))
|
||||
.pipe($.flatten())
|
||||
.pipe(gulp.dest(path.join(conf.paths.dist, '/fonts/')));
|
||||
});
|
||||
|
||||
gulp.task('other', function () {
|
||||
var fileFilter = $.filter(function (file) {
|
||||
return file.stat.isFile();
|
||||
});
|
||||
|
||||
return gulp.src([
|
||||
path.join(conf.paths.src, '/**/*'),
|
||||
path.join('!' + conf.paths.src, '/**/*.{html,css,js,scss}')
|
||||
])
|
||||
.pipe(fileFilter)
|
||||
.pipe(gulp.dest(path.join(conf.paths.dist, '/')));
|
||||
});
|
||||
|
||||
gulp.task('clean', function () {
|
||||
return $.del([path.join(conf.paths.dist, '/'), path.join(conf.paths.tmp, '/')]);
|
||||
});
|
||||
|
||||
gulp.task('build', ['html', 'fonts', 'other']);
|
41
webui/gulp/conf.js
Normal file
41
webui/gulp/conf.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* This file contains the variables used in other gulp files
|
||||
* which defines tasks
|
||||
* By design, we only put there very generic config values
|
||||
* which are used in several places to keep good readability
|
||||
* of the tasks
|
||||
*/
|
||||
|
||||
var gutil = require('gulp-util');
|
||||
|
||||
/**
|
||||
* The main paths of your project handle these with care
|
||||
*/
|
||||
exports.paths = {
|
||||
src: 'src',
|
||||
dist: '../static',
|
||||
tmp: '.tmp',
|
||||
e2e: 'e2e'
|
||||
};
|
||||
|
||||
/**
|
||||
* Wiredep is the lib which inject bower dependencies in your project
|
||||
* Mainly used to inject script tags in the index.html but also used
|
||||
* to inject css preprocessor deps and js files in karma
|
||||
*/
|
||||
exports.wiredep = {
|
||||
exclude: [/\/bootstrap\.js$/, /\/bootstrap-sass\/.*\.js/, /\/bootstrap\.css/],
|
||||
directory: 'bower_components'
|
||||
};
|
||||
|
||||
/**
|
||||
* Common implementation for an error handler of a Gulp plugin
|
||||
*/
|
||||
exports.errorHandler = function(title) {
|
||||
'use strict';
|
||||
|
||||
return function(err) {
|
||||
gutil.log(gutil.colors.red('[' + title + ']'), err.toString());
|
||||
this.emit('end');
|
||||
};
|
||||
};
|
38
webui/gulp/e2e-tests.js
Normal file
38
webui/gulp/e2e-tests.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
var gulp = require('gulp');
|
||||
var conf = require('./conf');
|
||||
|
||||
var browserSync = require('browser-sync');
|
||||
|
||||
var $ = require('gulp-load-plugins')();
|
||||
|
||||
// Downloads the selenium webdriver
|
||||
gulp.task('webdriver-update', $.protractor.webdriver_update);
|
||||
|
||||
gulp.task('webdriver-standalone', $.protractor.webdriver_standalone);
|
||||
|
||||
function runProtractor (done) {
|
||||
var params = process.argv;
|
||||
var args = params.length > 3 ? [params[3], params[4]] : [];
|
||||
|
||||
gulp.src(path.join(conf.paths.e2e, '/**/*.js'))
|
||||
.pipe($.protractor.protractor({
|
||||
configFile: 'protractor.conf.js',
|
||||
args: args
|
||||
}))
|
||||
.on('error', function (err) {
|
||||
// Make sure failed tests cause gulp to exit non-zero
|
||||
throw err;
|
||||
})
|
||||
.on('end', function () {
|
||||
// Close browser sync server
|
||||
browserSync.exit();
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
gulp.task('protractor', ['protractor:src']);
|
||||
gulp.task('protractor:src', ['serve:e2e', 'webdriver-update'], runProtractor);
|
||||
gulp.task('protractor:dist', ['serve:e2e-dist', 'webdriver-update'], runProtractor);
|
42
webui/gulp/inject.js
Normal file
42
webui/gulp/inject.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
var gulp = require('gulp');
|
||||
var conf = require('./conf');
|
||||
|
||||
var $ = require('gulp-load-plugins')();
|
||||
|
||||
var wiredep = require('wiredep').stream;
|
||||
var _ = require('lodash');
|
||||
|
||||
var browserSync = require('browser-sync');
|
||||
|
||||
gulp.task('inject-reload', ['inject'], function() {
|
||||
browserSync.reload();
|
||||
});
|
||||
|
||||
gulp.task('inject', ['scripts', 'styles'], function () {
|
||||
var injectStyles = gulp.src([
|
||||
path.join(conf.paths.tmp, '/serve/app/**/*.css'),
|
||||
path.join('!' + conf.paths.tmp, '/serve/app/vendor.css')
|
||||
], { read: false });
|
||||
|
||||
var injectScripts = gulp.src([
|
||||
path.join(conf.paths.src, '/app/**/*.module.js'),
|
||||
path.join(conf.paths.src, '/app/**/*.js'),
|
||||
path.join('!' + conf.paths.src, '/app/**/*.spec.js'),
|
||||
path.join('!' + conf.paths.src, '/app/**/*.mock.js'),
|
||||
])
|
||||
.pipe($.angularFilesort()).on('error', conf.errorHandler('AngularFilesort'));
|
||||
|
||||
var injectOptions = {
|
||||
ignorePath: [conf.paths.src, path.join(conf.paths.tmp, '/serve')],
|
||||
addRootSlash: false
|
||||
};
|
||||
|
||||
return gulp.src(path.join(conf.paths.src, '/*.html'))
|
||||
.pipe($.inject(injectStyles, injectOptions))
|
||||
.pipe($.inject(injectScripts, injectOptions))
|
||||
.pipe(wiredep(_.extend({}, conf.wiredep)))
|
||||
.pipe(gulp.dest(path.join(conf.paths.tmp, '/serve')));
|
||||
});
|
26
webui/gulp/scripts.js
Normal file
26
webui/gulp/scripts.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
var gulp = require('gulp');
|
||||
var conf = require('./conf');
|
||||
|
||||
var browserSync = require('browser-sync');
|
||||
|
||||
var $ = require('gulp-load-plugins')();
|
||||
|
||||
|
||||
gulp.task('scripts-reload', function() {
|
||||
return buildScripts()
|
||||
.pipe(browserSync.stream());
|
||||
});
|
||||
|
||||
gulp.task('scripts', function() {
|
||||
return buildScripts();
|
||||
});
|
||||
|
||||
function buildScripts() {
|
||||
return gulp.src(path.join(conf.paths.src, '/app/**/*.js'))
|
||||
.pipe($.eslint())
|
||||
.pipe($.eslint.format())
|
||||
.pipe($.size())
|
||||
};
|
63
webui/gulp/server.js
Normal file
63
webui/gulp/server.js
Normal file
|
@ -0,0 +1,63 @@
|
|||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
var gulp = require('gulp');
|
||||
var conf = require('./conf');
|
||||
|
||||
var browserSync = require('browser-sync');
|
||||
var browserSyncSpa = require('browser-sync-spa');
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var proxyMiddleware = require('http-proxy-middleware');
|
||||
|
||||
function browserSyncInit(baseDir, browser) {
|
||||
browser = browser === undefined ? 'default' : browser;
|
||||
|
||||
var routes = null;
|
||||
if(baseDir === conf.paths.src || (util.isArray(baseDir) && baseDir.indexOf(conf.paths.src) !== -1)) {
|
||||
routes = {
|
||||
'/bower_components': 'bower_components'
|
||||
};
|
||||
}
|
||||
|
||||
var server = {
|
||||
baseDir: baseDir,
|
||||
routes: routes
|
||||
};
|
||||
|
||||
/*
|
||||
* You can add a proxy to your backend by uncommenting the line below.
|
||||
* You just have to configure a context which will we redirected and the target url.
|
||||
* Example: $http.get('/users') requests will be automatically proxified.
|
||||
*
|
||||
* For more details and option, https://github.com/chimurai/http-proxy-middleware/blob/v0.9.0/README.md
|
||||
*/
|
||||
// server.middleware = proxyMiddleware('/users', {target: 'http://jsonplaceholder.typicode.com', changeOrigin: true});
|
||||
|
||||
browserSync.instance = browserSync.init({
|
||||
startPath: '/',
|
||||
server: server,
|
||||
browser: browser
|
||||
});
|
||||
}
|
||||
|
||||
browserSync.use(browserSyncSpa({
|
||||
selector: '[ng-app]'// Only needed for angular apps
|
||||
}));
|
||||
|
||||
gulp.task('serve', ['watch'], function () {
|
||||
browserSyncInit([path.join(conf.paths.tmp, '/serve'), conf.paths.src]);
|
||||
});
|
||||
|
||||
gulp.task('serve:dist', ['build'], function () {
|
||||
browserSyncInit(conf.paths.dist);
|
||||
});
|
||||
|
||||
gulp.task('serve:e2e', ['inject'], function () {
|
||||
browserSyncInit([conf.paths.tmp + '/serve', conf.paths.src], []);
|
||||
});
|
||||
|
||||
gulp.task('serve:e2e-dist', ['build'], function () {
|
||||
browserSyncInit(conf.paths.dist, []);
|
||||
});
|
54
webui/gulp/styles.js
Normal file
54
webui/gulp/styles.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
var gulp = require('gulp');
|
||||
var conf = require('./conf');
|
||||
|
||||
var browserSync = require('browser-sync');
|
||||
|
||||
var $ = require('gulp-load-plugins')();
|
||||
|
||||
var wiredep = require('wiredep').stream;
|
||||
var _ = require('lodash');
|
||||
|
||||
gulp.task('styles-reload', ['styles'], function() {
|
||||
return buildStyles()
|
||||
.pipe(browserSync.stream());
|
||||
});
|
||||
|
||||
gulp.task('styles', function() {
|
||||
return buildStyles();
|
||||
});
|
||||
|
||||
var buildStyles = function() {
|
||||
var sassOptions = {
|
||||
style: 'expanded'
|
||||
};
|
||||
|
||||
var injectFiles = gulp.src([
|
||||
path.join(conf.paths.src, '/app/**/*.scss'),
|
||||
path.join('!' + conf.paths.src, '/app/index.scss')
|
||||
], { read: false });
|
||||
|
||||
var injectOptions = {
|
||||
transform: function(filePath) {
|
||||
filePath = filePath.replace(conf.paths.src + '/app/', '');
|
||||
return '@import "' + filePath + '";';
|
||||
},
|
||||
starttag: '// injector',
|
||||
endtag: '// endinjector',
|
||||
addRootSlash: false
|
||||
};
|
||||
|
||||
|
||||
return gulp.src([
|
||||
path.join(conf.paths.src, '/app/index.scss')
|
||||
])
|
||||
.pipe($.inject(injectFiles, injectOptions))
|
||||
.pipe(wiredep(_.extend({}, conf.wiredep)))
|
||||
.pipe($.sourcemaps.init())
|
||||
.pipe($.sass(sassOptions)).on('error', conf.errorHandler('Sass'))
|
||||
.pipe($.autoprefixer()).on('error', conf.errorHandler('Autoprefixer'))
|
||||
.pipe($.sourcemaps.write())
|
||||
.pipe(gulp.dest(path.join(conf.paths.tmp, '/serve/app/')));
|
||||
};
|
52
webui/gulp/unit-tests.js
Normal file
52
webui/gulp/unit-tests.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
var gulp = require('gulp');
|
||||
var conf = require('./conf');
|
||||
|
||||
var karma = require('karma');
|
||||
|
||||
var pathSrcHtml = [
|
||||
path.join(conf.paths.src, '/**/*.html')
|
||||
];
|
||||
|
||||
var pathSrcJs = [
|
||||
path.join(conf.paths.src, '/**/!(*.spec).js')
|
||||
];
|
||||
|
||||
function runTests (singleRun, done) {
|
||||
var reporters = ['progress'];
|
||||
var preprocessors = {};
|
||||
|
||||
pathSrcHtml.forEach(function(path) {
|
||||
preprocessors[path] = ['ng-html2js'];
|
||||
});
|
||||
|
||||
if (singleRun) {
|
||||
pathSrcJs.forEach(function(path) {
|
||||
preprocessors[path] = ['coverage'];
|
||||
});
|
||||
reporters.push('coverage')
|
||||
}
|
||||
|
||||
var localConfig = {
|
||||
configFile: path.join(__dirname, '/../karma.conf.js'),
|
||||
singleRun: singleRun,
|
||||
autoWatch: !singleRun,
|
||||
reporters: reporters,
|
||||
preprocessors: preprocessors
|
||||
};
|
||||
|
||||
var server = new karma.Server(localConfig, function(failCount) {
|
||||
done(failCount ? new Error("Failed " + failCount + " tests.") : null);
|
||||
})
|
||||
server.start();
|
||||
}
|
||||
|
||||
gulp.task('test', ['scripts'], function(done) {
|
||||
runTests(true, done);
|
||||
});
|
||||
|
||||
gulp.task('test:auto', ['watch'], function(done) {
|
||||
runTests(false, done);
|
||||
});
|
39
webui/gulp/watch.js
Normal file
39
webui/gulp/watch.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
var gulp = require('gulp');
|
||||
var conf = require('./conf');
|
||||
|
||||
var browserSync = require('browser-sync');
|
||||
|
||||
function isOnlyChange(event) {
|
||||
return event.type === 'changed';
|
||||
}
|
||||
|
||||
gulp.task('watch', ['inject'], function () {
|
||||
|
||||
gulp.watch([path.join(conf.paths.src, '/*.html'), 'bower.json'], ['inject-reload']);
|
||||
|
||||
gulp.watch([
|
||||
path.join(conf.paths.src, '/app/**/*.css'),
|
||||
path.join(conf.paths.src, '/app/**/*.scss')
|
||||
], function(event) {
|
||||
if(isOnlyChange(event)) {
|
||||
gulp.start('styles-reload');
|
||||
} else {
|
||||
gulp.start('inject-reload');
|
||||
}
|
||||
});
|
||||
|
||||
gulp.watch(path.join(conf.paths.src, '/app/**/*.js'), function(event) {
|
||||
if(isOnlyChange(event)) {
|
||||
gulp.start('scripts-reload');
|
||||
} else {
|
||||
gulp.start('inject-reload');
|
||||
}
|
||||
});
|
||||
|
||||
gulp.watch(path.join(conf.paths.src, '/app/**/*.html'), function(event) {
|
||||
browserSync.reload(event.path);
|
||||
});
|
||||
});
|
29
webui/gulpfile.js
Normal file
29
webui/gulpfile.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* Welcome to your gulpfile!
|
||||
* The gulp tasks are splitted in several files in the gulp directory
|
||||
* because putting all here was really too long
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var gulp = require('gulp');
|
||||
var wrench = require('wrench');
|
||||
|
||||
/**
|
||||
* This will load all js or coffee files in the gulp directory
|
||||
* in order to load all gulp tasks
|
||||
*/
|
||||
wrench.readdirSyncRecursive('./gulp').filter(function(file) {
|
||||
return (/\.(js|coffee)$/i).test(file);
|
||||
}).map(function(file) {
|
||||
require('./gulp/' + file);
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Default task clean temporaries directories and launch the
|
||||
* main optimization build task
|
||||
*/
|
||||
gulp.task('default', ['clean'], function () {
|
||||
gulp.start('build');
|
||||
});
|
110
webui/karma.conf.js
Normal file
110
webui/karma.conf.js
Normal file
|
@ -0,0 +1,110 @@
|
|||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
var conf = require('./gulp/conf');
|
||||
|
||||
var _ = require('lodash');
|
||||
var wiredep = require('wiredep');
|
||||
|
||||
var pathSrcHtml = [
|
||||
path.join(conf.paths.src, '/**/*.html')
|
||||
];
|
||||
|
||||
function listFiles() {
|
||||
var wiredepOptions = _.extend({}, conf.wiredep, {
|
||||
dependencies: true,
|
||||
devDependencies: true
|
||||
});
|
||||
|
||||
var patterns = wiredep(wiredepOptions).js
|
||||
.concat([
|
||||
path.join(conf.paths.src, '/app/**/*.module.js'),
|
||||
path.join(conf.paths.src, '/app/**/*.js'),
|
||||
path.join(conf.paths.src, '/**/*.spec.js'),
|
||||
path.join(conf.paths.src, '/**/*.mock.js'),
|
||||
])
|
||||
.concat(pathSrcHtml);
|
||||
|
||||
var files = patterns.map(function(pattern) {
|
||||
return {
|
||||
pattern: pattern
|
||||
};
|
||||
});
|
||||
files.push({
|
||||
pattern: path.join(conf.paths.src, '/assets/**/*'),
|
||||
included: false,
|
||||
served: true,
|
||||
watched: false
|
||||
});
|
||||
return files;
|
||||
}
|
||||
|
||||
module.exports = function(config) {
|
||||
|
||||
var configuration = {
|
||||
files: listFiles(),
|
||||
|
||||
singleRun: true,
|
||||
|
||||
autoWatch: false,
|
||||
|
||||
ngHtml2JsPreprocessor: {
|
||||
stripPrefix: conf.paths.src + '/',
|
||||
moduleName: 'traefik'
|
||||
},
|
||||
|
||||
logLevel: 'WARN',
|
||||
|
||||
frameworks: ['jasmine', 'angular-filesort'],
|
||||
|
||||
angularFilesort: {
|
||||
whitelist: [path.join(conf.paths.src, '/**/!(*.html|*.spec|*.mock).js')]
|
||||
},
|
||||
|
||||
browsers : ['PhantomJS'],
|
||||
|
||||
plugins : [
|
||||
'karma-phantomjs-launcher',
|
||||
'karma-angular-filesort',
|
||||
'karma-coverage',
|
||||
'karma-jasmine',
|
||||
'karma-ng-html2js-preprocessor'
|
||||
],
|
||||
|
||||
coverageReporter: {
|
||||
type : 'html',
|
||||
dir : 'coverage/'
|
||||
},
|
||||
|
||||
reporters: ['progress'],
|
||||
|
||||
proxies: {
|
||||
'/assets/': path.join('/base/', conf.paths.src, '/assets/')
|
||||
}
|
||||
};
|
||||
|
||||
// This is the default preprocessors configuration for a usage with Karma cli
|
||||
// The coverage preprocessor is added in gulp/unit-test.js only for single tests
|
||||
// It was not possible to do it there because karma doesn't let us now if we are
|
||||
// running a single test or not
|
||||
configuration.preprocessors = {};
|
||||
pathSrcHtml.forEach(function(path) {
|
||||
configuration.preprocessors[path] = ['ng-html2js'];
|
||||
});
|
||||
|
||||
// This block is needed to execute Chrome on Travis
|
||||
// If you ever plan to use Chrome and Travis, you can keep it
|
||||
// If not, you can safely remove it
|
||||
// https://github.com/karma-runner/karma/issues/1144#issuecomment-53633076
|
||||
if(configuration.browsers[0] === 'Chrome' && process.env.TRAVIS) {
|
||||
configuration.customLaunchers = {
|
||||
'chrome-travis-ci': {
|
||||
base: 'Chrome',
|
||||
flags: ['--no-sandbox']
|
||||
}
|
||||
};
|
||||
configuration.browsers = ['chrome-travis-ci'];
|
||||
}
|
||||
|
||||
config.set(configuration);
|
||||
};
|
55
webui/package.json
Normal file
55
webui/package.json
Normal file
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"name": "traefik",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {},
|
||||
"scripts": {
|
||||
"test": "gulp test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"estraverse": "~4.1.0",
|
||||
"gulp": "~3.9.0",
|
||||
"gulp-autoprefixer": "~3.0.2",
|
||||
"gulp-angular-templatecache": "~1.8.0",
|
||||
"del": "~2.0.2",
|
||||
"lodash": "~3.10.1",
|
||||
"gulp-minify-css": "~1.2.1",
|
||||
"gulp-filter": "~3.0.1",
|
||||
"gulp-flatten": "~0.2.0",
|
||||
"gulp-eslint": "~1.0.0",
|
||||
"eslint-plugin-angular": "~0.12.0",
|
||||
"gulp-load-plugins": "~0.10.0",
|
||||
"gulp-size": "~2.0.0",
|
||||
"gulp-uglify": "~1.4.1",
|
||||
"gulp-useref": "~1.3.0",
|
||||
"gulp-util": "~3.0.6",
|
||||
"gulp-ng-annotate": "~1.1.0",
|
||||
"gulp-replace": "~0.5.4",
|
||||
"gulp-rename": "~1.2.2",
|
||||
"gulp-rev": "~6.0.1",
|
||||
"gulp-rev-replace": "~0.4.2",
|
||||
"gulp-minify-html": "~1.0.4",
|
||||
"gulp-inject": "~3.0.0",
|
||||
"gulp-protractor": "~1.0.0",
|
||||
"gulp-sourcemaps": "~1.6.0",
|
||||
"gulp-sass": "~2.0.4",
|
||||
"gulp-angular-filesort": "~1.1.1",
|
||||
"main-bower-files": "~2.9.0",
|
||||
"wiredep": "~2.2.2",
|
||||
"karma": "~0.13.10",
|
||||
"karma-jasmine": "~0.3.6",
|
||||
"karma-phantomjs-launcher": "~0.2.1",
|
||||
"phantomjs": "~1.9.18",
|
||||
"karma-angular-filesort": "~1.0.0",
|
||||
"karma-coverage": "~0.5.2",
|
||||
"karma-ng-html2js-preprocessor": "~0.2.0",
|
||||
"browser-sync": "~2.9.11",
|
||||
"browser-sync-spa": "~1.0.3",
|
||||
"http-proxy-middleware": "~0.9.0",
|
||||
"chalk": "~1.1.1",
|
||||
"uglify-save-license": "~0.4.1",
|
||||
"wrench": "~1.5.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
}
|
27
webui/protractor.conf.js
Normal file
27
webui/protractor.conf.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
'use strict';
|
||||
|
||||
var paths = require('./.yo-rc.json')['generator-gulp-angular'].props.paths;
|
||||
|
||||
// An example configuration file.
|
||||
exports.config = {
|
||||
// The address of a running selenium server.
|
||||
//seleniumAddress: 'http://localhost:4444/wd/hub',
|
||||
//seleniumServerJar: deprecated, this should be set on node_modules/protractor/config.json
|
||||
|
||||
// Capabilities to be passed to the webdriver instance.
|
||||
capabilities: {
|
||||
'browserName': 'chrome'
|
||||
},
|
||||
|
||||
baseUrl: 'http://localhost:3000',
|
||||
|
||||
// Spec patterns are relative to the current working directory when
|
||||
// protractor is called.
|
||||
specs: [paths.e2e + '/**/*.js'],
|
||||
|
||||
// Options to be passed to Jasmine-node.
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 30000
|
||||
}
|
||||
};
|
37
webui/readme.md
Normal file
37
webui/readme.md
Normal file
|
@ -0,0 +1,37 @@
|
|||
# Træfɪk Web UI
|
||||
|
||||
Access to Træfɪk Web UI, ex: http://localhost:8080
|
||||
|
||||
## Interface
|
||||
|
||||
Træfɪk Web UI provide 2 types of informations:
|
||||
- Providers with their backends and frontends information.
|
||||
- Health of the web server.
|
||||
|
||||
|
||||
## Build
|
||||
|
||||
Install [Node](https://nodejs.org).
|
||||
|
||||
Do `npm install` and `bower install`.
|
||||
|
||||
Run in web developer mode : `gulp serve`
|
||||
|
||||
Build Web UI : `gulp build`
|
||||
|
||||
|
||||
## Libraries
|
||||
|
||||
- [Node](https://nodejs.org)
|
||||
- [NPM](https://npmjs.com)
|
||||
- [NPM - Documentation](https://docs.npmjs.com/)
|
||||
- [Generator Gulp-Angular](https://github.com/Swiip/generator-gulp-angular)
|
||||
- [AngularJS](https://docs.angularjs.org/api)
|
||||
- [UI Router](https://github.com/angular-ui/ui-router)
|
||||
- [UI Router - Documentation](https://github.com/angular-ui/ui-router/wiki)
|
||||
- [Bootstrap](http://getbootstrap.com)
|
||||
- [Angular Bootstrap](https://angular-ui.github.io/bootstrap)
|
||||
- [D3](http://d3js.org)
|
||||
- [D3 - Documentation](https://github.com/mbostock/d3/wiki)
|
||||
- [NVD3](http://nvd3.org)
|
||||
- [Angular nvD3](http://krispo.github.io/angular-nvd3)
|
13
webui/src/app/core/health.resource.js
Normal file
13
webui/src/app/core/health.resource.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('traefik.core.health', ['ngResource'])
|
||||
.factory('Health', Health);
|
||||
|
||||
/** @ngInject */
|
||||
function Health($resource) {
|
||||
return $resource('/health');
|
||||
}
|
||||
|
||||
})();
|
13
webui/src/app/core/providers.resource.js
Normal file
13
webui/src/app/core/providers.resource.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('traefik.core.provider', ['ngResource'])
|
||||
.factory('Providers', Providers);
|
||||
|
||||
/** @ngInject */
|
||||
function Providers($resource) {
|
||||
return $resource('/api/providers');
|
||||
}
|
||||
|
||||
})();
|
15
webui/src/app/index.config.js
Normal file
15
webui/src/app/index.config.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('traefik')
|
||||
.config(config);
|
||||
|
||||
/** @ngInject */
|
||||
function config($logProvider) {
|
||||
// Enable log
|
||||
$logProvider.debugEnabled(true);
|
||||
|
||||
}
|
||||
|
||||
})();
|
9
webui/src/app/index.constants.js
Normal file
9
webui/src/app/index.constants.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
/* global moment:false */
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('traefik')
|
||||
.constant('moment', moment);
|
||||
|
||||
})();
|
7
webui/src/app/index.module.js
Normal file
7
webui/src/app/index.module.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('traefik', ['ngAnimate', 'ngCookies', 'ngSanitize', 'ngMessages', 'ngAria', 'ngResource', 'ui.router', 'ui.bootstrap', 'traefik.section']);
|
||||
|
||||
})();
|
14
webui/src/app/index.run.js
Normal file
14
webui/src/app/index.run.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('traefik')
|
||||
.run(runBlock);
|
||||
|
||||
/** @ngInject */
|
||||
function runBlock($log) {
|
||||
|
||||
$log.debug('runBlock end');
|
||||
}
|
||||
|
||||
})();
|
35
webui/src/app/index.scss
Normal file
35
webui/src/app/index.scss
Normal file
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* If you want to override some bootstrap variables, you have to change values here.
|
||||
* The list of variables are listed here bower_components/bootstrap-sass/assets/stylesheets/bootstrap/_variables.scss
|
||||
*/
|
||||
$navbar-inverse-link-color: #5AADBB;
|
||||
$icon-font-path: "../../bower_components/bootstrap-sass/assets/fonts/bootstrap/";
|
||||
|
||||
/**
|
||||
* Do not remove the comments below. It's the markers used by wiredep to inject
|
||||
* sass dependencies when defined in the bower.json of your dependencies
|
||||
*/
|
||||
// bower:scss
|
||||
// endbower
|
||||
|
||||
.browsehappy {
|
||||
margin: 0.2em 0;
|
||||
background: #ccc;
|
||||
color: #000;
|
||||
padding: 0.2em 0;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
height: 200px;
|
||||
|
||||
img.pull-right {
|
||||
width: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not remove the comments below. It's the markers used by gulp-inject to inject
|
||||
* all your sass files automatically
|
||||
*/
|
||||
// injector
|
||||
// endinjector
|
209
webui/src/app/sections/health/health.controller.js
Normal file
209
webui/src/app/sections/health/health.controller.js
Normal file
|
@ -0,0 +1,209 @@
|
|||
/* global d3:false */
|
||||
(function (d3) {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('traefik.section.health')
|
||||
.controller('HealthController', HealthController);
|
||||
|
||||
/** @ngInject */
|
||||
function HealthController($scope, $interval, $log, Health) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
vm.graph = {
|
||||
averageResponseTime: {},
|
||||
totalStatusCodeCount: {}
|
||||
};
|
||||
|
||||
vm.graph.totalStatusCodeCount.options = {
|
||||
"chart": {
|
||||
type: 'discreteBarChart',
|
||||
height: 200,
|
||||
margin: {
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 40,
|
||||
left: 55
|
||||
},
|
||||
x: function (d) {
|
||||
return d.label;
|
||||
},
|
||||
y: function (d) {
|
||||
return d.value;
|
||||
},
|
||||
showValues: true,
|
||||
valueFormat: function (d) {
|
||||
return d3.format('d')(d);
|
||||
},
|
||||
transitionDuration: 50,
|
||||
yAxis: {
|
||||
axisLabelDistance: 30
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"enable": true,
|
||||
"text": "Total Status Code Count",
|
||||
"css": {
|
||||
"textAlign": "center"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
vm.graph.totalStatusCodeCount.data = [
|
||||
{
|
||||
key: "Total Status Code Count",
|
||||
values: [
|
||||
{
|
||||
"label": "200",
|
||||
"value": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* Update Total Status Code Count graph
|
||||
*
|
||||
* @param {Object} totalStatusCodeCount Object from API
|
||||
*/
|
||||
function updateTotalStatusCodeCount(totalStatusCodeCount) {
|
||||
|
||||
// extract values
|
||||
vm.graph.totalStatusCodeCount.data[0].values = [];
|
||||
for (var code in totalStatusCodeCount) {
|
||||
if (totalStatusCodeCount.hasOwnProperty(code)) {
|
||||
vm.graph.totalStatusCodeCount.data[0].values.push({
|
||||
label: code,
|
||||
value: totalStatusCodeCount[code]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Update Total Status Code Count graph render
|
||||
if (vm.graph.totalStatusCodeCount.api) {
|
||||
vm.graph.totalStatusCodeCount.api.update();
|
||||
} else {
|
||||
$log.error('fail');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
vm.graph.averageResponseTime.options = {
|
||||
chart: {
|
||||
type: 'lineChart',
|
||||
height: 200,
|
||||
margin: {
|
||||
top: 20,
|
||||
right: 40,
|
||||
bottom: 40,
|
||||
left: 55
|
||||
},
|
||||
transitionDuration: 50,
|
||||
x: function (d) {
|
||||
return d.x;
|
||||
},
|
||||
y: function (d) {
|
||||
return d.y;
|
||||
},
|
||||
useInteractiveGuideline: true,
|
||||
xAxis: {
|
||||
tickFormat: function (d) {
|
||||
return d3.time.format('%X')(new Date(d));
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
tickFormat: function (d) {
|
||||
return d3.format(',.1f')(d);
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"enable": true,
|
||||
"text": "Average response time",
|
||||
"css": {
|
||||
"textAlign": "center"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var initialPoint = {
|
||||
x: Date.now() - 3000,
|
||||
y: 0
|
||||
};
|
||||
vm.graph.averageResponseTime.data = [
|
||||
{
|
||||
values: [initialPoint],
|
||||
key: 'Average response time (ms)',
|
||||
type: 'line',
|
||||
color: '#2ca02c'
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* Update average response time graph
|
||||
*
|
||||
* @param {Number} x Coordinate X
|
||||
* @param {Number} y Coordinate Y
|
||||
*/
|
||||
function updateAverageResponseTimeGraph(x, y) {
|
||||
|
||||
// x multiply 1000 by because unix time is in seconds and JS Date are in milliseconds
|
||||
var data = {
|
||||
x: x * 1000,
|
||||
y: y * 1000
|
||||
};
|
||||
vm.graph.averageResponseTime.data[0].values.push(data);
|
||||
// limit graph entries
|
||||
if (vm.graph.averageResponseTime.data[0].values.length > 100) {
|
||||
vm.graph.averageResponseTime.data[0].values.shift();
|
||||
}
|
||||
|
||||
// Update Average Response Time graph render
|
||||
if (vm.graph.averageResponseTime.api) {
|
||||
vm.graph.averageResponseTime.api.update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all graph's datas
|
||||
*
|
||||
* @param {Object} health Health data from server
|
||||
*/
|
||||
function loadData(health) {
|
||||
// Load datas and update Average Response Time graph render
|
||||
updateAverageResponseTimeGraph(health.unixtime, health.average_response_time_sec);
|
||||
|
||||
// Load datas and update Total Status Code Count graph render
|
||||
updateTotalStatusCodeCount(health.total_status_code_count);
|
||||
|
||||
// set data's view
|
||||
vm.health = health;
|
||||
}
|
||||
|
||||
/**
|
||||
* Action when load datas failed
|
||||
*
|
||||
* @param {Object} error Error state object
|
||||
*/
|
||||
function erroData(error) {
|
||||
vm.health = {};
|
||||
$log.error(error);
|
||||
}
|
||||
|
||||
// first load
|
||||
Health.get(loadData, erroData);
|
||||
|
||||
// Auto refresh data
|
||||
var intervalId = $interval(function () {
|
||||
Health.get(loadData, erroData);
|
||||
}, 3000);
|
||||
|
||||
// Stop auto refresh when page change
|
||||
$scope.$on('$destroy', function () {
|
||||
$interval.cancel(intervalId);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
})(d3);
|
43
webui/src/app/sections/health/health.html
Normal file
43
webui/src/app/sections/health/health.html
Normal file
|
@ -0,0 +1,43 @@
|
|||
<div>
|
||||
<h1 class="text-danger">
|
||||
<span class="glyphicon glyphicon-heart" aria-hidden="true"></span> Health
|
||||
</h1>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-6">
|
||||
<div>
|
||||
<nvd3 options="healthCtrl.graph.averageResponseTime.options" data="healthCtrl.graph.averageResponseTime.data" api="healthCtrl.graph.averageResponseTime.api"></nvd3>
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<span>Total response time :</span><span class="badge">{{healthCtrl.health.total_response_time}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<span>PID :</span><span class="badge">{{healthCtrl.health.pid}}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span>Uptime :</span><span class="badge">{{healthCtrl.health.uptime}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div>
|
||||
<nvd3 options="healthCtrl.graph.totalStatusCodeCount.options" data="healthCtrl.graph.totalStatusCodeCount.data" api="healthCtrl.graph.totalStatusCodeCount.api"></nvd3>
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<span>Total count :</span><span class="badge">{{healthCtrl.health.total_count}}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span>Count :</span><span class="badge">{{healthCtrl.health.count}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
19
webui/src/app/sections/health/health.module.js
Normal file
19
webui/src/app/sections/health/health.module.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular.module('traefik.section.health', ['traefik.core.health'])
|
||||
.config(config);
|
||||
|
||||
/** @ngInject */
|
||||
function config($stateProvider) {
|
||||
|
||||
$stateProvider.state('health', {
|
||||
url: '/health',
|
||||
templateUrl: 'app/sections/health/health.html',
|
||||
controller: 'HealthController',
|
||||
controllerAs: 'healthCtrl'
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
})();
|
|
@ -0,0 +1,26 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('traefik.section.providers.backend-monitor')
|
||||
.directive('backendMonitor', backendMonitor);
|
||||
|
||||
function backendMonitor() {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
templateUrl: 'app/sections/providers/backend-monitor/backend-monitor.html',
|
||||
controller: BackendMonitorController,
|
||||
controllerAs: 'backendCtrl',
|
||||
bindToController: true,
|
||||
scope: {
|
||||
backend: '=',
|
||||
backendId: '='
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function BackendMonitorController() {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
})();
|
|
@ -0,0 +1,23 @@
|
|||
<div class="panel panel-success">
|
||||
<div class="panel-heading">
|
||||
<strong><span class="glyphicon glyphicon-tasks" aria-hidden="true"></span> {{backendCtrl.backendId}}</strong>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table class="panel-table__servers table table-striped table-hover">
|
||||
<tr>
|
||||
<td><em>Server</em></td>
|
||||
<td><em>URL</em></td>
|
||||
<td><em>Weight</em></td>
|
||||
</tr>
|
||||
<tr data-ng-repeat="(serverId, server) in backendCtrl.backend.servers">
|
||||
<td>{{serverId}}</td>
|
||||
<td><code><a data-ng-href="{{server.url}}">{{server.url}}</a></code></td>
|
||||
<td>{{server.weight}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="panel-footer" data-ng-show="backendCtrl.backend.loadBalancer || backendCtrl.backend.circuitBreaker">
|
||||
<span data-ng-show="backendCtrl.backend.loadBalancer" class="label label-success">Load Balancer: {{backendCtrl.backend.loadBalancer.method}}</span>
|
||||
<span data-ng-show="backendCtrl.backend.circuitBreaker" class="label label-success">Circuit Breaker: {{backendCtrl.backend.circuitBreaker.expression}}</span>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,7 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('traefik.section.providers.backend-monitor', []);
|
||||
|
||||
})();
|
|
@ -0,0 +1,26 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('traefik.section.providers.frontend-monitor')
|
||||
.directive('frontendMonitor', frontendMonitor);
|
||||
|
||||
function frontendMonitor() {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
templateUrl: 'app/sections/providers/frontend-monitor/frontend-monitor.html',
|
||||
controller: FrontendMonitorController,
|
||||
controllerAs: 'frontendCtrl',
|
||||
bindToController: true,
|
||||
scope: {
|
||||
frontend: '=',
|
||||
frontendId: '='
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function FrontendMonitorController() {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
})();
|
|
@ -0,0 +1,23 @@
|
|||
<div class="panel panel-warning">
|
||||
<div class="panel-heading">
|
||||
<strong><span class="glyphicon glyphicon-globe" aria-hidden="true"></span> {{frontendCtrl.frontendId}}</strong>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table class="panel-table__routes table table-striped table-hover">
|
||||
<tr>
|
||||
<td><em>Route</em></td>
|
||||
<td><em>Rule</em></td>
|
||||
<td><em>Value</em></td>
|
||||
</tr>
|
||||
<tr data-ng-repeat="(routeId, route) in frontendCtrl.frontend.routes">
|
||||
<td>{{routeId}}</td>
|
||||
<td>{{route.rule}}</td>
|
||||
<td><code>{{route.value}}</code></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div data-bg-show="frontendCtrl.frontend.backend" class="panel-footer">
|
||||
<span class="label label-warning" role="button" data-toggle="collapse" href="#{{frontendCtrl.frontend.backend}}" aria-expanded="false">{{frontendCtrl.frontend.backend}}</span>
|
||||
<span data-ng-show="frontendCtrl.frontend.passHostHeader" class="label label-warning">Pass Host Header</span>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,6 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular.module('traefik.section.providers.frontend-monitor', []);
|
||||
|
||||
})();
|
28
webui/src/app/sections/providers/providers.controller.js
Normal file
28
webui/src/app/sections/providers/providers.controller.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('traefik.section.providers')
|
||||
.controller('ProvidersController', ProvidersController);
|
||||
|
||||
/** @ngInject */
|
||||
function ProvidersController($scope, $interval, $log, Providers) {
|
||||
var vm = this;
|
||||
|
||||
vm.providers = Providers.get();
|
||||
|
||||
var intervalId = $interval(function () {
|
||||
Providers.get(function (providers) {
|
||||
vm.providers = providers;
|
||||
}, function (error) {
|
||||
vm.providers = {};
|
||||
$log.error(error);
|
||||
});
|
||||
}, 2000);
|
||||
|
||||
$scope.$on('$destroy', function () {
|
||||
$interval.cancel(intervalId);
|
||||
});
|
||||
}
|
||||
|
||||
})();
|
20
webui/src/app/sections/providers/providers.html
Normal file
20
webui/src/app/sections/providers/providers.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
<div>
|
||||
<tabset>
|
||||
<tab data-ng-repeat="(providerId, provider) in providersCtrl.providers" heading="{{providerId}}">
|
||||
|
||||
<div class="row tabset-row__providers">
|
||||
<div class="col-md-6">
|
||||
<div data-ng-repeat="(frontendId, frontend) in provider.frontends">
|
||||
<frontend-monitor data-provider-id="providerId" data-frontend-id="frontendId" data-frontend="frontend"></frontend-monitor>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div data-ng-repeat="(backendId, backend) in provider.backends">
|
||||
<backend-monitor data-provider-id="providerId" data-backend-id="backendId" data-backend="backend"></backend-monitor>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</tab>
|
||||
</tabset>
|
||||
</div>
|
24
webui/src/app/sections/providers/providers.module.js
Normal file
24
webui/src/app/sections/providers/providers.module.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('traefik.section.providers', [
|
||||
'traefik.core.provider',
|
||||
'traefik.section.providers.backend-monitor',
|
||||
'traefik.section.providers.frontend-monitor'
|
||||
])
|
||||
.config(config);
|
||||
|
||||
/** @ngInject */
|
||||
function config($stateProvider) {
|
||||
|
||||
$stateProvider.state('provider', {
|
||||
url: '/',
|
||||
templateUrl: 'app/sections/providers/providers.html',
|
||||
controller: 'ProvidersController',
|
||||
controllerAs: 'providersCtrl'
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
})();
|
13
webui/src/app/sections/sections.config.js
Normal file
13
webui/src/app/sections/sections.config.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('traefik.section')
|
||||
.config(config);
|
||||
|
||||
/** @ngInject */
|
||||
function config($urlRouterProvider) {
|
||||
$urlRouterProvider.otherwise('/');
|
||||
}
|
||||
|
||||
})();
|
13
webui/src/app/sections/sections.module.js
Normal file
13
webui/src/app/sections/sections.module.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('traefik.section', [
|
||||
'ui.router',
|
||||
'ui.bootstrap',
|
||||
'nvd3',
|
||||
'traefik.section.providers',
|
||||
'traefik.section.health'
|
||||
]);
|
||||
|
||||
})();
|
26
webui/src/app/traefik.scss
Normal file
26
webui/src/app/traefik.scss
Normal file
|
@ -0,0 +1,26 @@
|
|||
|
||||
@font-face {
|
||||
font-family: 'charterregular';
|
||||
src: url('../assets/fonts/charter_regular-webfont.eot');
|
||||
src: url('../assets/fonts/charter_regular-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('../assets/fonts/charter_regular-webfont.woff') format('woff');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.traefik-blue {
|
||||
color: #00B1FF;
|
||||
}
|
||||
|
||||
.traefik-text {
|
||||
font-family: 'charterregular', Arial, sans-serif;
|
||||
}
|
||||
|
||||
.panel-body .panel-table__servers,
|
||||
.panel-body .panel-table__routes {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.tabset-row__providers {
|
||||
margin-top: 3rem;
|
||||
}
|
BIN
webui/src/assets/fonts/charter_regular-webfont.eot
Normal file
BIN
webui/src/assets/fonts/charter_regular-webfont.eot
Normal file
Binary file not shown.
BIN
webui/src/assets/fonts/charter_regular-webfont.ttf
Normal file
BIN
webui/src/assets/fonts/charter_regular-webfont.ttf
Normal file
Binary file not shown.
BIN
webui/src/assets/fonts/charter_regular-webfont.woff
Normal file
BIN
webui/src/assets/fonts/charter_regular-webfont.woff
Normal file
Binary file not shown.
BIN
webui/src/favicon.ico
Normal file
BIN
webui/src/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
76
webui/src/index.html
Normal file
76
webui/src/index.html
Normal file
|
@ -0,0 +1,76 @@
|
|||
<!doctype html>
|
||||
<html ng-app="traefik">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>/ˈTræfɪk/</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
|
||||
|
||||
<!-- build:css({.tmp/serve,src}) styles/vendor.css -->
|
||||
<!-- bower:css -->
|
||||
<!-- run `gulp inject` to automatically populate bower styles dependencies -->
|
||||
<!-- endbower -->
|
||||
<!-- endbuild -->
|
||||
|
||||
<!-- build:css({.tmp/serve,src}) styles/app.css -->
|
||||
<!-- inject:css -->
|
||||
<!-- css files will be automatically insert here -->
|
||||
<!-- endinject -->
|
||||
<!-- endbuild -->
|
||||
</head>
|
||||
<body>
|
||||
<!--[if lt IE 10]>
|
||||
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
||||
<![endif]-->
|
||||
|
||||
<div class="container">
|
||||
<header>
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand traefik-text" ui-sref="provider">/ˈTr<span class="traefik-blue">æ</span>fɪk/</a>
|
||||
</div>
|
||||
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a ui-sref="provider" class="active">Providers</a></li>
|
||||
<li><a ui-sref="health">Health</a></li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<a href="https://github.com/EmileVauge/traefik/blob/master/docs/index.md" target="_blank">Documentation</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://traefik.io" target="_blank"><span class="traefik-blue">traefik.io</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="main">
|
||||
<div data-ui-view></div>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- build:js(src) scripts/vendor.js -->
|
||||
<!-- bower:js -->
|
||||
<!-- run `gulp inject` to automatically populate bower script dependencies -->
|
||||
<!-- endbower -->
|
||||
<!-- endbuild -->
|
||||
|
||||
<!-- build:js({.tmp/serve,.tmp/partials,src}) scripts/app.js -->
|
||||
<!-- inject:js -->
|
||||
<!-- js files will be automatically insert here -->
|
||||
<!-- endinject -->
|
||||
|
||||
<!-- inject:partials -->
|
||||
<!-- angular templates will be automatically converted in js and inserted here -->
|
||||
<!-- endinject -->
|
||||
<!-- endbuild -->
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue