Skip to content
This repository was archived by the owner on Oct 6, 2022. It is now read-only.
35 changes: 32 additions & 3 deletions client/configuration/config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
angular.module('config', [])

.constant('ENV', {RequiresAuth:false,etcdHost:'127.0.0.1',etcdPort:'4001',hobknobHost:'localhost',hobknobPort:'3006',categories:[{id:0,name:'Simple Features',description:'Use when you want your feature to be either on or off'},{id:1,name:'Domain Features',description:'Use when you want your features to be toggled separately for different domains (e.g. com, couk, fr, ...)',values:['com','couk','de','fr']},{id:2,name:'Locale Features',description:'Use when you want your features to be toggled separately for different locales (e.g. en-GB, en-US, fr-FR, ...)',values:['en-GB','en-US','fr-FR','de-DE']}],plugin:{path:'../server/examplePlugin.js'},hooks:['server/exampleHook.js']})

;
.constant('ENV', {
RequiresAuth: false,
dataSource: 'etcd',
etcdHost: '127.0.0.1',
etcdPort: '4001',
hobknobHost: 'localhost',
hobknobPort: '3006',
loadBalancerFile: '/etc/lbstatus/hobknob',
categories: [{
id: 0,
name: 'Simple Features',
description: 'Use when you want your feature to be either on or off'
}, {
id: 1,
name: 'Domain Features',
description: 'Use when you want your features to be toggled separately for different domains (e.g. com, couk, fr, ...)',
values: ['com', 'couk', 'de', 'fr']
}, {
id: 2,
name: 'Locale Features',
description: 'Use when you want your features to be toggled separately for different locales (e.g. en-GB, en-US, fr-FR, ...)',
values: ['en-GB', 'en-US', 'fr-FR', 'de-DE']
}],
plugin: {
path: '../server/examplePlugin.js'
},
hooks: ['server/exampleHook.js'],
customization: {
logo: '/img/opentable-hr-mono.png',
title: 'Opentable Feature Toggles'
}
});
5 changes: 4 additions & 1 deletion client/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ featureToggleFrontend.config(function ($routeProvider, $locationProvider, $httpP
$httpProvider.defaults.headers.post = {'Content-Type': 'application/json'};
});

featureToggleFrontend.run(function (editableOptions) {
featureToggleFrontend.run(function ($rootScope, editableOptions, ENV) {
editableOptions.theme = 'bs3';
$rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
$rootScope.title = current.$$route.title || ENV.customization.title;
});
});
2 changes: 2 additions & 0 deletions client/js/controllers/loginController.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
'use strict';

featureToggleFrontend.controller('LoginController', ['$scope', '$window', 'ENV', function ($scope, $window, ENV) {
$scope.logo = ENV.customization.logo;

if (ENV.RequiresAuth) {
var authProvider = Object.keys(ENV.AuthProviders)[0];

Expand Down
3 changes: 2 additions & 1 deletion client/js/controllers/sideBarController.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
'use strict';

featureToggleFrontend.controller('SideBarController', ['$scope', 'applicationService', 'authorisationService', '$location', 'focus', 'CurrentUser', function ($scope, applicationService, authorisationService, $location, focus, CurrentUser) {
featureToggleFrontend.controller('SideBarController', ['$scope', 'applicationService', 'authorisationService', '$location', 'focus', 'CurrentUser', 'ENV', function ($scope, applicationService, authorisationService, $location, focus, CurrentUser, ENV) {
$scope.applications = [];
$scope.newApplicationName = '';
$scope.adding = false;
$scope.CurrentUser = CurrentUser;
$scope.logo = ENV.customization.logo;

var loadApplications = function () {
applicationService.getApplications(
Expand Down
8 changes: 4 additions & 4 deletions client/views/login.pug
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
doctype html
html(lang='en')
html(lang='en' ng-app='featureToggleFrontend' ng-cloak)
head
meta(name='viewport', content='width=device-width, initial-scale=1')
title Hobknob Dashboard
title(ng-bind="$root.title")
link(href='/bower_components/bootstrap/dist/css/bootstrap.min.css', media='all', rel='stylesheet')
link(href='/css/dashboard.css', media='all', rel='stylesheet')
link(href='/css/toggleswitch.css', media='all', rel='stylesheet')
link(href='/bower_components/bootstrap-social/bootstrap-social.css', media='all', rel='stylesheet')
link(href='/bower_components/font-awesome/css/font-awesome.css', media='all', rel='stylesheet')
script(src='/bower_components/jquery/dist/jquery.min.js')
body(ng-app='featureToggleFrontend' ng-cloak)
body
#wrapper
#sidebar-wrapper
ul.sidebar-nav(ng-controller="LoginController")
li.sidebar-brand
a.navbar-brand.logo(href='/')
img(src='/img/opentable-hr-mono.png')
img(ng-src='{{logo}}')
a(href='/auth/google',ng-if="authProvider == 'GoogleAuth'", id="loginwithgoogle").btn.btn-block.btn-social.btn-google-plus.sign-in
i.fa.fa-google-plus
| Sign in with Google
Expand Down
6 changes: 3 additions & 3 deletions client/views/main.pug
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
doctype html
html(lang='en')
html(lang='en' ng-app='featureToggleFrontend')
head
meta(name='viewport', content='width=device-width, initial-scale=1')
title Hobknob Dashboard
title(ng-bind="$root.title")
link(href='/bower_components/bootstrap/dist/css/bootstrap.min.css', media='all', rel='stylesheet')
link(href='/css/dashboard.css', media='all', rel='stylesheet')
link(href='/bower_components/angular-xeditable/dist/css/xeditable.css' rel='stylesheet')
Expand All @@ -11,7 +11,7 @@ html(lang='en')
script(src='/bower_components/jquery/dist/jquery.min.js')
script(type="text/javascript").
window.user = !{JSON.stringify(user || {}, null, 2)};
body(ng-app='featureToggleFrontend')
body
#wrapper
include ./sidebar.pug
#page-content-wrapper
Expand Down
2 changes: 1 addition & 1 deletion client/views/sidebar.pug
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
ul.sidebar-nav
li.sidebar-brand
a.navbar-brand.logo(href='/')
img(src='/img/opentable-hr-mono.png')
img(ng-src='{{logo}}')
li
.profile
img.media-object.img-circle(ng-src='{{CurrentUser.getPicture()}}', alt='avatar')
Expand Down
8 changes: 6 additions & 2 deletions config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"RequiresAuth": false,
"dataSource": "etcd",
"etcdHost": "127.0.0.1",
"etcdPort": "4001",
"etcdPort": "2379",
"hobknobHost": "localhost",
"hobknobPort": "3006",
"loadBalancerFile": "/etc/lbstatus/hobknob",
Expand Down Expand Up @@ -40,5 +40,9 @@
},
"hooks": [
"server/exampleHook.js"
]
],
"customization": {
"logo": "/img/opentable-hr-mono.png",
"title": "Hobknob Dashboard"
}
}
2 changes: 2 additions & 0 deletions server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ var passportGoogleAuthenticateParams = function () {


app.use(express.static(path.join(__dirname, '/../client')));

// TODO: all this can be moved out into routes/handlers
app.get('/login', authenticateRoutes.login);
app.get('/', ensureAuthenticatedOrRedirectToLogin, dashboardRoutes.dashboard);
app.get('/partials/:name', dashboardRoutes.partials);
Expand Down
55 changes: 53 additions & 2 deletions server/domain/application.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
'use strict';

var config = require('config');
var common = require('./common');
var audit = require('./audit');
var acl = require('./acl');

var application = function() {
switch (config.dataSource.toLowerCase()) {
case 'etcd':
Expand All @@ -17,11 +21,58 @@ module.exports = {
},

addApplication: function (applicationName, req, cb) {
application().addApplication(applicationName, req, cb);
application().addApplication(applicationName, req, function(err) {
if (err) {
return cb(err);
}

var userDetails = common.getUserDetails(req)

audit.addApplicationAudit(userDetails, applicationName, 'Created', function () {
if (err) {
cb(err);
return;
}
});

if (config.RequiresAuth) {
var userEmail = userDetails.email.toLowerCase(); // todo: need better user management
acl.grant(userEmail, applicationName, function (grantErr) {
if (grantErr) {
cb(grantErr);
return;
}
cb();
});
} else {
cb();
}
});
},

deleteApplication: function (applicationName, req, cb) {
application().deleteApplication(applicationName, req, cb);
application().deleteApplication(applicationName, req, function (err) {
if (err) {
return cb(err);
}

audit.addApplicationAudit(common.getUserDetails(req), applicationName, 'Deleted', function () {
if (err) {
console.log(err);
}
});

if (config.RequiresAuth) {
acl.revokeAll(applicationName, function (revokeErr) {
if (revokeErr) {
return cb(revokeErr);
}
cb();
});
} else {
cb();
}
});
},

getApplicationMetaData: function (applicationName, cb) {
Expand Down
9 changes: 9 additions & 0 deletions server/domain/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict';

var config = require('config');

module.exports = {
getUserDetails: function (req) {
return config.RequiresAuth ? req.user._json : {name: 'Anonymous'};
}
};
55 changes: 3 additions & 52 deletions server/domain/etcd/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ var acl = require('../acl');
var audit = require('../audit');
var etcdBaseUrl = 'http://' + config.etcdHost + ':' + config.etcdPort + '/v2/keys/';

var getUserDetails = function (req) {
return config.RequiresAuth ? req.user._json : {name: 'Anonymous'};
};

module.exports = {
getApplications: function (cb) {
etcd.client.get('v1/toggles/', {recursive: false}, function (err, result) {
Expand All @@ -31,58 +27,13 @@ module.exports = {
},

addApplication: function (applicationName, req, cb) {
var path = 'v1/toggles/' + applicationName;
etcd.client.mkdir(path, function (err) {
if (err) {
return cb(err);
}

audit.addApplicationAudit(getUserDetails(req), applicationName, 'Created', function () {
if (err) {
console.log(err); // todo: better logging
}
});

// todo: not sure if this is correct
if (config.RequiresAuth) {
var userEmail = getUserDetails(req).email.toLowerCase(); // todo: need better user management
acl.grant(userEmail, applicationName, function (grantErr) {
if (grantErr) {
cb(grantErr);
return;
}
cb();
});
} else {
cb();
}
});
var path = 'v1/toggles/' + applicationName;
etcd.client.mkdir(path, cb);
},

deleteApplication: function (applicationName, req, cb) {
var path = 'v1/toggles/' + applicationName;
etcd.client.delete(path, {recursive: true}, function (err) {
if (err) {
return cb(err);
}

audit.addApplicationAudit(getUserDetails(req), applicationName, 'Deleted', function () {
if (err) {
console.log(err);
}
});

if (config.RequiresAuth) {
acl.revokeAll(applicationName, function (revokeErr) {
if (revokeErr) {
return cb(revokeErr);
}
cb();
});
} else {
cb();
}
});
etcd.client.delete(path, {recursive: true}, cb);
},

getApplicationMetaData: function (applicationName, cb) {
Expand Down
10 changes: 10 additions & 0 deletions server/domain/etcd/audit.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ module.exports = {
var path = 'v1/audit/application/' + applicationName;
etcd.client.get(path, {recursive: true}, function (err, result) {
if (err) {
if (err.errorCode === 100) { // key not found
callback(null, []);
return;
}

callback(err);
return;
}
Expand All @@ -26,6 +31,11 @@ module.exports = {
var path = 'v1/audit/feature/' + applicationName + '/' + featureName;
etcd.client.get(path, {recursive: true}, function (err, result) {
if (err) {
if (err.errorCode === 100) { // key not found
callback(null, []);
return;
}

callback(err);
return;
}
Expand Down
Loading