Merge pull request #558 from micaelmbagira/webui-webpack-fountainjs

Move webui to FountainJS with Webpack
This commit is contained in:
Emile Vauge 2016-07-27 22:29:17 +02:00 committed by GitHub
commit 2a596b8162
58 changed files with 1027 additions and 1239 deletions

View file

@ -73,7 +73,7 @@ run-dev:
generate-webui: build-webui
if [ ! -d "static" ]; then \
mkdir -p static; \
docker run --rm -v "$$PWD/static":'/src/static' traefik-webui gulp; \
docker run --rm -v "$$PWD/static":'/src/static' traefik-webui npm run build; \
echo 'For more informations show `webui/readme.md`' > $$PWD/static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md; \
fi

View file

@ -1,3 +0,0 @@
{
"directory": "bower_components"
}

1
webui/.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
* text=auto

9
webui/.gitignore vendored
View file

@ -1,5 +1,6 @@
node_modules/
bower_components/
coverage/
.sass-cache/
.tmp/
coverage/
dist/
node_modules/
.sass-cache/

View file

@ -1,74 +1,20 @@
{
"generator-gulp-angular": {
"version": "1.0.2",
"generator-fountain-angular1": {
"version": "0.6.0",
"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"
"resolved": "/Users/micael/Documents/zenika/fountain/generator-fountain-angular1/generators/app/index.js",
"namespace": "fountain-angular1:app",
"argv": {
"remain": [],
"cooked": [],
"original": []
},
"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"
}
"framework": "angular1",
"modules": "webpack",
"css": "scss",
"js": "js",
"sample": "hello",
"router": "none"
}
}
}

View file

@ -3,16 +3,11 @@ FROM node:6.3.0
ENV WEBUI_DIR /src/webui
RUN mkdir -p $WEBUI_DIR
RUN npm install -g gulp bower
COPY package.json $WEBUI_DIR/
COPY .bowerrc $WEBUI_DIR/
COPY bower.json $WEBUI_DIR/
WORKDIR $WEBUI_DIR
RUN npm set progress=false
RUN npm install --quiet
RUN bower install --allow-root --quiet
COPY . $WEBUI_DIR/

View file

@ -1,45 +0,0 @@
{
"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"
}
}

View file

@ -0,0 +1,12 @@
const conf = require('./gulp.conf');
module.exports = function () {
return {
server: {
baseDir: [
conf.paths.dist
]
},
open: false
};
};

13
webui/conf/browsersync.conf.js Executable file
View file

@ -0,0 +1,13 @@
const conf = require('./gulp.conf');
module.exports = function () {
return {
server: {
baseDir: [
conf.paths.tmp,
conf.paths.src
]
},
open: false
};
};

47
webui/conf/gulp.conf.js Normal file
View file

@ -0,0 +1,47 @@
'use strict';
/**
* 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
*/
const path = require('path');
const gutil = require('gulp-util');
exports.ngModule = 'traefik';
/**
* The main paths of your project handle these with care
*/
exports.paths = {
src: 'src',
dist: '../static',
tmp: '.tmp',
e2e: 'e2e',
tasks: 'gulp_tasks'
};
exports.path = {};
for (const pathName in exports.paths) {
if (exports.paths.hasOwnProperty(pathName)) {
exports.path[pathName] = function pathJoin() {
const pathValue = exports.paths[pathName];
const funcArgs = Array.prototype.slice.call(arguments);
const joinArgs = [pathValue].concat(funcArgs);
return path.join.apply(this, joinArgs);
};
}
}
/**
* Common implementation for an error handler of a Gulp plugin
*/
exports.errorHandler = function (title) {
return function (err) {
gutil.log(gutil.colors.red(`[${title}]`), err.toString());
this.emit('end');
};
};

View file

@ -0,0 +1,55 @@
const conf = require('./gulp.conf');
module.exports = function (config) {
const configuration = {
basePath: '../',
singleRun: false,
autoWatch: true,
logLevel: 'INFO',
junitReporter: {
outputDir: 'test-reports'
},
browsers: [
'PhantomJS'
],
frameworks: [
'jasmine'
],
files: [
'node_modules/es6-shim/es6-shim.js',
conf.path.src('index.spec.js'),
conf.path.src('**/*.html')
],
preprocessors: {
[conf.path.src('index.spec.js')]: [
'webpack'
],
[conf.path.src('**/*.html')]: [
'ng-html2js'
]
},
ngHtml2JsPreprocessor: {
stripPrefix: `${conf.paths.src}/`
},
reporters: ['progress', 'coverage'],
coverageReporter: {
type: 'html',
dir: 'coverage/'
},
webpack: require('./webpack-test.conf'),
webpackMiddleware: {
noInfo: true
},
plugins: [
require('karma-jasmine'),
require('karma-junit-reporter'),
require('karma-coverage'),
require('karma-phantomjs-launcher'),
require('karma-phantomjs-shim'),
require('karma-ng-html2js-preprocessor'),
require('karma-webpack')
]
};
config.set(configuration);
};

55
webui/conf/karma.conf.js Normal file
View file

@ -0,0 +1,55 @@
const conf = require('./gulp.conf');
module.exports = function (config) {
const configuration = {
basePath: '../',
singleRun: true,
autoWatch: false,
logLevel: 'INFO',
junitReporter: {
outputDir: 'test-reports'
},
browsers: [
'PhantomJS'
],
frameworks: [
'jasmine'
],
files: [
'node_modules/es6-shim/es6-shim.js',
conf.path.src('index.spec.js'),
conf.path.src('**/*.html')
],
preprocessors: {
[conf.path.src('index.spec.js')]: [
'webpack'
],
[conf.path.src('**/*.html')]: [
'ng-html2js'
]
},
ngHtml2JsPreprocessor: {
stripPrefix: `${conf.paths.src}/`
},
reporters: ['progress', 'coverage'],
coverageReporter: {
type: 'html',
dir: 'coverage/'
},
webpack: require('./webpack-test.conf'),
webpackMiddleware: {
noInfo: true
},
plugins: [
require('karma-jasmine'),
require('karma-junit-reporter'),
require('karma-coverage'),
require('karma-phantomjs-launcher'),
require('karma-phantomjs-shim'),
require('karma-ng-html2js-preprocessor'),
require('karma-webpack')
]
};
config.set(configuration);
};

View file

@ -0,0 +1,74 @@
const webpack = require('webpack');
const conf = require('./gulp.conf');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const SplitByPathPlugin = require('webpack-split-by-path');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const autoprefixer = require('autoprefixer');
module.exports = {
module: {
preLoaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint'
}
],
loaders: [
{
test: /.json$/,
loaders: [
'json'
]
},
{
test: /\.(css|scss)$/,
loaders: ExtractTextPlugin.extract('style', 'css?minimize!sass', 'postcss')
},
{
test: /\.js$/,
exclude: /node_modules/,
loaders: [
'ng-annotate'
]
},
{
test: /.html$/,
loaders: [
'html'
]
},
{
test: /\.(png|woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=100000'
}
]
},
plugins: [
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.NoErrorsPlugin(),
new HtmlWebpackPlugin({
template: conf.path.src('index.html'),
inject: true
}),
new webpack.optimize.UglifyJsPlugin({
compress: {unused: true, dead_code: true} // eslint-disable-line camelcase
}),
new SplitByPathPlugin([{
name: 'vendor',
path: path.join(__dirname, '../node_modules')
}]),
new ExtractTextPlugin('./index-[contenthash].css')
],
postcss: () => [autoprefixer],
output: {
path: path.join(process.cwd(), conf.paths.dist),
filename: './[name]-[hash].js'
},
entry: {
app: `./${conf.path.src('index')}`
}
};

View file

@ -0,0 +1,41 @@
module.exports = {
module: {
preLoaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint'
}
],
loaders: [
{
test: /.json$/,
loaders: [
'json'
]
},
{
test: /\.js$/,
exclude: /node_modules/,
loaders: [
'ng-annotate'
]
},
{
test: /.html$/,
loaders: [
'html'
]
},
{
test: /\.js$/,
exclude: /(node_modules|.*\.spec\.js)/,
loader: 'isparta'
}
]
},
plugins: [],
debug: true,
devtool: 'cheap-module-eval-source-map'
};

View file

@ -0,0 +1,61 @@
const webpack = require('webpack');
const conf = require('./gulp.conf');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const autoprefixer = require('autoprefixer');
module.exports = {
module: {
loaders: [
{
test: /.json$/,
loaders: [
'json'
]
},
{
test: /\.(css|scss)$/,
loaders: [
'style',
'css',
'sass',
'postcss'
]
},
{
test: /\.js$/,
exclude: /node_modules/,
loaders: [
'ng-annotate'
]
},
{
test: /.html$/,
loaders: [
'html'
]
},
{
test: /\.(png|woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=100000'
}
]
},
plugins: [
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.NoErrorsPlugin(),
new HtmlWebpackPlugin({
template: conf.path.src('index.html'),
inject: true
})
],
postcss: () => [autoprefixer],
debug: true,
devtool: 'cheap-module-eval-source-map',
output: {
path: path.join(process.cwd(), conf.paths.tmp),
filename: 'index.js'
},
entry: `./${conf.path.src('index')}`
};

View file

@ -1,9 +0,0 @@
{
"globals": {
"browser": false,
"element": false,
"by": false,
"$": false,
"$$": false
}
}

View file

@ -1,5 +0,0 @@
{
"env": {
"node": true
}
}

View file

@ -1,98 +0,0 @@
'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, '/**'), '!' + conf.paths.dist, path.join(conf.paths.tmp, '/')], {force: true});
});
gulp.task('build', ['html', 'fonts', 'other']);

View file

@ -1,41 +0,0 @@
/**
* 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');
};
};

View file

@ -1,38 +0,0 @@
'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);

View file

@ -1,42 +0,0 @@
'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')));
});

View file

@ -1,26 +0,0 @@
'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())
};

View file

@ -1,63 +0,0 @@
'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, []);
});

View file

@ -1,54 +0,0 @@
'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/')));
};

View file

@ -1,52 +0,0 @@
'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);
});

View file

@ -1,39 +0,0 @@
'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);
});
});

View file

@ -0,0 +1,21 @@
const gulp = require('gulp');
const browserSync = require('browser-sync');
const spa = require('browser-sync-spa');
const browserSyncConf = require('../conf/browsersync.conf');
const browserSyncDistConf = require('../conf/browsersync-dist.conf');
browserSync.use(spa());
gulp.task('browsersync', browserSyncServe);
gulp.task('browsersync:dist', browserSyncDist);
function browserSyncServe(done) {
browserSync.init(browserSyncConf());
done();
}
function browserSyncDist(done) {
browserSync.init(browserSyncDistConf());
done();
}

25
webui/gulp_tasks/karma.js Normal file
View file

@ -0,0 +1,25 @@
const path = require('path');
const gulp = require('gulp');
const karma = require('karma');
gulp.task('karma:single-run', karmaSingleRun);
gulp.task('karma:auto-run', karmaAutoRun);
function karmaFinishHandler(done) {
return failCount => {
done(failCount ? new Error(`Failed ${failCount} tests.`) : null);
};
}
function karmaSingleRun(done) {
const configFile = path.join(process.cwd(), 'conf', 'karma.conf.js');
const karmaServer = new karma.Server({configFile}, karmaFinishHandler(done));
karmaServer.start();
}
function karmaAutoRun(done) {
const configFile = path.join(process.cwd(), 'conf', 'karma-auto.conf.js');
const karmaServer = new karma.Server({configFile}, karmaFinishHandler(done));
karmaServer.start();
}

25
webui/gulp_tasks/misc.js Normal file
View file

@ -0,0 +1,25 @@
const path = require('path');
const gulp = require('gulp');
const del = require('del');
const filter = require('gulp-filter');
const conf = require('../conf/gulp.conf');
gulp.task('clean', clean);
gulp.task('other', other);
function clean() {
return del([conf.paths.tmp]);
}
function other() {
const fileFilter = filter(file => file.stat.isFile());
return gulp.src([
path.join(conf.paths.src, '/**/*'),
path.join(`!${conf.paths.src}`, '/**/*.{scss,js,html}')
])
.pipe(fileFilter)
.pipe(gulp.dest(conf.paths.dist));
}

View file

@ -0,0 +1,48 @@
/* eslint angular/module-getter:0 */
const gulp = require('gulp');
const gutil = require('gulp-util');
const webpack = require('webpack');
const webpackConf = require('../conf/webpack.conf');
const webpackDistConf = require('../conf/webpack-dist.conf');
const browsersync = require('browser-sync');
gulp.task('webpack:dev', done => {
webpackWrapper(false, webpackConf, done);
});
gulp.task('webpack:watch', done => {
webpackWrapper(true, webpackConf, done);
});
gulp.task('webpack:dist', done => {
webpackWrapper(false, webpackDistConf, done);
});
function webpackWrapper(watch, conf, done) {
const webpackBundler = webpack(conf);
const webpackChangeHandler = (err, stats) => {
if (err) {
conf.errorHandler('Webpack')(err);
}
gutil.log(stats.toString({
colors: true,
chunks: false,
hash: false,
version: false
}));
if (done) {
done();
done = null;
} else {
browsersync.reload();
}
};
if (watch) {
webpackBundler.watch(200, webpackChangeHandler);
} else {
webpackBundler.run(webpackChangeHandler);
}
}

View file

@ -1,29 +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
*/
const gulp = require('gulp');
const HubRegistry = require('gulp-hub');
const browserSync = require('browser-sync');
'use strict';
const conf = require('./conf/gulp.conf');
var gulp = require('gulp');
var wrench = require('wrench');
// Load some files into the registry
const hub = new HubRegistry([conf.path.tasks('*.js')]);
/**
* 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);
});
// Tell gulp to use the tasks just loaded
gulp.registry(hub);
gulp.task('build', gulp.series(gulp.parallel('other', 'webpack:dist')));
gulp.task('test', gulp.series('karma:single-run'));
gulp.task('test:auto', gulp.series('karma:auto-run'));
gulp.task('serve', gulp.series('webpack:watch', 'watch', 'browsersync'));
gulp.task('serve:dist', gulp.series('default', 'browsersync:dist'));
gulp.task('default', gulp.series('clean', 'build'));
gulp.task('watch', watch);
/**
* Default task clean temporaries directories and launch the
* main optimization build task
*/
gulp.task('default', ['clean'], function () {
gulp.start('build');
});
function reloadBrowserSync(cb) {
browserSync.reload();
cb();
}
function watch(done) {
gulp.watch(conf.path.src('app/**/*.html'), reloadBrowserSync);
done();
}

View file

@ -1,110 +0,0 @@
'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);
};

View file

@ -1,55 +1,102 @@
{
"name": "traefik",
"version": "0.0.0",
"dependencies": {},
"scripts": {
"test": "gulp test"
"version": "2.0.0",
"homepage": "http://traefik.io",
"authors": [
"Fernandez Ludovic <lfernandez.dev@gmail.com>",
"Micaël Mbagira <micael.mbagira@icloud.com>"
],
"description": "Front end for Træfɪk",
"license": "MIT",
"dependencies": {
"angular": "^1.4.2",
"angular-animate": "^1.5.8",
"animate.css": "^3.4.0",
"angular-aria": "^1.5.8",
"angular-cookies": "^1.5.8",
"angular-messages": "^1.5.8",
"angular-nvd3": "^1.0.8",
"angular-resource": "^1.5.8",
"angular-sanitize": "^1.5.8",
"angular-ui-bootstrap": "^2.0.0",
"angular-ui-router": "^0.3.1",
"bootstrap": "^3.3.6",
"moment": "^2.14.1"
},
"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"
"angular-mocks": "^1.4.2",
"autoprefixer": "^6.2.2",
"babel-loader": "^6.2.0",
"browser-sync": "^2.9.11",
"browser-sync-spa": "^1.0.3",
"css-loader": "^0.23.1",
"del": "^2.0.2",
"es6-shim": "^0.35.0",
"eslint": "^2.11.0",
"eslint-config-angular": "^0.5.0",
"eslint-config-xo-space": "^0.12.0",
"eslint-loader": "^1.3.0",
"eslint-plugin-angular": "^1.3.0",
"extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.9.0",
"gulp": "gulpjs/gulp#4ed9a4a3275559c73a396eff7e1fde3824951ebb",
"gulp-angular-filesort": "^1.1.1",
"gulp-angular-templatecache": "^1.8.0",
"gulp-filter": "^4.0.0",
"gulp-htmlmin": "^1.3.0",
"gulp-hub": "frankwallis/gulp-hub#d461b9c700df9010d0a8694e4af1fb96d9f38bf4",
"gulp-insert": "^0.5.0",
"gulp-ng-annotate": "^1.1.0",
"gulp-sass": "^2.1.1",
"gulp-util": "^3.0.7",
"html-loader": "^0.4.3",
"html-webpack-plugin": "^2.9.0",
"isparta-loader": "^2.0.0",
"jasmine": "^2.4.1",
"json-loader": "^0.5.4",
"karma": "^0.13.14",
"karma-angular-filesort": "^1.0.0",
"karma-coverage": "^0.5.3",
"karma-jasmine": "^0.3.8",
"karma-junit-reporter": "^0.4.2",
"karma-ng-html2js-preprocessor": "^0.2.0",
"karma-phantomjs-launcher": "^1.0.0",
"karma-phantomjs-shim": "^1.1.2",
"karma-webpack": "^1.7.0",
"ng-annotate-loader": "^0.0.10",
"node-sass": "^3.4.2",
"phantomjs-prebuilt": "^2.1.6",
"postcss-loader": "^0.8.0",
"sass-loader": "^3.1.2",
"style-loader": "^0.13.0",
"url-loader": "^0.5.7",
"webpack": "2.1.0-beta.15",
"webpack-split-by-path": "^0.0.10"
},
"engines": {
"node": ">=0.10.0"
"scripts": {
"build": "gulp",
"serve": "gulp serve",
"serve:dist": "gulp serve:dist",
"test": "gulp test",
"test:auto": "gulp test:auto"
},
"overrides": {
"angular-nvd3": {
"main": "dist/angular-nvd3.js"
}
},
"eslintConfig": {
"globals": {
"expect": true
},
"root": true,
"env": {
"browser": true,
"jasmine": true
},
"extends": [
"angular",
"xo-space"
]
}
}

View file

@ -1,27 +0,0 @@
'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
}
};

View file

@ -19,13 +19,12 @@ make generate-webui # Generate static contents in `traefik/static/` folder.
## How to build (only for frontends developer)
- prerequisite: [Node](https://nodejs.org)
- prerequisite: [Node 4+ and NPM 3+](https://nodejs.org)
- Go to the directory `webui`
- To install dependencies, execute the following commands:
- `npm install`
- `bower install`
- Build static Web UI, execute the following command:
- `gulp`
@ -40,6 +39,7 @@ make generate-webui # Generate static contents in `traefik/static/` folder.
- add vendor prefixes to CSS (cross-bowser support)
- add a hash in the file names to prevent browser cache problems
- all images will be optimized at build
- bundle JavaScript in one file
## How to edit (only for frontends developer)
@ -62,7 +62,8 @@ make generate-webui # Generate static contents in `traefik/static/` folder.
## Libraries
- [Node](https://nodejs.org)
- [Generator Gulp-Angular](https://github.com/Swiip/generator-gulp-angular)
- [Generator FountainJS](https://github.com/FountainJS/generator-fountain-webapp)
- [Webpack](https://github.com/webpack/webpack)
- [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)
@ -71,4 +72,4 @@ make generate-webui # Generate static contents in `traefik/static/` folder.
- [D3](http://d3js.org)
- [D3 - Documentation](https://github.com/mbostock/d3/wiki)
- [NVD3](http://nvd3.org)
- [Angular nvD3](http://krispo.github.io/angular-nvd3)
- [Angular nvD3](http://krispo.github.io/angular-nvd3)

View file

@ -1,13 +1,14 @@
(function () {
'use strict';
'use strict';
var angular = require('angular');
angular
.module('traefik.core.health', ['ngResource'])
.factory('Health', Health);
var traefikCoreHealth = 'traefik.core.health';
module.exports = traefikCoreHealth;
/** @ngInject */
function Health($resource) {
return $resource('../health');
}
angular
.module(traefikCoreHealth, ['ngResource'])
.factory('Health', Health);
})();
/** @ngInject */
function Health($resource) {
return $resource('../health');
}

View file

@ -1,13 +1,14 @@
(function () {
'use strict';
'use strict';
var angular = require('angular');
angular
.module('traefik.core.provider', ['ngResource'])
.factory('Providers', Providers);
var traefikCoreProvider = 'traefik.core.provider';
module.exports = traefikCoreProvider;
/** @ngInject */
function Providers($resource) {
return $resource('../api/providers');
}
angular
.module(traefikCoreProvider, ['ngResource'])
.factory('Providers', Providers);
})();
/** @ngInject */
function Providers($resource) {
return $resource('../api/providers');
}

View file

@ -1,15 +0,0 @@
(function() {
'use strict';
angular
.module('traefik')
.config(config);
/** @ngInject */
function config($logProvider) {
// Enable log
$logProvider.debugEnabled(true);
}
})();

View file

@ -1,9 +0,0 @@
/* global moment:false */
(function() {
'use strict';
angular
.module('traefik')
.constant('moment', moment);
})();

View file

@ -1,7 +0,0 @@
(function() {
'use strict';
angular
.module('traefik', ['ngAnimate', 'ngCookies', 'ngSanitize', 'ngMessages', 'ngAria', 'ngResource', 'ui.router', 'ui.bootstrap', 'traefik.section']);
})();

View file

@ -1,14 +0,0 @@
(function() {
'use strict';
angular
.module('traefik')
.run(runBlock);
/** @ngInject */
function runBlock($log) {
$log.debug('runBlock end');
}
})();

View file

@ -3,7 +3,6 @@
* 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

View file

@ -1,209 +1,204 @@
/* global d3:false */
(function (d3) {
'use strict';
'use strict';
var d3 = require('d3');
angular
.module('traefik.section.health')
.controller('HealthController', HealthController);
/** @ngInject */
function HealthController($scope, $interval, $log, Health) {
/** @ngInject */
function HealthController($scope, $interval, $log, Health) {
var vm = this;
var vm = this;
vm.graph = {
averageResponseTime: {},
totalStatusCodeCount: {}
};
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.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 = [
vm.graph.totalStatusCodeCount.data = [
{
key: "Total Status Code Count",
values: [
{
key: "Total Status Code Count",
values: [
{
"label": "200",
"value": 0
}
]
"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');
}
/**
* 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]
});
}
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);
// 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);
});
}
module.exports = HealthController;

View file

@ -1,19 +1,24 @@
(function () {
'use strict';
'use strict';
var angular = require('angular');
var traefikCoreHealth = require('../../core/health.resource');
var HealthController = require('./health.controller');
angular.module('traefik.section.health', ['traefik.core.health'])
.config(config);
var traefikSectionHealth = 'traefik.section.health';
module.exports = traefikSectionHealth;
/** @ngInject */
function config($stateProvider) {
angular
.module(traefikSectionHealth, [traefikCoreHealth])
.controller('HealthController', HealthController)
.config(config);
$stateProvider.state('health', {
url: '/health',
templateUrl: 'app/sections/health/health.html',
controller: 'HealthController',
controllerAs: 'healthCtrl'
});
/** @ngInject */
function config($stateProvider) {
}
$stateProvider.state('health', {
url: '/health',
template: require('./health.html'),
controller: 'HealthController',
controllerAs: 'healthCtrl'
});
})();
}

View file

@ -1,26 +1,21 @@
(function () {
'use strict';
'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 backendMonitor() {
return {
restrict: 'EA',
template: require('./backend-monitor.html'),
controller: BackendMonitorController,
controllerAs: 'backendCtrl',
bindToController: true,
scope: {
backend: '=',
backendId: '='
}
};
}
function BackendMonitorController() {
// Nothing
}
function BackendMonitorController() {
// Nothing
}
})();
module.exports = backendMonitor;

View file

@ -1,7 +1,10 @@
(function () {
'use strict';
'use strict';
var angular = require('angular');
var backendMonitor = require('./backend-monitor.directive');
angular
.module('traefik.section.providers.backend-monitor', []);
var traefikBackendMonitor = 'traefik.section.providers.backend-monitor';
module.exports = traefikBackendMonitor;
})();
angular
.module(traefikBackendMonitor, [])
.directive('backendMonitor', backendMonitor);

View file

@ -1,26 +1,21 @@
(function () {
'use strict';
'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 frontendMonitor() {
return {
restrict: 'EA',
template: require('./frontend-monitor.html'),
controller: FrontendMonitorController,
controllerAs: 'frontendCtrl',
bindToController: true,
scope: {
frontend: '=',
frontendId: '='
}
};
}
function FrontendMonitorController() {
// Nothing
}
function FrontendMonitorController() {
// Nothing
}
})();
module.exports = frontendMonitor;

View file

@ -1,6 +1,10 @@
(function () {
'use strict';
'use strict';
var angular = require('angular');
var frontendMonitor = require('./frontend-monitor.directive');
angular.module('traefik.section.providers.frontend-monitor', []);
var traefikFrontendMonitor = 'traefik.section.providers.frontend-monitor';
module.exports = traefikFrontendMonitor;
})();
angular
.module(traefikFrontendMonitor, [])
.directive('frontendMonitor', frontendMonitor);

View file

@ -1,28 +1,23 @@
(function () {
'use strict';
'use strict';
angular
.module('traefik.section.providers')
.controller('ProvidersController', ProvidersController);
/** @ngInject */
function ProvidersController($scope, $interval, $log, Providers) {
var vm = this;
/** @ngInject */
function ProvidersController($scope, $interval, $log, Providers) {
var vm = this;
vm.providers = Providers.get();
vm.providers = Providers.get();
var intervalId = $interval(function () {
Providers.get(function (providers) {
vm.providers = providers;
}, function (error) {
vm.providers = {};
$log.error(error);
});
}, 2000);
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);
});
}
$scope.$on('$destroy', function () {
$interval.cancel(intervalId);
});
}
})();
module.exports = ProvidersController;

View file

@ -1,24 +1,30 @@
(function () {
'use strict';
'use strict';
var angular = require('angular');
var traefikCoreProvider = require('../../core/providers.resource');
var ProvidersController = require('./providers.controller');
var traefikBackendMonitor = require('./backend-monitor/backend-monitor.module');
var traefikFrontendMonitor = require('./frontend-monitor/frontend-monitor.module');
angular
.module('traefik.section.providers', [
'traefik.core.provider',
'traefik.section.providers.backend-monitor',
'traefik.section.providers.frontend-monitor'
])
.config(config);
var traefikSectionProviders = 'traefik.section.providers';
module.exports = traefikSectionProviders;
/** @ngInject */
function config($stateProvider) {
angular
.module(traefikSectionProviders, [
traefikCoreProvider,
traefikBackendMonitor,
traefikFrontendMonitor
])
.config(config)
.controller('ProvidersController', ProvidersController);
$stateProvider.state('provider', {
url: '/',
templateUrl: 'app/sections/providers/providers.html',
controller: 'ProvidersController',
controllerAs: 'providersCtrl'
});
/** @ngInject */
function config($stateProvider) {
}
$stateProvider.state('provider', {
url: '/',
template: require('./providers.html'),
controller: 'ProvidersController',
controllerAs: 'providersCtrl'
});
})();
}

View file

@ -1,13 +0,0 @@
(function () {
'use strict';
angular
.module('traefik.section')
.config(config);
/** @ngInject */
function config($urlRouterProvider) {
$urlRouterProvider.otherwise('/');
}
})();

View file

@ -0,0 +1,24 @@
'use strict';
var angular = require('angular');
require('nvd3');
var ndv3 = require('angular-nvd3');
var traefikSectionHealth = require('./health/health.module');
var traefikSectionProviders = require('./providers/providers.module');
var traefikSection = 'traefik.section';
module.exports = traefikSection;
angular
.module(traefikSection, [
'ui.router',
'ui.bootstrap',
ndv3,
traefikSectionProviders,
traefikSectionHealth
])
.config(config);
/** @ngInject */
function config($urlRouterProvider) {
$urlRouterProvider.otherwise('/');
}

View file

@ -1,13 +0,0 @@
(function () {
'use strict';
angular
.module('traefik.section', [
'ui.router',
'ui.bootstrap',
'nvd3',
'traefik.section.providers',
'traefik.section.health'
]);
})();

View file

@ -1,9 +1,9 @@
@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');
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;
}

View file

@ -2,29 +2,17 @@
<html ng-app="traefik">
<head>
<meta charset="utf-8">
<title>Træfɪk</title>
<title>Træfɪk</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<link rel="icon" type="image/png" href="traefik.icon.png" />
<!-- 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">
@ -56,22 +44,5 @@
</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>

46
webui/src/index.js Normal file
View file

@ -0,0 +1,46 @@
'use strict';
var angular = require('angular');
var ngAnimate = require('angular-animate');
var ngCookies = require('angular-cookies');
var ngSanitize = require('angular-sanitize');
var ngMessages = require('angular-messages');
var ngAria = require('angular-aria');
var ngResource = require('angular-resource');
var uiRouter = require('angular-ui-router');
var uiBootstrap = require('angular-ui-bootstrap');
var moment = require('moment');
var traefikSection = require('./app/sections/sections');
require('./index.scss');
require('animate.css/animate.css');
require('nvd3/build/nv.d3.css');
require('bootstrap/dist/css/bootstrap.css');
var app = 'traefik';
module.exports = app;
angular
.module(app, [
ngAnimate,
ngCookies,
ngSanitize,
ngMessages,
ngAria,
ngResource,
uiRouter,
uiBootstrap,
traefikSection
])
.run(runBlock)
.constant('moment', moment)
.config(config);
/** @ngInject */
function config($logProvider) {
// Enable log
$logProvider.debugEnabled(true);
}
/** @ngInject */
function runBlock($log) {
$log.debug('runBlock end');
}

3
webui/src/index.scss Normal file
View file

@ -0,0 +1,3 @@
$icon-font-path: "../bower_components/bootstrap-sass/assets/fonts/bootstrap/";
@import 'app/index.scss';
@import 'app/traefik.scss';