Barebones angular app

This commit is contained in:
Matej Kramny 2014-08-24 18:36:34 +01:00
parent 82a40395d9
commit 5465fb2e11
29 changed files with 1168 additions and 0 deletions

3
.bowerrc Normal file
View File

@ -0,0 +1,3 @@
{
"directory": "public/vendor"
}

5
.gitignore vendored
View File

@ -1,3 +1,8 @@
lib/credentials.js
lib/credentials.json
public/vendor
dist/
# Logs
logs
*.log

156
Gruntfile.js Normal file
View File

@ -0,0 +1,156 @@
module.exports = function(grunt) {
require('load-grunt-tasks')(grunt);
// Configuration
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
bump: {
options: {
files: ['package.json'],
pushTo: 'origin',
commitFiles: ['package.json']
}
},
watch: {
js: {
files: ['public/js/*.js', 'public/js/**/*.js'],
tasks: ['newer:copy:js']
},
styles: {
files: ['public/css/{,**/}*.less'],
tasks: ['newer:less:development']
},
livereload: {
files: [
'dist/{,**/}*.{css,js,png,jpg,jpeg,gif,webp,svg,html}'
],
options: {
livereload: true
}
}
},
less: {
development: {
expand: true,
cwd: 'public/css',
dest: 'dist/css',
src: '**/*',
ext: '.css'
},
production: {
expand: true,
cwd: 'public/css',
dest: 'dist/css',
src: '**/*',
ext: '.css',
options: {
cleancss: true
}
}
},
clean: {
clean: {
files: [{
dot: true,
src: [
'dist'
]
}]
}
},
// Copies remaining files to places other tasks can use
copy: {
js: {
expand: true,
cwd: 'public/js',
dest: 'dist/js/',
src: '{,**/}*.js'
},
img: {
expand: true,
cwd: 'public/img',
dest: 'dist/img/',
src: '{,**/}*.{png,jpg,jpeg,gif}'
},
vendor: {
files: [{
expand: true,
cwd: 'public/vendor',
dest: 'dist/vendor',
src: ['**/*.js', '**/*.css', '**/*.png', '**/*.jpg', '**/*.jpeg', '**/*.woff', '**/*.ttf', '**/*.svg', '**/*.eot']
}]
},
fonts: {
expand: true,
cwd: 'public/fonts',
dest: 'dist/fonts/',
src: '{,**/}*.{woff,ttf,svg,eot}'
}
},
// Run some tasks in parallel to speed up the build process
concurrent: {
options: {
limit: 6
},
server: [
'copy:js',
'copy:vendor',
'copy:img',
'copy:fonts'
],
watch: {
tasks: [
'nodemon:dev',
'watch'
],
options: {
logConcurrentOutput: true
}
}
},
uglify: {
dist: {
files: [{
expand: true,
cwd: 'public/js',
src: ['**/*.js', '*.js'],
dest: 'dist/js'
}]
}
},
nodemon: {
dev: {
script: 'lib/app.js',
logConcurrentOutput: true,
options: {
cwd: __dirname,
watch: ['lib/']
}
}
}
});
grunt.registerTask('serve', [
'clean:clean',
'concurrent:server',
'less:development',
'concurrent:watch'
]);
grunt.registerTask('build', [
'clean:clean',
'concurrent:server',
'less:production'
]);
grunt.registerTask('default', [
'build'
]);
};

16
bower.json Normal file
View File

@ -0,0 +1,16 @@
{
"name": "semaphore",
"version": "0.0.0",
"dependencies": {
"angular": "latest",
"requirejs": "latest",
"fontawesome": "latest",
"jquery": "latest",
"bootstrap": "latest",
"moment": "latest",
"d3": "latest",
"async": "latest",
"angular-couch-potato": "~0.1.1",
"angular-ui-router": "~0.2.10"
}
}

126
lib/app.js Normal file
View File

@ -0,0 +1,126 @@
var config = require('./config');
var newrelic = {
getBrowserTimingHeader: function () {}
};
if (config.production && config.credentials.use_analytics) {
newrelic = require('newrelic');
}
var express = require('express')
, routes = require('./routes')
, http = require('http')
, path = require('path')
, mongoose = require('mongoose')
, util = require('./util')
, session = require('express-session')
, RedisStore = require('connect-redis')(session)
, passport = require('passport')
, auth = require('./auth')
, bugsnag = require('bugsnag')
, socketPassport = require('passport.socketio')
, bodyParser = require('body-parser')
var app = exports.app = express();
if (config.production) {
require('newrelic');
}
var releaseStage = config.production ? "production" : "development";
bugsnag.register("c0c7568710bb46d4bf14b3dad719dbbe", {
notifyReleaseStages: ["production"],
releaseStage: releaseStage
});
mongoose.connect(config.credentials.db, config.credentials.db_options);
var sessionStore = new RedisStore({
host: config.credentials.redis_host,
port: config.credentials.redis_port,
ttl: 604800000,
pass: config.credentials.redis_key
});
var db = mongoose.connection
db.on('error', console.error.bind(console, 'Mongodb Connection Error:'));
db.once('open', function callback () {
if (!config.is_testing) console.log("Mongodb connection established")
});
// all environments
app.enable('trust proxy');
app.set('port', process.env.PORT || 3000); // Port
app.set('views', __dirname + '/views');
app.set('view engine', 'jade'); // Templating engine
app.set('app version', config.version); // App version
app.set('x-powered-by', false);
app.set('view cache', config.production);
app.locals.newrelic = newrelic;
config.configure(app);
app.use(function(req, res, next) {
res.set('x-frame-options', 'SAMEORIGIN');
res.set('x-xss-protection', '1; mode=block');
next();
});
app.use(require('serve-static')(path.join(__dirname, '..', 'dist')));
app.use(require('morgan')(config.production ? 'combined' : 'dev'));
app.use(bugsnag.requestHandler);
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
app.use(require('cookie-parser')());
app.use(session({
secret: "#semaphore",
name: 'semaphore',
store: sessionStore,
proxy: true,
saveUninitialized: false,
resave: false,
cookie: {
secure: config.credentials.is_ssl,
maxAge: 604800000
}
}));
app.use(passport.initialize());
app.use(passport.session());
// Custom middleware
app.use(function(req, res, next) {
res.locals.user = req.user;
res.locals.loggedIn = res.locals.user != null;
next();
});
// routes
routes.router(app);
app.use(bugsnag.errorHandler);
var server = http.createServer(app)
server.listen(app.get('port'), function(){
console.log('Semaphore listening on port ' + app.get('port'));
});
exports.io = io = require('socket.io').listen(server)
config.init();
io.use(socketPassport.authorize({
cookieParser: require('cookie-parser'),
secret: "#semaphore",
key: 'semaphore',
store: sessionStore,
passport: passport,
fail: function(data, message, error, accept) {
accept(false);
}
}))

13
lib/auth.js Normal file
View File

@ -0,0 +1,13 @@
var passport = require('passport')
, models = require('./models')
, bugsnag = require('bugsnag')
passport.serializeUser(function(user, done) {
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
models.User.findOne({
_id: id
}, done);
})

75
lib/config.js Normal file
View File

@ -0,0 +1,75 @@
var fs = require('fs')
, mailer = require('nodemailer')
try {
var credentials = require('./credentials.json')
} catch (e) {
console.log("\nNo credentials.json File!\n")
process.exit(1);
}
exports.credentials = credentials;
exports.version = require('../package.json').version;
exports.hash = 'dirty';
exports.production = process.env.NODE_ENV == "production";
exports.port = process.env.PORT || credentials.port;
exports.path = __dirname;
if (process.platform.match(/^win/) == null) {
try {
var spawn_process = require('child_process').spawn
var readHash = spawn_process('git', ['rev-parse', '--short', 'HEAD']);
readHash.stdout.on('data', function (data) {
exports.hash = data.toString().trim();
require('./app').app.locals.versionHash = exports.hash;
})
} catch (e) {
console.log("\n~= Unable to obtain git commit hash =~\n")
}
}
exports.configure = function (app) {
app.locals.pretty = exports.production // Pretty HTML outside production mode
app.locals.version = exports.version;
app.locals.versionHash = exports.hash;
app.locals.production = exports.production;
app.locals.use_analytics = credentials.use_analytics;
}
// Create SMTP transport method
exports.transport_enabled = credentials.smtp.user.length > 0;
exports.transport = null;
if (exports.transport_enabled) {
var smtp = require('nodemailer-smtp-transport');
exports.transport = mailer.createTransport(smtp({
service: "Mandrill",
auth: credentials.smtp,
port: 2525 // should bypass any port restrictions
}));
}
exports.init = function () {
var models = require('./models');
models.User.findOne({
email: 'admin@semaphore.local'
}).exec(function (err, admin) {
if (!admin) {
console.log("Creating Admin user admin@semaphore.local!");
admin = new models.User({
email: 'admin@semaphore.local',
username: 'semaphore',
name: 'Administrator'
});
models.User.hashPassword('CastawayLabs', function (hash) {
admin.password = hash;
admin.save();
});
}
})
}

View File

@ -0,0 +1,22 @@
{
"redis_port": 6379,
"redis_host": "127.0.0.1",
"redis_key": "",
"use_analytics": false,
"is_ssl": false,
"newrelic_key": "",
"bugsnag_key": "",
"smtp": {
"user": "",
"pass": ""
},
"db": "mongodb://127.0.0.1/semaphore",
"db_options": {
"auto_reconnect": true,
"native_parser": true,
"server": {
"auto_reconnect": true
}
},
"port": 3000
}

34
lib/models/User.js Normal file
View File

@ -0,0 +1,34 @@
var bcrypt = require('bcrypt')
var mongoose = require('mongoose')
var ObjectId = mongoose.Schema.ObjectId;
var schema = mongoose.Schema({
created: {
type: Date,
default: Date.now
},
username: String,
name: String,
email: String,
password: String
});
schema.index({
email: 1
});
schema.statics.hashPassword = function(password, cb) {
bcrypt.hash(password, 10, function(err, hash) {
cb(hash);
});
}
schema.methods.comparePassword = function (password, cb) {
bcrypt.compare(password, this.password, function(err, res) {
// res is boolean
cb(res);
})
}
module.exports = mongoose.model('User', schema);

7
lib/models/index.js Normal file
View File

@ -0,0 +1,7 @@
var manifest = [
'User'
];
manifest.forEach(function (model) {
module.exports[model] = require('./'+model);
});

9
lib/routes/apps.js Normal file
View File

@ -0,0 +1,9 @@
exports.httpRouter = function () {
}
exports.router = function () {
}

171
lib/routes/auth.js Normal file
View File

@ -0,0 +1,171 @@
var passport = require('passport')
, models = require('../models')
, validator = require('validator')
, util = require('../util')
, config = require('../config')
, async = require('async')
, express = require('express')
, mongoose = require('mongoose')
exports.unauthorized = function (app, template) {
// Unrestricted -- non-authorized people can access!
template([
'login'
], {
prefix: 'auth'
});
var auth = express.Router();
auth.post('/password', doLogin)
.get('/loggedin', isLoggedIn)
.get('/logout', doLogout)
app.use('/auth', auth);
}
exports.router = function (app) {
// Restricted -- only authorized people can access!
app.post('/auth/register', doRegister)
}
function isLoggedIn (req, res) {
res.send({
hasSession: req.user != null,
isLoggedIn: res.locals.loggedIn
});
}
function doLogin (req, res) {
var auth = req.body.auth;
var isValid = true;
if (!validator.isLength(auth, 4)) {
isValid = false;
}
// validate password
if (!validator.isLength(req.body.password, 6)) {
isValid = false;
}
if (!isValid) {
return authCallback(false, null, req, res);
}
var query = {
email: auth.toLowerCase()
};
models.User.findOne(query, function(err, user) {
if (err) {
throw err;
}
user.comparePassword(req.body.password, function (matches) {
if (!matches) {
isValid = false;
}
authCallback(isValid, user, req, res);
});
})
}
function authCallback (isValid, user, req, res) {
if (!isValid) {
res.send(400, {
message: "Nope. Incorrect Credentials!"
});
return;
}
req.login(user, function(err) {
if (err) throw err;
res.send(201)
})
}
function doRegister (req, res) {
var errs = {
name: false,
email: false,
password: false,
username: false
};
var userObject = req.body.user;
if (!(userObject && typeof userObject === 'object')) {
return res.send(400, {
message: 'Invalid Request'
});
}
var email = userObject.email;
if (email) {
email = email.toLowerCase();
}
var password = userObject.password;
var username = userObject.username;
var name = userObject.name;
errs.email = !validator.isEmail(email);
errs.username = !validator.isLength(username, 3, 15);
errs.name = !validator.isLength(name, 4, 50);
if (!(username && username.match(/^[a-zA-Z0-9_-]{3,15}$/) && validator.isAscii(username))) {
// Errornous username
errs.username = true;
}
// validate password
errs.password = !validator.isLength(password, 8, 100);
if (!(errs.username == false && errs.password == false && errs.name == false && errs.email == false)) {
res.send(400, {
fields: errs,
message: ''
});
return;
}
// Register
var user = new models.User({
email: email,
username: username,
name: name
});
models.User.hashPassword(password, function (hash) {
user.password = hash;
user.save();
// log in now
req.login(user, function(err) {
if (err) throw err;
res.send({
message: "Registration Successful",
user_id: user._id
});
});
});
}
function doLogout (req, res) {
req.logout();
req.session.destroy();
res.format({
json: function() {
res.send(201)
},
html: function() {
res.redirect('/')
}
})
}

43
lib/routes/index.js Normal file
View File

@ -0,0 +1,43 @@
var util = require('../util')
, auth = require('./auth')
, apps = require('./apps')
exports.router = function(app) {
var templates = require('../templates')(app);
templates.route([
auth,
// apps
]);
templates.add('homepage')
templates.setup();
app.get('/', layout);
app.all('*', util.authorized);
// Handle HTTP reqs
apps.httpRouter(app);
// only json beyond this point
app.get('*', function(req, res, next) {
res.format({
json: function() {
next()
},
html: function() {
layout(req, res);
}
});
});
auth.router(app);
apps.router(app);
}
function layout (req, res) {
if (res.locals.loggedIn) {
res.render('layout')
} else {
res.render('auth');
}
}

67
lib/templates.js Normal file
View File

@ -0,0 +1,67 @@
// By Matej Kramny <matej@matej.me>
// Please leave this comment here.
module.exports = function(app) {
var self = this;
self.routes = [];
self.app = app
self.route = function (controller) {
if (!(controller instanceof Array)) {
controller = [controller];
}
for (c in controller) {
controller[c].unauthorized(self.app, self.add.bind(self));
}
}
self.makeRoute = function(route, view) {
return {
route: route,
view: view
}
}
self.add = function (routes, opts) {
var args = arguments;
var prefix = opts ? opts.prefix : null;
if (!prefix) prefix = '';
else prefix += '/';
if (typeof routes === 'string') {
self.routes.push(self.makeRoute(prefix+routes, prefix+routes));
return;
}
if (Object.prototype.toString.call(routes) == '[object Object]') {
self.routes.push(routes);
return;
}
for (var i = 0; i < routes.length; i++) {
var r;
if (typeof routes[i] == 'string') {
r = self.makeRoute(prefix+routes[i], prefix+routes[i]);
} else if (routes[i] instanceof Array) {
r = self.makeRoute(prefix+routes[i][0], routes[i][1]);
} else {
r = routes[i]
}
self.routes.push(r);
}
}
self.setup = function () {
for (var i = 0; i < routes.length; i++) {
app.get('/view/'+routes[i].route, self.getView.bind(routes[i]));
}
}
self.getView = function (req, res) {
res.render(this.view);
}
return self;
}

16
lib/util.js Normal file
View File

@ -0,0 +1,16 @@
exports.authorized = function (req, res, next) {
if (res.locals.loggedIn) {
next()
} else {
res.format({
html: function() {
res.render('auth');
},
json: function() {
res.send(403, {
message: "Unauthorized"
})
}
})
}
}

32
lib/views/auth.jade Normal file
View File

@ -0,0 +1,32 @@
doctype
html
head
meta(http-equiv="Content-Type", content="text/html; charset=utf-8;")
meta(name="viewport" content="width=device-width, initial-scale=1.0")
block meta
title(ng-bind-template="{{ pageTitle }} - Semaphore") Semaphore
link(href="/favicon.ico" type="image/x-icon" rel="icon")
link(href="/favicon.ico" type="image/x-icon" rel="shortcut icon")
//- all css goes here
block css
link(rel="stylesheet" href="/css/semaphore.css")
body
.container-fluid(style="margin-top: 100px;")
.row
.col-sm-6.col-sm-offset-3
block content
ui-view(autoscroll="false")
p.lead.text-center
i.fa.fa-spin.fa-cog
| Loading...
block js
script(src="/vendor/requirejs/require.js" data-main="/js/semaphore_auth.js")
if use_analytics
!= newrelic.getBrowserTimingHeader()
//- page-specific js
block addonjs

21
lib/views/auth/login.jade Normal file
View File

@ -0,0 +1,21 @@
.panel.panel-default
.panel-heading
h3.panel-title Semaphore Log In
.panel-body
form.form-horizontal
.form-group(ng-if="status.length > 0")
.col-sm-7.col-sm-offset-4
span(ng-bind="status")
.form-group
label.control-label.col-sm-4 Email
.col-sm-7
input.form-control(type="email" ng-model="user.auth" placeholder="Email Address")
.form-group
label.control-label.col-sm-4 Password
.col-sm-7
input.form-control(type="password" ng-model="user.password" placeholder="Password")
.form-group
.col-sm-7.col-sm-offset-4
button.btn.btn-default(ng-click="authenticate(user)") Log in

1
lib/views/homepage.jade Normal file
View File

@ -0,0 +1 @@
h1 Hello there!

39
lib/views/layout.jade Normal file
View File

@ -0,0 +1,39 @@
doctype
html
head
meta(http-equiv="Content-Type", content="text/html; charset=utf-8;")
meta(name="viewport" content="width=device-width, initial-scale=1.0")
block meta
title(ng-bind-template="{{ pageTitle }} - Semaphore") Semaphore
link(href="/favicon.ico" type="image/x-icon" rel="icon")
link(href="/favicon.ico" type="image/x-icon" rel="shortcut icon")
//- all css goes here
block css
link(rel="stylesheet" href="/css/semaphore.css")
body
.container-fluid
.row
.col-sm-3.col-lg-2
ul.nav
h2.text-center: a(ui-sref="homepage") Semaphore
li(ng-repeat="playbook in playbooks")
a(ui-sref="playbook({ pid: playbook._id })") {{ playbook.name }}
.col-sm-9.col-lg-10
block content
ui-view(autoscroll="false")
p.lead.text-center
i.fa.fa-spin.fa-cog
| Loading...
block js
script(src="/vendor/requirejs/require.js" data-main="/js/semaphore.js")
if use_analytics
!= newrelic.getBrowserTimingHeader()
//- page-specific js
block addonjs

15
newrelic.js Normal file
View File

@ -0,0 +1,15 @@
var config = require('./lib/config')
exports.config = {
app_name : ['Semaphore'],
license_key : config.credentials.newrelic_key,
logging : {
level : 'trace'
},
rules: {
ignore: [
'^/socket.io/.*/*-polling',
'^/ping$'
]
}
};

76
package.json Normal file
View File

@ -0,0 +1,76 @@
{
"name": "semaphore",
"version": "0.0.0",
"description": "Open Source Alternative to Ansible Tower",
"main": "bin/semaphore.js",
"scripts": {
"test": "mocha -R spec",
"start": "node bin/semaphore.js"
},
"repository": {
"type": "git",
"url": "git://github.com/CastawayLabs/semaphore.git"
},
"keywords": [
"ansible"
],
"author": "Matej Kramny <matej@matej.me>",
"license": "MIT",
"bugs": {
"url": "https://github.com/CastawayLabs/semaphore/issues"
},
"homepage": "https://github.com/CastawayLabs/semaphore",
"dependencies": {
"async": "latest",
"bcrypt": "^0.8.0",
"body-parser": "1.4.3",
"bower": "latest",
"bugsnag": "latest",
"connect-mongo": "latest",
"connect-redis": "latest",
"cookie-parser": "latest",
"express": "latest",
"express-session": "latest",
"grunt": "latest",
"grunt-bump": "latest",
"grunt-cli": "latest",
"grunt-concurrent": "latest",
"grunt-contrib-clean": "latest",
"grunt-contrib-concat": "latest",
"grunt-contrib-connect": "latest",
"grunt-contrib-copy": "latest",
"grunt-contrib-cssmin": "latest",
"grunt-contrib-jade": "latest",
"grunt-contrib-jshint": "latest",
"grunt-contrib-less": "latest",
"grunt-contrib-uglify": "latest",
"grunt-contrib-watch": "latest",
"grunt-newer": "latest",
"grunt-nodemon": "latest",
"hiredis": "latest",
"jade": "latest",
"load-grunt-tasks": "latest",
"moment": "latest",
"mongodb": "latest",
"mongoose": "latest",
"morgan": "^1.2.2",
"mysql": "latest",
"newrelic": "latest",
"nodemailer": "latest",
"nodemailer-smtp-transport": "latest",
"passport": "latest",
"passport.socketio": "latest",
"ratelimiter": "^1.0.1",
"redis": "latest",
"request": "^2.40.0",
"serve-static": "latest",
"socket.io": "latest",
"socket.io-client": "latest",
"speakeasy": "latest",
"validator": "latest"
},
"devDependencies": {
"should": "latest",
"supertest": "latest"
}
}

13
public/css/semaphore.less Normal file
View File

@ -0,0 +1,13 @@
@import '../vendor/bootstrap/less/variables.less';
@import '../vendor/fontawesome/less/variables.less';
@import '//fonts.googleapis.com/css?family=Roboto:300,400,400italic,700,500,700italic';
@import '//fonts.googleapis.com/css?family=Source+Code+Pro:300,400,500,700:latin';
@fa-font-path: "../vendor/fontawesome/fonts";
@font-family-sans-serif: Roboto, Arial, sans-serif;
@font-family-monospace: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
@font-family-base: @font-family-sans-serif;
@import '../vendor/bootstrap/less/bootstrap.less';
@import '../vendor/fontawesome/less/font-awesome.less';

36
public/js/app.js Normal file
View File

@ -0,0 +1,36 @@
define([
'angular',
'couchPotato'
], function(angular, couchPotato, ga) {
var app = angular.module('semaphore', ['scs.couch-potato', 'ui.router']);
couchPotato.configureApp(app);
app.run(function($rootScope, $window, $couchPotato) {
app.lazy = $couchPotato;
$rootScope.$on('$locationChangeStart', function(event, newUrl, oldUrl){
if (newUrl.match(/\&no_router/)) {
event.preventDefault();
$window.location.href = newUrl.replace(/\&no_router/, '');
}
});
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
if (toState.pageTitle) {
$rootScope.pageTitle = "Loading " + toState.pageTitle;
} else {
$rootScope.pageTitle = "Loading..";
}
});
$rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams){
if (toState.pageTitle) {
$rootScope.pageTitle = toState.pageTitle;
} else {
$rootScope.pageTitle = "Semaphore Page";
}
})
});
return app;
})

View File

@ -0,0 +1,35 @@
define([
'app'
], function(app) {
app.registerController('SignInCtrl', ['$scope', '$rootScope', '$http', '$state', function($scope, $rootScope, $http, $state) {
$scope.status = "";
$scope.user = {
auth: "",
password: ""
};
$scope.authenticate = function(user) {
$scope.status = "Authenticating..";
var pwd = user.password;
user.password = "";
$http.post('/auth/password', {
auth: user.auth,
password: pwd
}).success(function(data, status) {
$scope.status = "Login Successful";
window.location = "/";
}).error(function (data, status) {
if (status == 400) {
// Login Failed
$scope.status = data.message;
return;
}
$scope.status = status + ' Request Failed. Try again later.';
});
}
}]);
});

19
public/js/routes/auth.js Normal file
View File

@ -0,0 +1,19 @@
define([
'app'
], function(app) {
app.config(function($stateProvider, $urlRouterProvider, $locationProvider, $couchPotatoProvider) {
$locationProvider.html5Mode(true);
$urlRouterProvider.otherwise('/');
$stateProvider.state('login', {
url: '/',
pageTitle: "Sign In",
templateUrl: "/view/auth/login",
controller: "SignInCtrl",
resolve: {
dummy: $couchPotatoProvider.resolveDependencies(['controllers/auth/login'])
}
})
});
});

View File

@ -0,0 +1,35 @@
define([
'app',
'services/user'
], function(app) {
app.config(function($stateProvider, $urlRouterProvider, $locationProvider, $couchPotatoProvider) {
$locationProvider.html5Mode(true);
$urlRouterProvider.otherwise('');
$stateProvider
.state('homepage', {
url: '/',
pageTitle: 'Homepage',
templateUrl: "/view/homepage"
})
.state('logout', {
url: '/logout',
pageTitle: 'Log Out',
controller: function($scope) {
window.location = "/logout";
}
})
})
.run(function($rootScope, $state, $stateParams, $http, user) {
$rootScope.$state = $state
$rootScope.$stateParams = $stateParams
user.getUser(function() {})
$http.get('/playbooks').success(function(data, status) {
$rootScope.playbooks = data.playbooks;
})
})
})

34
public/js/semaphore.js Normal file
View File

@ -0,0 +1,34 @@
require.config({
paths: {
angular: '../vendor/angular/angular.min',
uiRouter: '../vendor/angular-ui-router/release/angular-ui-router.min',
jquery: '../vendor/jquery/dist/jquery.min',
moment: '../vendor/moment/moment',
bootstrap: '../vendor/bootstrap/dist/js/bootstrap.min',
couchPotato: '../vendor/angular-couch-potato/dist/angular-couch-potato'
},
shim: {
angular: {
exports: 'angular'
},
uiRouter: {
deps: ['angular']
},
bootstrap: ['jquery']
}
});
require([
'jquery',
'angular',
'couchPotato',
'uiRouter',
'app',
'routes/routes'
], function($, angular) {
var $html = angular.element(document.getElementsByTagName('html')[0]);
angular.element().ready(function() {
angular.bootstrap($html, ['semaphore'])
});
});

View File

@ -0,0 +1,34 @@
require.config({
paths: {
angular: '../vendor/angular/angular.min',
uiRouter: '../vendor/angular-ui-router/release/angular-ui-router.min',
jquery: '../vendor/jquery/dist/jquery.min',
moment: '../vendor/moment/moment',
bootstrap: '../vendor/bootstrap/dist/js/bootstrap.min',
couchPotato: '../vendor/angular-couch-potato/dist/angular-couch-potato'
},
shim: {
angular: {
exports: 'angular'
},
uiRouter: {
deps: ['angular']
},
bootstrap: ['jquery']
}
});
require([
'jquery',
'angular',
'couchPotato',
'uiRouter',
'app',
'routes/auth'
], function($, angular) {
var $html = angular.element(document.getElementsByTagName('html')[0]);
angular.element().ready(function() {
angular.bootstrap($html, ['semaphore'])
});
});

View File

@ -0,0 +1,15 @@
define([
'app'
], function(app) {
app.service('user', function($http, $rootScope) {
var self = this;
self.getUser = function(cb) {
$http.get('/profile').success(function(data) {
$rootScope.user = self.user = data.user;
cb();
});
}
});
});