Sails app

This commit is contained in:
Ben Ramey
2014-03-08 18:32:43 -06:00
parent fb64ad1f45
commit 2a34de5580
44 changed files with 5802 additions and 0 deletions

45
.gitignore vendored Normal file
View File

@@ -0,0 +1,45 @@
########################
# sails
########################
.sails
.waterline
.rigging
.tmp
########################
# node.js / npm
########################
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
node_modules
npm-debug.log
########################
# misc / editors
########################
*~
*#
.DS_STORE
.netbeans
nbproject
.idea
########################
# local config
########################
config/local.js

484
Gruntfile.js Normal file
View File

@@ -0,0 +1,484 @@
/**
* Gruntfile
*
* If you created your Sails app with `sails new foo --linker`,
* the following files will be automatically injected (in order)
* into the EJS and HTML files in your `views` and `assets` folders.
*
* At the top part of this file, you'll find a few of the most commonly
* configured options, but Sails' integration with Grunt is also fully
* customizable. If you'd like to work with your assets differently
* you can change this file to do anything you like!
*
* More information on using Grunt to work with static assets:
* http://gruntjs.com/configuring-tasks
*/
module.exports = function (grunt) {
/**
* CSS files to inject in order
* (uses Grunt-style wildcard/glob/splat expressions)
*
* By default, Sails also supports LESS in development and production.
* To use SASS/SCSS, Stylus, etc., edit the `sails-linker:devStyles` task
* below for more options. For this to work, you may need to install new
* dependencies, e.g. `npm install grunt-contrib-sass`
*/
var cssFilesToInject = [
'linker/**/*.css'
];
/**
* Javascript files to inject in order
* (uses Grunt-style wildcard/glob/splat expressions)
*
* To use client-side CoffeeScript, TypeScript, etc., edit the
* `sails-linker:devJs` task below for more options.
*/
var jsFilesToInject = [
// Below, as a demonstration, you'll see the built-in dependencies
// linked in the proper order order
// Bring in the socket.io client
'linker/js/socket.io.js',
// then beef it up with some convenience logic for talking to Sails.js
'linker/js/sails.io.js',
// A simpler boilerplate library for getting you up and running w/ an
// automatic listener for incoming messages from Socket.io.
'linker/js/app.js',
// *-> put other dependencies here <-*
// All of the rest of your app scripts imported here
'linker/**/*.js'
];
/**
* Client-side HTML templates are injected using the sources below
* The ordering of these templates shouldn't matter.
* (uses Grunt-style wildcard/glob/splat expressions)
*
* By default, Sails uses JST templates and precompiles them into
* functions for you. If you want to use jade, handlebars, dust, etc.,
* edit the relevant sections below.
*/
var templateFilesToInject = [
'linker/**/*.html'
];
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
//
// DANGER:
//
// With great power comes great responsibility.
//
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
// Modify css file injection paths to use
cssFilesToInject = cssFilesToInject.map(function (path) {
return '.tmp/public/' + path;
});
// Modify js file injection paths to use
jsFilesToInject = jsFilesToInject.map(function (path) {
return '.tmp/public/' + path;
});
templateFilesToInject = templateFilesToInject.map(function (path) {
return 'assets/' + path;
});
// Get path to core grunt dependencies from Sails
var depsPath = grunt.option('gdsrc') || 'node_modules/sails/node_modules';
grunt.loadTasks(depsPath + '/grunt-contrib-clean/tasks');
grunt.loadTasks(depsPath + '/grunt-contrib-copy/tasks');
grunt.loadTasks(depsPath + '/grunt-contrib-concat/tasks');
grunt.loadTasks(depsPath + '/grunt-sails-linker/tasks');
grunt.loadTasks(depsPath + '/grunt-contrib-jst/tasks');
grunt.loadTasks(depsPath + '/grunt-contrib-watch/tasks');
grunt.loadTasks(depsPath + '/grunt-contrib-uglify/tasks');
grunt.loadTasks(depsPath + '/grunt-contrib-cssmin/tasks');
grunt.loadTasks(depsPath + '/grunt-contrib-less/tasks');
grunt.loadTasks(depsPath + '/grunt-contrib-coffee/tasks');
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
copy: {
dev: {
files: [
{
expand: true,
cwd: './assets',
src: ['**/*.!(coffee)'],
dest: '.tmp/public'
}
]
},
build: {
files: [
{
expand: true,
cwd: '.tmp/public',
src: ['**/*'],
dest: 'www'
}
]
}
},
clean: {
dev: ['.tmp/public/**'],
build: ['www']
},
jst: {
dev: {
// To use other sorts of templates, specify the regexp below:
// options: {
// templateSettings: {
// interpolate: /\{\{(.+?)\}\}/g
// }
// },
files: {
'.tmp/public/jst.js': templateFilesToInject
}
}
},
less: {
dev: {
files: [
{
expand: true,
cwd: 'assets/styles/',
src: ['*.less'],
dest: '.tmp/public/styles/',
ext: '.css'
}, {
expand: true,
cwd: 'assets/linker/styles/',
src: ['*.less'],
dest: '.tmp/public/linker/styles/',
ext: '.css'
}
]
}
},
coffee: {
dev: {
options:{
bare:true
},
files: [
{
expand: true,
cwd: 'assets/js/',
src: ['**/*.coffee'],
dest: '.tmp/public/js/',
ext: '.js'
}, {
expand: true,
cwd: 'assets/linker/js/',
src: ['**/*.coffee'],
dest: '.tmp/public/linker/js/',
ext: '.js'
}
]
}
},
concat: {
js: {
src: jsFilesToInject,
dest: '.tmp/public/concat/production.js'
},
css: {
src: cssFilesToInject,
dest: '.tmp/public/concat/production.css'
}
},
uglify: {
dist: {
src: ['.tmp/public/concat/production.js'],
dest: '.tmp/public/min/production.js'
}
},
cssmin: {
dist: {
src: ['.tmp/public/concat/production.css'],
dest: '.tmp/public/min/production.css'
}
},
'sails-linker': {
devJs: {
options: {
startTag: '<!--SCRIPTS-->',
endTag: '<!--SCRIPTS END-->',
fileTmpl: '<script src="%s"></script>',
appRoot: '.tmp/public'
},
files: {
'.tmp/public/**/*.html': jsFilesToInject,
'views/**/*.html': jsFilesToInject,
'views/**/*.ejs': jsFilesToInject
}
},
prodJs: {
options: {
startTag: '<!--SCRIPTS-->',
endTag: '<!--SCRIPTS END-->',
fileTmpl: '<script src="%s"></script>',
appRoot: '.tmp/public'
},
files: {
'.tmp/public/**/*.html': ['.tmp/public/min/production.js'],
'views/**/*.html': ['.tmp/public/min/production.js'],
'views/**/*.ejs': ['.tmp/public/min/production.js']
}
},
devStyles: {
options: {
startTag: '<!--STYLES-->',
endTag: '<!--STYLES END-->',
fileTmpl: '<link rel="stylesheet" href="%s">',
appRoot: '.tmp/public'
},
// cssFilesToInject defined up top
files: {
'.tmp/public/**/*.html': cssFilesToInject,
'views/**/*.html': cssFilesToInject,
'views/**/*.ejs': cssFilesToInject
}
},
prodStyles: {
options: {
startTag: '<!--STYLES-->',
endTag: '<!--STYLES END-->',
fileTmpl: '<link rel="stylesheet" href="%s">',
appRoot: '.tmp/public'
},
files: {
'.tmp/public/index.html': ['.tmp/public/min/production.css'],
'views/**/*.html': ['.tmp/public/min/production.css'],
'views/**/*.ejs': ['.tmp/public/min/production.css']
}
},
// Bring in JST template object
devTpl: {
options: {
startTag: '<!--TEMPLATES-->',
endTag: '<!--TEMPLATES END-->',
fileTmpl: '<script type="text/javascript" src="%s"></script>',
appRoot: '.tmp/public'
},
files: {
'.tmp/public/index.html': ['.tmp/public/jst.js'],
'views/**/*.html': ['.tmp/public/jst.js'],
'views/**/*.ejs': ['.tmp/public/jst.js']
}
},
/*******************************************
* Jade linkers (TODO: clean this up)
*******************************************/
devJsJADE: {
options: {
startTag: '// SCRIPTS',
endTag: '// SCRIPTS END',
fileTmpl: 'script(type="text/javascript", src="%s")',
appRoot: '.tmp/public'
},
files: {
'views/**/*.jade': jsFilesToInject
}
},
prodJsJADE: {
options: {
startTag: '// SCRIPTS',
endTag: '// SCRIPTS END',
fileTmpl: 'script(type="text/javascript", src="%s")',
appRoot: '.tmp/public'
},
files: {
'views/**/*.jade': ['.tmp/public/min/production.js']
}
},
devStylesJADE: {
options: {
startTag: '// STYLES',
endTag: '// STYLES END',
fileTmpl: 'link(rel="stylesheet", href="%s")',
appRoot: '.tmp/public'
},
files: {
'views/**/*.jade': cssFilesToInject
}
},
prodStylesJADE: {
options: {
startTag: '// STYLES',
endTag: '// STYLES END',
fileTmpl: 'link(rel="stylesheet", href="%s")',
appRoot: '.tmp/public'
},
files: {
'views/**/*.jade': ['.tmp/public/min/production.css']
}
},
// Bring in JST template object
devTplJADE: {
options: {
startTag: '// TEMPLATES',
endTag: '// TEMPLATES END',
fileTmpl: 'script(type="text/javascript", src="%s")',
appRoot: '.tmp/public'
},
files: {
'views/**/*.jade': ['.tmp/public/jst.js']
}
}
/************************************
* Jade linker end
************************************/
},
watch: {
api: {
// API files to watch:
files: ['api/**/*']
},
assets: {
// Assets to watch:
files: ['assets/**/*'],
// When assets are changed:
tasks: ['compileAssets', 'linkAssets']
}
}
});
// When Sails is lifted:
grunt.registerTask('default', [
'compileAssets',
'linkAssets',
'watch'
]);
grunt.registerTask('compileAssets', [
'clean:dev',
'jst:dev',
'less:dev',
'copy:dev',
'coffee:dev'
]);
grunt.registerTask('linkAssets', [
// Update link/script/template references in `assets` index.html
'sails-linker:devJs',
'sails-linker:devStyles',
'sails-linker:devTpl',
'sails-linker:devJsJADE',
'sails-linker:devStylesJADE',
'sails-linker:devTplJADE'
]);
// Build the assets into a web accessible folder.
// (handy for phone gap apps, chrome extensions, etc.)
grunt.registerTask('build', [
'compileAssets',
'linkAssets',
'clean:build',
'copy:build'
]);
// When sails is lifted in production
grunt.registerTask('prod', [
'clean:dev',
'jst:dev',
'less:dev',
'copy:dev',
'coffee:dev',
'concat',
'uglify',
'cssmin',
'sails-linker:prodJs',
'sails-linker:prodStyles',
'sails-linker:devTpl',
'sails-linker:prodJsJADE',
'sails-linker:prodStylesJADE',
'sails-linker:devTplJADE'
]);
// When API files are changed:
// grunt.event.on('watch', function(action, filepath) {
// grunt.log.writeln(filepath + ' has ' + action);
// // Send a request to a development-only endpoint on the server
// // which will reuptake the file that was changed.
// var baseurl = grunt.option('baseurl');
// var gruntSignalRoute = grunt.option('signalpath');
// var url = baseurl + gruntSignalRoute + '?action=' + action + '&filepath=' + filepath;
// require('http').get(url)
// .on('error', function(e) {
// console.error(filepath + ' has ' + action + ', but could not signal the Sails.js server: ' + e.message);
// });
// });
};

2
README.bak.md Normal file
View File

@@ -0,0 +1,2 @@
# lunch
### a Sails application

0
api/adapters/.gitkeep Normal file
View File

0
api/controllers/.gitkeep Normal file
View File

0
api/models/.gitkeep Normal file
View File

View File

@@ -0,0 +1,21 @@
/**
* isAuthenticated
*
* @module :: Policy
* @description :: Simple policy to allow any authenticated user
* Assumes that your login action in one of your controllers sets `req.session.authenticated = true;`
* @docs :: http://sailsjs.org/#!documentation/policies
*
*/
module.exports = function(req, res, next) {
// User is allowed, proceed to the next policy,
// or if this is the last policy, the controller
if (req.session.authenticated) {
return next();
}
// User is not allowed
// (default res.forbidden() behavior can be overridden in `config/403.js`)
return res.forbidden('You are not permitted to perform this action.');
};

0
api/services/.gitkeep Normal file
View File

2
app.js Normal file
View File

@@ -0,0 +1,2 @@
// Start sails and pass it command line arguments
require('sails').lift(require('optimist').argv);

BIN
assets/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 920 B

0
assets/images/.gitkeep Normal file
View File

0
assets/js/.gitkeep Normal file
View File

71
assets/js/app.js Normal file
View File

@@ -0,0 +1,71 @@
/**
* app.js
*
* This file contains some conventional defaults for working with Socket.io + Sails.
* It is designed to get you up and running fast, but is by no means anything special.
*
* Feel free to change none, some, or ALL of this file to fit your needs!
*/
(function (io) {
// as soon as this file is loaded, connect automatically,
var socket = io.connect();
if (typeof console !== 'undefined') {
log('Connecting to Sails.js...');
}
socket.on('connect', function socketConnected() {
// Listen for Comet messages from Sails
socket.on('message', function messageReceived(message) {
///////////////////////////////////////////////////////////
// Replace the following with your own custom logic
// to run when a new message arrives from the Sails.js
// server.
///////////////////////////////////////////////////////////
log('New comet message received :: ', message);
//////////////////////////////////////////////////////
});
///////////////////////////////////////////////////////////
// Here's where you'll want to add any custom logic for
// when the browser establishes its socket connection to
// the Sails.js server.
///////////////////////////////////////////////////////////
log(
'Socket is now connected and globally accessible as `socket`.\n' +
'e.g. to send a GET request to Sails, try \n' +
'`socket.get("/", function (response) ' +
'{ console.log(response); })`'
);
///////////////////////////////////////////////////////////
});
// Expose connected `socket` instance globally so that it's easy
// to experiment with from the browser console while prototyping.
window.socket = socket;
// Simple log function to keep the example simple
function log () {
if (typeof console !== 'undefined') {
console.log.apply(console, arguments);
}
}
})(
// In case you're wrapping socket.io to prevent pollution of the global namespace,
// you can replace `window.io` with your own `io` here:
window.io
);

166
assets/js/sails.io.js Normal file
View File

@@ -0,0 +1,166 @@
/**
* sails.io.js
*
* This file allows you to send and receive socket.io messages to & from Sails
* by simulating a REST client interface on top of socket.io.
*
* It models its API after the $.ajax pattern from jQuery you might be familiar with.
*
* So to switch from using AJAX to Socket.io, instead of:
* `$.post( url, [data], [cb] )`
*
* You would use:
* `socket.post( url, [data], [cb] )`
*
* For more information, visit:
* http://sailsjs.org/#documentation
*/
(function (io) {
// We'll be adding methods to `io.SocketNamespace.prototype`, the prototype for the
// Socket instance returned when the browser connects with `io.connect()`
var Socket = io.SocketNamespace;
/**
* Simulate a GET request to sails
* e.g.
* `socket.get('/user/3', Stats.populate)`
*
* @param {String} url :: destination URL
* @param {Object} params :: parameters to send with the request [optional]
* @param {Function} cb :: callback function to call when finished [optional]
*/
Socket.prototype.get = function (url, data, cb) {
return this.request(url, data, cb, 'get');
};
/**
* Simulate a POST request to sails
* e.g.
* `socket.post('/event', newMeeting, $spinner.hide)`
*
* @param {String} url :: destination URL
* @param {Object} params :: parameters to send with the request [optional]
* @param {Function} cb :: callback function to call when finished [optional]
*/
Socket.prototype.post = function (url, data, cb) {
return this.request(url, data, cb, 'post');
};
/**
* Simulate a PUT request to sails
* e.g.
* `socket.post('/event/3', changedFields, $spinner.hide)`
*
* @param {String} url :: destination URL
* @param {Object} params :: parameters to send with the request [optional]
* @param {Function} cb :: callback function to call when finished [optional]
*/
Socket.prototype.put = function (url, data, cb) {
return this.request(url, data, cb, 'put');
};
/**
* Simulate a DELETE request to sails
* e.g.
* `socket.delete('/event', $spinner.hide)`
*
* @param {String} url :: destination URL
* @param {Object} params :: parameters to send with the request [optional]
* @param {Function} cb :: callback function to call when finished [optional]
*/
Socket.prototype['delete'] = function (url, data, cb) {
return this.request(url, data, cb, 'delete');
};
/**
* Simulate HTTP over Socket.io
* @api private :: but exposed for backwards compatibility w/ <= sails@~0.8
*/
Socket.prototype.request = request;
function request (url, data, cb, method) {
var socket = this;
var usage = 'Usage:\n socket.' +
(method || 'request') +
'( destinationURL, dataToSend, fnToCallWhenComplete )';
// Remove trailing slashes and spaces
url = url.replace(/^(.+)\/*\s*$/, '$1');
// If method is undefined, use 'get'
method = method || 'get';
if ( typeof url !== 'string' ) {
throw new Error('Invalid or missing URL!\n' + usage);
}
// Allow data arg to be optional
if ( typeof data === 'function' ) {
cb = data;
data = {};
}
// Build to request
var json = io.JSON.stringify({
url: url,
data: data
});
// Send the message over the socket
socket.emit(method, json, function afterEmitted (result) {
var parsedResult = result;
if (result && typeof result === 'string') {
try {
parsedResult = io.JSON.parse(result);
} catch (e) {
if (typeof console !== 'undefined') {
console.warn("Could not parse:", result, e);
}
throw new Error("Server response could not be parsed!\n" + result);
}
}
// TODO: Handle errors more effectively
if (parsedResult === 404) throw new Error("404: Not found");
if (parsedResult === 403) throw new Error("403: Forbidden");
if (parsedResult === 500) throw new Error("500: Server error");
cb && cb(parsedResult);
});
}
}) (
// In case you're wrapping socket.io to prevent pollution of the global namespace,
// you can replace `window.io` with your own `io` here:
window.io
);

3328
assets/js/socket.io.js Normal file

File diff suppressed because it is too large Load Diff

6
assets/robots.txt Normal file
View File

@@ -0,0 +1,6 @@
# The robots.txt file is used to control how search engines index your live URLs.
# See http://www.robotstxt.org/wc/norobots.html for more information.
#
# To prevent search engines from seeing the site altogether, uncomment the next two lines:
# User-Agent: *
# Disallow: /

0
assets/styles/.gitkeep Normal file
View File

73
config/400.js Normal file
View File

@@ -0,0 +1,73 @@
/**
* Default 400 (Bad Request) handler
*
* Sails will automatically respond using this middleware when a blueprint is requested
* with missing or invalid parameters
* (e.g. `POST /user` was used to create a user, but required parameters were missing)
*
* This middleware can also be invoked manually from a controller or policy:
* res.badRequest( [validationErrors], [redirectTo] )
*
*
* @param {Array|Object|String} validationErrors
* optional errors
* usually an array of validation errors from the ORM
*
* @param {String} redirectTo
* optional URL
* (absolute or relative, e.g. google.com/foo or /bar/baz)
* of the page to redirect to. Usually only relevant for traditional HTTP requests,
* since if this was triggered from an AJAX or socket request, JSON should be sent instead.
*/
module.exports[400] = function badRequest(validationErrors, redirectTo, req, res) {
/*
* NOTE: This function is Sails middleware-- that means that not only do `req` and `res`
* work just like their Express equivalents to handle HTTP requests, they also simulate
* the same interface for receiving socket messages.
*/
var statusCode = 400;
var result = {
status: statusCode
};
// Optional validationErrors object
if (validationErrors) {
result.validationErrors = validationErrors;
}
// For requesters expecting JSON, everything works like you would expect-- a simple JSON response
// indicating the 400: Bad Request status with relevant information will be returned.
if (req.wantsJSON) {
return res.json(result, result.status);
}
// For traditional (not-AJAX) web forms, this middleware follows best-practices
// for when a user submits invalid form data:
// i. First, a one-time-use flash variable is populated, probably a string message or an array
// of semantic validation error objects.
// ii. Then the user is redirected back to `redirectTo`, i.e. the URL where the bad request originated.
// iii. There, the controller and/or view might use the flash `errors` to either display a message or highlight
// the invalid HTML form fields.
if (redirectTo) {
// Set flash message called `errors` (one-time-use in session)
req.flash('errors', validationErrors);
// then redirect back to the `redirectTo` URL
return res.redirect(redirectTo);
}
// Depending on your app's needs, you may choose to look at the Referer header here
// and redirect back. Please do so at your own risk!
// For security reasons, Sails does not provide this affordance by default.
// It's safest to provide a 'redirectTo' URL and redirect there directly.
// If `redirectTo` was not specified, just respond w/ JSON
return res.json(result, result.status);
};

52
config/403.js Normal file
View File

@@ -0,0 +1,52 @@
/**
* Default 403 (Forbidden) middleware
*
* This middleware can be invoked from a controller or policy:
* res.forbidden( [message] )
*
*
* @param {String|Object|Array} message
* optional message to inject into view locals or JSON response
*
*/
module.exports[403] = function badRequest(message, req, res) {
/*
* NOTE: This function is Sails middleware-- that means that not only do `req` and `res`
* work just like their Express equivalents to handle HTTP requests, they also simulate
* the same interface for receiving socket messages.
*/
var viewFilePath = '403';
var statusCode = 403;
var result = {
status: statusCode
};
// Optional message
if (message) {
result.message = message;
}
// If the user-agent wants a JSON response, send json
if (req.wantsJSON) {
return res.json(result, result.status);
}
// Set status code and view locals
res.status(result.status);
for (var key in result) {
res.locals[key] = result[key];
}
// And render view
res.render(viewFilePath, result, function (err) {
// If the view doesn't exist, or an error occured, send json
if (err) { return res.json(result, result.status); }
// Otherwise, serve the `views/403.*` page
res.render(viewFilePath);
});
};

38
config/404.js Normal file
View File

@@ -0,0 +1,38 @@
/**
* Default 404 (Not Found) handler
*
* If no route matches are found for a request, Sails will respond using this handler.
*
* This middleware can also be invoked manually from a controller or policy:
* Usage: res.notFound()
*/
module.exports[404] = function pageNotFound(req, res) {
/*
* NOTE: This function is Sails middleware-- that means that not only do `req` and `res`
* work just like their Express equivalents to handle HTTP requests, they also simulate
* the same interface for receiving socket messages.
*/
var viewFilePath = '404';
var statusCode = 404;
var result = {
status: statusCode
};
// If the user-agent wants a JSON response, send json
if (req.wantsJSON) {
return res.json(result, result.status);
}
res.status(result.status);
res.render(viewFilePath, function (err) {
// If the view doesn't exist, or an error occured, send json
if (err) { return res.json(result, result.status); }
// Otherwise, serve the `views/404.*` page
res.render(viewFilePath);
});
};

77
config/500.js Normal file
View File

@@ -0,0 +1,77 @@
/**
* Default 500 (Server Error) middleware
*
* If an error is thrown in a policy or controller,
* Sails will respond using this default error handler
*
* This middleware can also be invoked manually from a controller or policy:
* res.serverError( [errors] )
*
*
* @param {Array|Object|String} errors
* optional errors
*/
module.exports[500] = function serverErrorOccurred(errors, req, res) {
/*
* NOTE: This function is Sails middleware-- that means that not only do `req` and `res`
* work just like their Express equivalents to handle HTTP requests, they also simulate
* the same interface for receiving socket messages.
*/
var viewFilePath = '500',
statusCode = 500,
i, errorToLog, errorToJSON;
var result = {
status: statusCode
};
// Normalize a {String|Object|Error} or array of {String|Object|Error}
// into an array of proper, readable {Error}
var errorsToDisplay = sails.util.normalizeErrors(errors);
for (i in errorsToDisplay) {
// Log error(s) as clean `stack`
// (avoids ending up with \n, etc.)
if ( errorsToDisplay[i].original ) {
errorToLog = sails.util.inspect(errorsToDisplay[i].original);
}
else {
errorToLog = errorsToDisplay[i].stack;
}
sails.log.error('Server Error (500)');
sails.log.error(errorToLog);
// Use original error if it exists
errorToJSON = errorsToDisplay[i].original || errorsToDisplay[i].message;
errorsToDisplay[i] = errorToJSON;
}
// Only include errors if application environment is set to 'development'
// In production, don't display any identifying information about the error(s)
if (sails.config.environment === 'development') {
result.errors = errorsToDisplay;
}
// If the user-agent wants JSON, respond with JSON
if (req.wantsJSON) {
return res.json(result, result.status);
}
// Set status code and view locals
res.status(result.status);
for (var key in result) {
res.locals[key] = result[key];
}
// And render view
res.render(viewFilePath, result, function (err) {
// If the view doesn't exist, or an error occured, just send JSON
if (err) { return res.json(result, result.status); }
// Otherwise, if it can be rendered, the `views/500.*` page is rendered
res.render(viewFilePath, result);
});
};

39
config/adapters.js Normal file
View File

@@ -0,0 +1,39 @@
/**
* Global adapter config
*
* The `adapters` configuration object lets you create different global "saved settings"
* that you can mix and match in your models. The `default` option indicates which
* "saved setting" should be used if a model doesn't have an adapter specified.
*
* Keep in mind that options you define directly in your model definitions
* will override these settings.
*
* For more information on adapter configuration, check out:
* http://sailsjs.org/#documentation
*/
module.exports.adapters = {
// If you leave the adapter config unspecified
// in a model definition, 'default' will be used.
'default': 'disk',
// Persistent adapter for DEVELOPMENT ONLY
// (data is preserved when the server shuts down)
disk: {
module: 'sails-disk'
},
// MySQL is the world's most popular relational database.
// Learn more: http://en.wikipedia.org/wiki/MySQL
myLocalMySQLDatabase: {
module: 'sails-mysql',
host: 'YOUR_MYSQL_SERVER_HOSTNAME_OR_IP_ADDRESS',
user: 'YOUR_MYSQL_USER',
// Psst.. You can put your password in config/local.js instead
// so you don't inadvertently push it up if you're using version control
password: 'YOUR_MYSQL_PASSWORD',
database: 'YOUR_MYSQL_DB'
}
};

16
config/bootstrap.js vendored Normal file
View File

@@ -0,0 +1,16 @@
/**
* Bootstrap
*
* An asynchronous boostrap function that runs before your Sails app gets lifted.
* This gives you an opportunity to set up your data model, run jobs, or perform some special logic.
*
* For more information on bootstrapping your app, check out:
* http://sailsjs.org/#documentation
*/
module.exports.bootstrap = function (cb) {
// It's very important to trigger this callack method when you are finished
// with the bootstrap! (otherwise your server will never lift, since it's waiting on the bootstrap)
cb();
};

169
config/controllers.js Normal file
View File

@@ -0,0 +1,169 @@
/**
* Controllers
*
* By default, Sails inspects your controllers, models, and configuration and binds
* certain routes automatically. These dynamically generated routes are called blueprints.
*
* These settings are for the global configuration of controllers & blueprint routes.
* You may also override these settings on a per-controller basis by defining a '_config'
* key in any of your controller files, and assigning it an object, e.g.:
* {
* // ...
* _config: { blueprints: { rest: false } }
* // ...
* }
*
* For more information on configuring controllers and blueprints, check out:
* http://sailsjs.org/#documentation
*/
module.exports.controllers = {
/**
* NOTE:
* A lot of the configuration options below affect so-called "CRUD methods",
* or your controllers' `find`, `create`, `update`, and `destroy` actions.
*
* It's important to realize that, even if you haven't defined these yourself, as long as
* a model exists with the same name as the controller, Sails will respond with built-in CRUD
* logic in the form of a JSON API, including support for sort, pagination, and filtering.
*/
blueprints: {
/**
* `actions`
*
* Action blueprints speed up backend development and shorten the development workflow by
* eliminating the need to manually bind routes.
* When enabled, GET, POST, PUT, and DELETE routes will be generated for every one of a controller's actions.
*
* If an `index` action exists, additional naked routes will be created for it.
* Finally, all `actions` blueprints support an optional path parameter, `id`, for convenience.
*
* For example, assume we have an EmailController with actions `send` and `index`.
* With `actions` enabled, the following blueprint routes would be bound at runtime:
*
* `EmailController.index`
* :::::::::::::::::::::::::::::::::::::::::::::::::::::::
* `GET /email/:id?` `GET /email/index/:id?`
* `POST /email/:id?` `POST /email/index/:id?`
* `PUT /email/:id?` `PUT /email/index/:id?`
* `DELETE /email/:id?` `DELETE /email/index/:id?`
*
* `EmailController.send`
* :::::::::::::::::::::::::::::::::::::::::::::::::::::::
* `GET /email/send/:id?`
* `POST /email/send/:id?`
* `PUT /email/send/:id?`
* `DELETE /email/send/:id?`
*
*
* `actions` are enabled by default, and are OK for production-- however,
* you must take great care not to inadvertently expose unsafe controller logic to GET requests.
*/
actions: true,
/**
* `rest`
*
* REST blueprints are the automatically generated routes Sails uses to expose
* a conventional REST API on top of a controller's `find`, `create`, `update`, and `destroy`
* actions.
*
* For example, a BoatController with `rest` enabled generates the following routes:
* :::::::::::::::::::::::::::::::::::::::::::::::::::::::
* GET /boat/:id? -> BoatController.find
* POST /boat -> BoatController.create
* PUT /boat/:id -> BoatController.update
* DELETE /boat/:id -> BoatController.destroy
*
* `rest` blueprints are enabled by default, and suitable for a production scenario.
*/
rest: true,
/**
* `shortcuts`
*
* Shortcut blueprints are simple helpers to provide access to a controller's CRUD methods
* from your browser's URL bar. When enabled, GET, POST, PUT, and DELETE routes will be generated
* for the controller's`find`, `create`, `update`, and `destroy` actions.
*
* `shortcuts` are enabled by default, but SHOULD BE DISABLED IN PRODUCTION!!!!!
*/
shortcuts: true,
/**
* `prefix`
*
* An optional mount path for all blueprint routes on a controller, including `rest`,
* `actions`, and `shortcuts`. This allows you to continue to use blueprints, even if you
* need to namespace your API methods.
*
* For example, `prefix: '/api/v2'` would make the following REST blueprint routes
* for a FooController:
*
* `GET /api/v2/foo/:id?`
* `POST /api/v2/foo`
* `PUT /api/v2/foo/:id`
* `DELETE /api/v2/foo/:id`
*
* By default, no prefix is used.
*/
prefix: '',
/**
* `pluralize`
*
* Whether to pluralize controller names in generated routes
*
* For example, REST blueprints for `FooController` with `pluralize` enabled:
* GET /foos/:id?
* POST /foos
* PUT /foos/:id?
* DELETE /foos/:id?
*/
pluralize: false
},
/**
* `jsonp`
*
* If enabled, allows built-in CRUD methods to support JSONP for cross-domain requests.
*
* Example usage (REST blueprint + UserController):
* `GET /user?name=ciaran&limit=10&callback=receiveJSONPResponse`
*
* Defaults to false.
*/
jsonp: false,
/**
* `expectIntegerId`
*
* If enabled, built-in CRUD methods will only accept valid integers as an :id parameter.
*
* i.e. trigger built-in API if requests look like:
* `GET /user/8`
* but not like:
* `GET /user/a8j4g9jsd9ga4ghjasdha`
*
* Defaults to false.
*/
expectIntegerId: false
};

49
config/cors.js Normal file
View File

@@ -0,0 +1,49 @@
/**
* Cross-Origin Resource Sharing (CORS)
*
* CORS is like a more modern version of JSONP-- it allows your server/API
* to successfully respond to requests from client-side JavaScript code
* running on some other domain (e.g. google.com)
* Unlike JSONP, it works with POST, PUT, and DELETE requests
*
* For more information on CORS, check out:
* http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
*
* Note that any of these settings (besides 'allRoutes') can be changed on a per-route basis
* by adding a "cors" object to the route configuration:
*
* '/get foo': {
* controller: 'foo',
* action: 'bar',
* cors: {
* origin: 'http://foobar.com,https://owlhoot.com'
* }
* }
*
*/
module.exports.cors = {
// Allow CORS on all routes by default? If not, you must enable CORS on a
// per-route basis by either adding a "cors" configuration object
// to the route config, or setting "cors:true" in the route config to
// use the default settings below.
allRoutes: false,
// Which domains which are allowed CORS access?
// This can be a comma-delimited list of hosts (beginning with http:// or https://)
// or "*" to allow all domains CORS access.
origin: '*',
// Allow cookies to be shared for CORS requests?
credentials: true,
// Which methods should be allowed for CORS requests? This is only used
// in response to preflight requests (see article linked above for more info)
methods: 'GET, POST, PUT, DELETE, OPTIONS, HEAD',
// Which headers should be allowed for CORS requests? This is only used
// in response to preflight requests.
headers: 'content-type'
};

40
config/csrf.js Normal file
View File

@@ -0,0 +1,40 @@
/**
* Cross-Site Request Forgery Protection
*
* CSRF tokens are like a tracking chip. While a session tells the server that a user
* "is who they say they are", a csrf token tells the server "you are where you say you are".
*
* When enabled, all non-GET requests to the Sails server must be accompanied by
* a special token, identified as the '_csrf' parameter.
*
* This option protects your Sails app against cross-site request forgery (or CSRF) attacks.
* A would-be attacker needs not only a user's session cookie, but also this timestamped,
* secret CSRF token, which is refreshed/granted when the user visits a URL on your app's domain.
*
* This allows us to have certainty that our users' requests haven't been hijacked,
* and that the requests they're making are intentional and legitimate.
*
* This token has a short-lived expiration timeline, and must be acquired by either:
*
* (a) For traditional view-driven web apps:
* Fetching it from one of your views, where it may be accessed as
* a local variable, e.g.:
* <form>
* <input type="hidden" name="_csrf" value="<%= _csrf %>" />
* </form>
*
* or (b) For AJAX/Socket-heavy and/or single-page apps:
* Sending a GET request to the `/csrfToken` route, where it will be returned
* as JSON, e.g.:
* { _csrf: 'ajg4JD(JGdajhLJALHDa' }
*
*
* Enabling this option requires managing the token in your front-end app.
* For traditional web apps, it's as easy as passing the data from a view into a form action.
* In AJAX/Socket-heavy apps, just send a GET request to the /csrfToken route to get a valid token.
*
* For more information on CSRF, check out:
* http://en.wikipedia.org/wiki/Cross-site_request_forgery
*/
module.exports.csrf = false;

17
config/i18n.js Normal file
View File

@@ -0,0 +1,17 @@
/**
* Internationalization / Localization Settings
*
* If your app will touch people from all over the world, i18n (or internationalization)
* may be an important part of your international strategy.
*
*
* For more information, check out:
* http://sailsjs.org/#documentation
*/
module.exports.i18n = {
// Which locales are supported?
locales: ['en', 'es', 'fr', 'de']
};

24
config/locales/_README.md Normal file
View File

@@ -0,0 +1,24 @@
# Internationalization / Localization Settings
## Locale
All locale files live under `config/locales`. Here is where you can add locale data as JSON key-value pairs. The name of the file should match the language that you are supporting, which allows for automatic language detection based on the user request.
Here is an example locale stringfile for the Spanish language (`config/locales/es.json`):
```json
{
"Hello!": "Hola!",
"Hello %s, how are you today?": "¿Hola %s, como estas?",
}
```
## Usage
Locales can be accessed in controllers/policies through `res.i18n()`, or in views through the `__(key)` or `i18n(key)` functions.
Remember that the keys are case sensitive and require exact key matches, e.g.
```ejs
<h1> <%= __('Welcome to PencilPals!') %> </h1>
<h2> <%= i18n('Hello %s, how are you today?', 'Pencil Maven') %> </h2>
<p> <%= i18n('That\'s right-- you can use either i18n() or __()') %> </p>
```
## Configuration
Localization/internationalization config can be found in `config/i18n.js`, from where you can set your supported locales.

3
config/locales/de.json Normal file
View File

@@ -0,0 +1,3 @@
{
"Welcome": "Wilkommen"
}

3
config/locales/en.json Normal file
View File

@@ -0,0 +1,3 @@
{
"Welcome": "Welcome"
}

3
config/locales/es.json Normal file
View File

@@ -0,0 +1,3 @@
{
"Welcome": "Bienvenido"
}

3
config/locales/fr.json Normal file
View File

@@ -0,0 +1,3 @@
{
"Welcome": "Bienvenue"
}

27
config/log.js Normal file
View File

@@ -0,0 +1,27 @@
/**
* Logger configuration
*
* Configure the log level for your app, as well as the transport
* (Underneath the covers, Sails uses Winston for logging, which
* allows for some pretty neat custom transports/adapters for log messages)
*
* For more information on the Sails logger, check out:
* http://sailsjs.org/#documentation
*/
module.exports = {
// Valid `level` configs:
// i.e. the minimum log level to capture with sails.log.*()
//
// 'error' : Display calls to `.error()`
// 'warn' : Display calls from `.error()` to `.warn()`
// 'debug' : Display calls from `.error()`, `.warn()` to `.debug()`
// 'info' : Display calls from `.error()`, `.warn()`, `.debug()` to `.info()`
// 'verbose': Display calls from `.error()`, `.warn()`, `.debug()`, `.info()` to `.verbose()`
//
log: {
level: 'info'
}
};

83
config/policies.js Normal file
View File

@@ -0,0 +1,83 @@
/**
* Policy mappings (ACL)
*
* Policies are simply Express middleware functions which run **before** your controllers.
* You can apply one or more policies to a given controller, or protect just one of its actions.
*
* Any policy file (e.g. `authenticated.js`) can be dropped into the `/policies` folder,
* at which point it can be accessed below by its filename, minus the extension, (e.g. `authenticated`)
*
* For more information on policies, check out:
* http://sailsjs.org/#documentation
*/
module.exports.policies = {
// Default policy for all controllers and actions
// (`true` allows public access)
'*': true
/*
// Here's an example of adding some policies to a controller
RabbitController: {
// Apply the `false` policy as the default for all of RabbitController's actions
// (`false` prevents all access, which ensures that nothing bad happens to our rabbits)
'*': false,
// For the action `nurture`, apply the 'isRabbitMother' policy
// (this overrides `false` above)
nurture : 'isRabbitMother',
// Apply the `isNiceToAnimals` AND `hasRabbitFood` policies
// before letting any users feed our rabbits
feed : ['isNiceToAnimals', 'hasRabbitFood']
}
*/
};
/**
* Here's what the `isNiceToAnimals` policy from above might look like:
* (this file would be located at `policies/isNiceToAnimals.js`)
*
* We'll make some educated guesses about whether our system will
* consider this user someone who is nice to animals.
*
* Besides protecting rabbits (while a noble cause, no doubt),
* here are a few other example use cases for policies:
*
* + cookie-based authentication
* + role-based access control
* + limiting file uploads based on MB quotas
* + OAuth
* + BasicAuth
* + or any other kind of authentication scheme you can imagine
*
*/
/*
module.exports = function isNiceToAnimals (req, res, next) {
// `req.session` contains a set of data specific to the user making this request.
// It's kind of like our app's "memory" of the current user.
// If our user has a history of animal cruelty, not only will we
// prevent her from going even one step further (`return`),
// we'll go ahead and redirect her to PETA (`res.redirect`).
if ( req.session.user.hasHistoryOfAnimalCruelty ) {
return res.redirect('http://PETA.org');
}
// If the user has been seen frowning at puppies, we have to assume that
// they might end up being mean to them, so we'll
if ( req.session.user.frownsAtPuppies ) {
return res.redirect('http://www.dailypuppy.com/');
}
// Finally, if the user has a clean record, we'll call the `next()` function
// to let them through to the next policy or our controller
next();
};
*/

166
config/routes.js Normal file
View File

@@ -0,0 +1,166 @@
/**
* Routes
*
* Sails uses a number of different strategies to route requests.
* Here they are top-to-bottom, in order of precedence.
*
* For more information on routes, check out:
* http://sailsjs.org/#documentation
*/
/**
* (1) Core middleware
*
* Middleware included with `app.use` is run first, before the router
*/
/**
* (2) Static routes
*
* This object routes static URLs to handler functions--
* In most cases, these functions are actions inside of your controllers.
* For convenience, you can also connect routes directly to views or external URLs.
*
*/
module.exports.routes = {
// By default, your root route (aka home page) points to a view
// located at `views/home/index.ejs`
//
// (This would also work if you had a file at: `/views/home.ejs`)
'/': {
view: 'home/index'
}
/*
// But what if you want your home page to display
// a signup form located at `views/user/signup.ejs`?
'/': {
view: 'user/signup'
}
// Let's say you're building an email client, like Gmail
// You might want your home route to serve an interface using custom logic.
// In this scenario, you have a custom controller `MessageController`
// with an `inbox` action.
'/': 'MessageController.inbox'
// Alternatively, you can use the more verbose syntax:
'/': {
controller: 'MessageController',
action: 'inbox'
}
// If you decided to call your action `index` instead of `inbox`,
// since the `index` action is the default, you can shortcut even further to:
'/': 'MessageController'
// Up until now, we haven't specified a specific HTTP method/verb
// The routes above will apply to ALL verbs!
// If you want to set up a route only for one in particular
// (GET, POST, PUT, DELETE, etc.), just specify the verb before the path.
// For example, if you have a `UserController` with a `signup` action,
// and somewhere else, you're serving a signup form looks like:
//
// <form action="/signup">
// <input name="username" type="text"/>
// <input name="password" type="password"/>
// <input type="submit"/>
// </form>
// You would want to define the following route to handle your form:
'post /signup': 'UserController.signup'
// What about the ever-popular "vanity URLs" aka URL slugs?
// (you might remember doing this with `mod_rewrite` in Apache)
//
// This is where you want to set up root-relative dynamic routes like:
// http://yourwebsite.com/twinkletoez
//
// NOTE:
// You'll still want to allow requests through to the static assets,
// so we need to set up this route to ignore URLs that have a trailing ".":
// (e.g. your javascript, CSS, and image files)
'get /*(^.*)': 'UserController.profile'
*/
};
/**
* (3) Action blueprints
* These routes can be disabled by setting (in `config/controllers.js`):
* `module.exports.controllers.blueprints.actions = false`
*
* All of your controllers ' actions are automatically bound to a route. For example:
* + If you have a controller, `FooController`:
* + its action `bar` is accessible at `/foo/bar`
* + its action `index` is accessible at `/foo/index`, and also `/foo`
*/
/**
* (4) Shortcut CRUD blueprints
*
* These routes can be disabled by setting (in config/controllers.js)
* `module.exports.controllers.blueprints.shortcuts = false`
*
* If you have a model, `Foo`, and a controller, `FooController`,
* you can access CRUD operations for that model at:
* /foo/find/:id? -> search lampshades using specified criteria or with id=:id
*
* /foo/create -> create a lampshade using specified values
*
* /foo/update/:id -> update the lampshade with id=:id
*
* /foo/destroy/:id -> delete lampshade with id=:id
*
*/
/**
* (5) REST blueprints
*
* These routes can be disabled by setting (in config/controllers.js)
* `module.exports.controllers.blueprints.rest = false`
*
* If you have a model, `Foo`, and a controller, `FooController`,
* you can access CRUD operations for that model at:
*
* get /foo/:id? -> search lampshades using specified criteria or with id=:id
*
* post /foo -> create a lampshade using specified values
*
* put /foo/:id -> update the lampshade with id=:id
*
* delete /foo/:id -> delete lampshade with id=:id
*
*/
/**
* (6) Static assets
*
* Flat files in your `assets` directory- (these are sometimes referred to as 'public')
* If you have an image file at `/assets/images/foo.jpg`, it will be made available
* automatically via the route: `/images/foo.jpg`
*
*/
/**
* (7) 404 (not found) handler
*
* Finally, if nothing else matched, the default 404 handler is triggered.
* See `config/404.js` to adjust your app's 404 logic.
*/

56
config/session.js Normal file
View File

@@ -0,0 +1,56 @@
/**
* Session
*
* Sails session integration leans heavily on the great work already done by Express, but also unifies
* Socket.io with the Connect session store. It uses Connect's cookie parser to normalize configuration
* differences between Express and Socket.io and hooks into Sails' middleware interpreter to allow you
* to access and auto-save to `req.session` with Socket.io the same way you would with Express.
*
* For more information on configuring the session, check out:
* http://sailsjs.org/#documentation
*/
module.exports.session = {
// Session secret is automatically generated when your new app is created
// Replace at your own risk in production-- you will invalidate the cookies of your users,
// forcing them to log in again.
secret: '7c4a87c5984e13546e5ee92cef884fc3'
// In production, uncomment the following lines to set up a shared redis session store
// that can be shared across multiple Sails.js servers
// adapter: 'redis',
//
// The following values are optional, if no options are set a redis instance running
// on localhost is expected.
// Read more about options at: https://github.com/visionmedia/connect-redis
//
// host: 'localhost',
// port: 6379,
// ttl: <redis session TTL in seconds>,
// db: 0,
// pass: <redis auth password>
// prefix: 'sess:'
// Uncomment the following lines to use your Mongo adapter as a session store
// adapter: 'mongo',
//
// host: 'localhost',
// port: 27017,
// db: 'sails',
// collection: 'sessions',
//
// Optional Values:
//
// # Note: url will override other connection settings
// url: 'mongodb://user:pass@host:port/database/collection',
//
// username: '',
// password: '',
// auto_reconnect: false,
// ssl: false,
// stringify: true
};

223
config/sockets.js Normal file
View File

@@ -0,0 +1,223 @@
/**
* Socket Configuration
*
* These configuration options provide transparent access to Sails' encapsulated
* pubsub/socket server for complete customizability.
*
* For more information on using Sails with Sockets, check out:
* http://sailsjs.org/#documentation
*/
module.exports.sockets = {
// This custom onConnect function will be run each time AFTER a new socket connects
// (To control whether a socket is allowed to connect, check out `authorization` config.)
// Keep in mind that Sails' RESTful simulation for sockets
// mixes in socket.io events for your routes and blueprints automatically.
onConnect: function(session, socket) {
// By default: do nothing
// This is a good place to subscribe a new socket to a room, inform other users that
// someone new has come online, or any other custom socket.io logic
},
// This custom onDisconnect function will be run each time a socket disconnects
onDisconnect: function(session, socket) {
// By default: do nothing
// This is a good place to broadcast a disconnect message, or any other custom socket.io logic
},
// `transports`
//
// A array of allowed transport methods which the clients will try to use.
// The flashsocket transport is disabled by default
// You can enable flashsockets by adding 'flashsocket' to this list:
transports: [
'websocket',
'htmlfile',
'xhr-polling',
'jsonp-polling'
],
// Use this option to set the datastore socket.io will use to manage rooms/sockets/subscriptions:
// default: memory
adapter: 'memory',
// Node.js (and consequently Sails.js) apps scale horizontally.
// It's a powerful, efficient approach, but it involves a tiny bit of planning.
// At scale, you'll want to be able to copy your app onto multiple Sails.js servers
// and throw them behind a load balancer.
//
// One of the big challenges of scaling an application is that these sorts of clustered
// deployments cannot share memory, since they are on physically different machines.
// On top of that, there is no guarantee that a user will "stick" with the same server between
// requests (whether HTTP or sockets), since the load balancer will route each request to the
// Sails server with the most available resources. However that means that all room/pubsub/socket
// processing and shared memory has to be offloaded to a shared, remote messaging queue (usually Redis)
//
// Luckily, Socket.io (and consequently Sails.js) apps support Redis for sockets by default.
// To enable a remote redis pubsub server:
// adapter: 'redis',
// host: '127.0.0.1',
// port: 6379,
// db: 'sails',
// pass: '<redis auth password>'
// Worth mentioning is that, if `adapter` config is `redis`,
// but host/port is left unset, Sails will try to connect to redis
// running on localhost via port 6379
// `authorization`
//
// Global authorization for Socket.IO access,
// this is called when the initial handshake is performed with the server.
//
// By default (`authorization: true`), when a socket tries to connect, Sails verifies
// that a valid cookie was sent with the upgrade request. If the cookie doesn't match
// any known user session, a new user session is created for it.
//
// However, in the case of cross-domain requests, it is possible to receive a connection
// upgrade request WITHOUT A COOKIE (for certain transports)
// In this case, there is no way to keep track of the requesting user between requests,
// since there is no identifying information to link him/her with a session.
//
// If you don't care about keeping track of your socket users between requests,
// you can bypass this cookie check by setting `authorization: false`
// which will disable the session for socket requests (req.session is still accessible
// in each request, but it will be empty, and any changes to it will not be persisted)
//
// On the other hand, if you DO need to keep track of user sessions,
// you can pass along a ?cookie query parameter to the upgrade url,
// which Sails will use in the absense of a proper cookie
// e.g. (when connection from the client):
// io.connect('http://localhost:1337?cookie=smokeybear')
//
// (Un)fortunately, the user's cookie is (should!) not accessible in client-side js.
// Using HTTP-only cookies is crucial for your app's security.
// Primarily because of this situation, as well as a handful of other advanced
// use cases, Sails allows you to override the authorization behavior
// with your own custom logic by specifying a function, e.g:
/*
authorization: function authorizeAttemptedSocketConnection(reqObj, cb) {
// Any data saved in `handshake` is available in subsequent requests
// from this as `req.socket.handshake.*`
//
// to allow the connection, call `cb(null, true)`
// to prevent the connection, call `cb(null, false)`
// to report an error, call `cb(err)`
}
*/
authorization: true,
// Match string representing the origins that are allowed to connect to the Socket.IO server
origins: '*:*',
// Should we use heartbeats to check the health of Socket.IO connections?
heartbeats: true,
// When client closes connection, the # of seconds to wait before attempting a reconnect.
// This value is sent to the client after a successful handshake.
'close timeout': 60,
// The # of seconds between heartbeats sent from the client to the server
// This value is sent to the client after a successful handshake.
'heartbeat timeout': 60,
// The max # of seconds to wait for an expcted heartbeat before declaring the pipe broken
// This number should be less than the `heartbeat timeout`
'heartbeat interval': 25,
// The maximum duration of one HTTP poll-
// if it exceeds this limit it will be closed.
'polling duration': 20,
// Enable the flash policy server if the flashsocket transport is enabled
// 'flash policy server': true,
// By default the Socket.IO client will check port 10843 on your server
// to see if flashsocket connections are allowed.
// The Adobe Flash Player normally uses 843 as default port,
// but Socket.io defaults to a non root port (10843) by default
//
// If you are using a hosting provider that doesn't allow you to start servers
// other than on port 80 or the provided port, and you still want to support flashsockets
// you can set the `flash policy port` to -1
'flash policy port': 10843,
// Used by the HTTP transports. The Socket.IO server buffers HTTP request bodies up to this limit.
// This limit is not applied to websocket or flashsockets.
'destroy buffer size': '10E7',
// Do we need to destroy non-socket.io upgrade requests?
'destroy upgrade': true,
// Should Sails/Socket.io serve the `socket.io.js` client?
// (as well as WebSocketMain.swf for Flash sockets, etc.)
'browser client': true,
// Cache the Socket.IO file generation in the memory of the process
// to speed up the serving of the static files.
'browser client cache': true,
// Does Socket.IO need to send a minified build of the static client script?
'browser client minification': false,
// Does Socket.IO need to send an ETag header for the static requests?
'browser client etag': false,
// Adds a Cache-Control: private, x-gzip-ok="", max-age=31536000 header to static requests,
// but only if the file is requested with a version number like /socket.io/socket.io.v0.9.9.js.
'browser client expires': 315360000,
// Does Socket.IO need to GZIP the static files?
// This process is only done once and the computed output is stored in memory.
// So we don't have to spawn a gzip process for each request.
'browser client gzip': false,
// Optional override function to serve all static files,
// including socket.io.js et al.
// Of the form :: function (req, res) { /* serve files */ }
'browser client handler': false,
// Meant to be used when running socket.io behind a proxy.
// Should be set to true when you want the location handshake to match the protocol of the origin.
// This fixes issues with terminating the SSL in front of Node
// and forcing location to think it's wss instead of ws.
'match origin protocol': false,
// Direct access to the socket.io MQ store config
// The 'adapter' property is the preferred method
// (`undefined` indicates that Sails should defer to the 'adapter' config)
store: undefined,
// A logger instance that is used to output log information.
// (`undefined` indicates deferment to the main Sails log config)
logger: undefined,
// The amount of detail that the server should output to the logger.
// (`undefined` indicates deferment to the main Sails log config)
'log level': undefined,
// Whether to color the log type when output to the logger.
// (`undefined` indicates deferment to the main Sails log config)
'log colors': undefined,
// A Static instance that is used to serve the socket.io client and its dependencies.
// (`undefined` indicates use default)
'static': undefined,
// The entry point where Socket.IO starts looking for incoming connections.
// This should be the same between the client and the server.
resource: '/socket.io'
};

72
config/views.js Normal file
View File

@@ -0,0 +1,72 @@
/**
* Views
*
* Server-sent views are a classic and effective way to get your app up and running.
* Views are normally served from controllers. Below, you can configure your
* templating language/framework of choice and configure Sails' layout support.
*
* For more information on views and layouts, check out:
* http://sailsjs.org/#documentation
*/
module.exports.views = {
// View engine (aka template language)
// to use for your app's *server-side* views
//
// Sails+Express supports all view engines which implement
// TJ Holowaychuk's `consolidate.js`, including, but not limited to:
//
// ejs, jade, handlebars, mustache
// underscore, hogan, haml, haml-coffee, dust
// atpl, eco, ect, jazz, jqtpl, JUST, liquor, QEJS,
// swig, templayed, toffee, walrus, & whiskers
// For more options, check out the docs:
// https://github.com/balderdashy/sails-wiki/blob/0.9/config.views.md#engine
engine: 'ejs',
// Layouts are simply top-level HTML templates you can use as wrappers
// for your server-side views. If you're using ejs or jade, you can take advantage of
// Sails' built-in `layout` support.
//
// When using a layout, when one of your views is served, it is injected into
// the `body` partial defined in the layout. This lets you reuse header
// and footer logic between views.
//
// NOTE: Layout support is only implemented for the `ejs` view engine!
// For most other engines, it is not necessary, since they implement
// partials/layouts themselves. In those cases, this config will be silently
// ignored.
//
// The `layout` setting may be set to one of:
//
// If `true`, Sails will look for the default, located at `views/layout.ejs`
// If `false`, layouts will be disabled.
// Otherwise, if a string is specified, it will be interpreted as the relative path
// to your layout from `views/` folder.
// (the file extension, e.g. ".ejs", should be omitted)
//
layout: 'layout'
// Using Multiple Layouts with EJS
//
// If you're using the default engine, `ejs`, Sails supports the use of multiple
// `layout` files. To take advantage of this, before rendering a view, override
// the `layout` local in your controller by setting `res.locals.layout`.
// (this is handy if you parts of your app's UI look completely different from each other)
//
// e.g. your default might be
// layout: 'layouts/public'
//
// But you might override that in some of your controllers with:
// layout: 'layouts/internal'
};

21
package.json Normal file
View File

@@ -0,0 +1,21 @@
{
"name": "lunch",
"private": true,
"version": "0.0.0",
"description": "a Sails application",
"dependencies": {
"sails": "0.9.9",
"grunt": "0.4.1",
"sails-disk": "~0.9.0",
"ejs": "0.8.4",
"optimist": "0.3.4"
},
"scripts": {
"start": "node app.js",
"debug": "node debug app.js"
},
"main": "app.js",
"repository": "",
"author": "",
"license": ""
}

76
views/403.ejs Normal file
View File

@@ -0,0 +1,76 @@
<!DOCTYPE html>
<!--
444444444 000000000 333333333333333
4::::::::4 00:::::::::00 3:::::::::::::::33
4:::::::::4 00:::::::::::::00 3::::::33333::::::3
4::::44::::4 0:::::::000:::::::03333333 3:::::3
4::::4 4::::4 0::::::0 0::::::0 3:::::3
4::::4 4::::4 0:::::0 0:::::0 3:::::3
4::::4 4::::4 0:::::0 0:::::0 33333333:::::3
4::::444444::::4440:::::0 000 0:::::0 3:::::::::::3
4::::::::::::::::40:::::0 000 0:::::0 33333333:::::3
4444444444:::::4440:::::0 0:::::0 3:::::3
4::::4 0:::::0 0:::::0 3:::::3
4::::4 0::::::0 0::::::0 3:::::3
4::::4 0:::::::000:::::::03333333 3:::::3
44::::::44 00:::::::::::::00 3::::::33333::::::3
4::::::::4 00:::::::::00 3:::::::::::::::33
4444444444 000000000 333333333333333
This is the default "403: Forbidden" page.
User agents that don't "Accept" HTML will see a JSON version instead.
You can customize the control logic for your needs in `config/403.js`
You can trigger this response from one of your controllers or policies with:
`return res.forbidden( msg );`
(where `msg` is an optional error message to include in the response)
-->
<html>
<head>
<title>Forbidden</title>
<link href='http://sailsjs.org/styles/fonts.css' rel='stylesheet'/>
<style>
/* Styles included inline since you'll probably be deleting or replacing this page anyway */
html,body{text-align:left;font-size:1em}html,body,img,form,textarea,input,fieldset,div,p,div,ul,li,ol,dl,dt,dd,h1,h2,h3,h4,h5,h6,pre,code{margin:0;padding:0}ul,li{list-style:none}img{display:block}a img{border:0}a{text-decoration:none;font-weight:normal;font-family:inherit}*:active,*:focus{outline:0;-moz-outline-style:none}h1,h2,h3,h4,h5,h6,h7{font-weight:normal;font-size:1em}.clearfix:after{clear:both;content:".";display:block;font-size:0;height:0;line-height:0;visibility:hidden}.page .ocean{background:url('http://sailsjs.com/images/waves.png') #0c8da0 no-repeat center 0;height:315px}.page .ocean img{margin-right:auto;margin-left:auto}.page .waves{display:block;padding-top:25px;margin-right:auto;margin-left:auto}.page .main{display:block;margin-top:90px}.page .logo{width:150px;margin-top:3.5em;margin-left:auto;margin-right:auto}.page .fishy{display:block;padding-top:100px}.page .help{padding-top:2em}.page h1{font-family:"Open Sans","Myriad Pro",Arial,sans-serif;font-weight:bold;font-size:1.7em;color:#001c20;text-align:center}.page h2{font-family:"Open Sans","Myriad Pro",Arial,sans-serif;font-weight:300;font-size:1.5em;color:#001c20;text-align:center}.page p{font-family:"Open Sans","Myriad Pro",Arial,sans-serif;font-size:1.25em;color:#001c20;text-align:center}.page a{color:#118798}.page a:hover{color:#b1eef7}
</style>
</head>
<body>
<div class="page">
<div class="ocean">
<img class="fishy" src="http://sailsjs.com/images/image_devInTub.png">
</div>
<div class="main">
<h1>
Forbidden
</h1>
<h2>
<% if (typeof message !== 'undefined') { %>
<%= message %>
<% } else { %>
You don't have permission to see the page you're trying to reach.
<% } %>
</h2>
<p class="help">
<a href="http://en.wikipedia.org/wiki/HTTP_403">Why</a> might this be happening?
</p>
</div>
<div class="logo">
<a href="http://sailsjs.org">
<img src="http://sailsjs.org/images/logo.png">
</a>
</div>
</div>
</body>
</html>

72
views/404.ejs Normal file
View File

@@ -0,0 +1,72 @@
<!DOCTYPE html>
<!--
444444444 000000000 444444444
4::::::::4 00:::::::::00 4::::::::4
4:::::::::4 00:::::::::::::00 4:::::::::4
4::::44::::4 0:::::::000:::::::0 4::::44::::4
4::::4 4::::4 0::::::0 0::::::0 4::::4 4::::4
4::::4 4::::4 0:::::0 0:::::0 4::::4 4::::4
4::::4 4::::4 0:::::0 0:::::0 4::::4 4::::4
4::::444444::::4440:::::0 000 0:::::04::::444444::::444
4::::::::::::::::40:::::0 000 0:::::04::::::::::::::::4
4444444444:::::4440:::::0 0:::::04444444444:::::444
4::::4 0:::::0 0:::::0 4::::4
4::::4 0::::::0 0::::::0 4::::4
4::::4 0:::::::000:::::::0 4::::4
44::::::44 00:::::::::::::00 44::::::44
4::::::::4 00:::::::::00 4::::::::4
4444444444 000000000 4444444444
This is the default "404: Not Found" page.
User agents that don't "Accept" HTML will see a JSON version instead.
You can customize the control logic for your needs in `config/404.js`
Sails considers a request to be in a "404: Not Found" state when a user
requests a URL which doesn't match any of your app's routes or blueprints.
You can also trigger this response from one of your controllers or policies with:
`return res.notFound();`
-->
<html>
<head>
<title>Page Not Found</title>
<link href='http://sailsjs.org/styles/fonts.css' rel='stylesheet'/>
<style>
/* Styles included inline since you'll probably be deleting this page anyway */
html,body{text-align:left;font-size:1em}html,body,img,form,textarea,input,fieldset,div,p,div,ul,li,ol,dl,dt,dd,h1,h2,h3,h4,h5,h6,pre,code{margin:0;padding:0}ul,li{list-style:none}img{display:block}a img{border:0}a{text-decoration:none;font-weight:normal;font-family:inherit}*:active,*:focus{outline:0;-moz-outline-style:none}h1,h2,h3,h4,h5,h6,h7{font-weight:normal;font-size:1em}.clearfix:after{clear:both;content:".";display:block;font-size:0;height:0;line-height:0;visibility:hidden}.fourohfour .ocean{background:url('http://sailsjs.com/images/waves.png') #0c8da0 no-repeat center 0;height:315px}.fourohfour .ocean img{margin-right:auto;margin-left:auto}.fourohfour .waves{display:block;padding-top:25px;margin-right:auto;margin-left:auto}.fourohfour .main{display:block;margin-top:90px}.fourohfour .logo{width:150px;margin-top:3.5em;margin-left:auto;margin-right:auto}.fourohfour .fishy{display:block;padding-top:27px}.fourohfour .help{padding-top:2em}.fourohfour h1{font-family:"Open Sans","Myriad Pro",Arial,sans-serif;font-weight:bold;font-size:1.7em;color:#001c20;text-align:center}.fourohfour h2{font-family:"Open Sans","Myriad Pro",Arial,sans-serif;font-weight:300;font-size:1.5em;color:#001c20;text-align:center}.fourohfour p{font-family:"Open Sans","Myriad Pro",Arial,sans-serif;font-size:1.25em;color:#001c20;text-align:center}.fourohfour a{color:#118798}.fourohfour a:hover{color:#b1eef7}
</style>
</head>
<body>
<div class="fourohfour">
<div class="ocean">
<img class="fishy" src="http://sailsjs.org/images/fishy4.png">
</div>
<div class="main">
<h1>
Something's fishy here.
</h1>
<h2>
The page you were trying to reach doesn't exist.
</h2>
<p class="help">
<a href="http://en.wikipedia.org/wiki/HTTP_404">Why</a> might this be happening?
</p>
</div>
<div class="logo">
<a href="http://sailsjs.org">
<img src="http://sailsjs.org/images/logo.png">
</a>
</div>
</div>
</body>
</html>

86
views/500.ejs Normal file

File diff suppressed because one or more lines are too long

93
views/home/index.ejs Normal file
View File

@@ -0,0 +1,93 @@
<!-- Default home page -->
<link type="text/css" href='http://sailsjs.org/styles/fonts.css' rel='stylesheet'/>
<style>
/* Styles included inline since you'll probably be deleting this page anyway */
html,body{text-align:left;font-size:1em}html,body,img,form,textarea,input,fieldset,div,p,div,ul,li,ol,dl,dt,dd,h1,h2,h3,h4,h5,h6,pre,code{margin:0;padding:0}ul,li{list-style:none}img{display:block}a img{border:0}a{text-decoration:none;font-weight:normal;font-family:inherit}*:active,*:focus{outline:0;-moz-outline-style:none}h1,h2,h3,h4,h5,h6{font-weight:normal}div.clear{clear:both}.clearfix:after{clear:both;content:".";display:block;font-size:0;height:0;line-height:0;visibility:hidden}body{font-family:"Open Sans",Arial,sans-serif;font-weight:300;font-size:15px}h1{color:#0c8da0;font-size:2em;font-weight:300}h2{font-size:1.5em;font-weight:300;margin-top:4%}h3{font-size:1.25em;font-weight:300;font-style:italic;margin-bottom:5px}h4{color:#0c8da0;font-weight:300;font-size:1.5em}span{font-weight:700}ul{margin-top:5%;margin-bottom:5%}a{text-decoration:none;color:inherit}p{margin-bottom:7px;font-size:.9em}.container{max-width:997px;margin:0 auto;padding:0 4%}.sprite{font-weight:normal;background:url(http://sailsjs.org/images/newapp.sprite.png) no-repeat}.top-bar{position:relative;padding-top:10px;background-color:#001c20;height:55px}.main{float:left;max-width:610px;height:555px;margin-top:50px}.steps{height:250px}.getting-started p{ margin-bottom: 30px; line-height: 26px; }.getting-started div{float:left;width:540px}.getting-started li{clear:both;height:60px;margin-top:20px;margin-bottom:20px}.getting-started .sprite{margin-left:10px;padding-left:60px;height:42px;width:0}.getting-started .one{background-position:0 0}.getting-started .two{background-position:0 -42px}.getting-started .three{background-position:0 -83px}.delete{margin-top:5%;height:52px;background:#e3f0f1;border:1px solid #118798;color:#118798;clear:both}.delete .sprite{margin-top:10px;margin-bottom:10px;margin-left:9%;padding-left:42px;padding-top:7px;height:25px;background-position:0 -126px}.delete a{color:#0c8da0;font-weight:bold;padding-left:1%}.side-bar{max-width:327px;height:555px;float:left;border-left:1px solid #0c8da0;margin-left:25px;margin-top:50px;padding-left:25px}.side-bar ul{margin-bottom:10%}.side-bar ul li{margin-top:5px;margin-bottom:.25em}.side-bar ul li a{margin-bottom:.25em}.side-bar .sprite{padding-left:25px}.side-bar .single_page{background-position:0 -199px}.side-bar .traditional{background-position:0 -219px}.side-bar .realtime{background-position:0 -179px}.side-bar .api{background-position:0 -158px}.boxy{font-family:Courier,"Courier New",sans-serif;background-color:#e4edec;border:1px solid #d0d6d6;padding-left:5px;padding-right:5px;padding-top:2px;padding-bottom:2px;font-weight:100}.sixteen{margin-right:10px}.nineteen{margin-right:7px}
.main { width: 100%; }
body { min-width: 925px; }
</style>
<!--[if IE 7]>
<style>
.getting-started li{overflow:visible;clear:both}.delete{width:690px}
</style>
<![endif]-->
<div class="top-bar">
<div class="container clearfix">
<img class="logo" src="http://sailsjs.org/images/sails-logo.jpg" />
</div>
</div>
<div class="container clearfix">
<div class="main">
<h1 id="main-title"><%= __('Welcome') %></h1>
<h2>Getting started</h2>
<p>Don't worry, we've got your back.</p>
<div class="steps">
<ul class="getting-started">
<li>
<div class="sprite one"></div>
<div>
<h3>Get your API going.</h3>
<p>
Run <span class="boxy">sails generate foo</span>. This will create a model <span class="boxy">Foo</span> and controller <span class="boxy">FooController</span>
</p>
</div>
</li>
<li>
<div class="sprite two"></div>
<div>
<h3>
Lift your app.
</h3>
<p>
Run <span class="boxy">sails lift</span> to start up your app. If you visit <span class="boxy">http://localhost:1337/foo</span> in your browser, you'll see a socket.io-compatible REST API was generated for your 'Foo' model.
</p>
</div>
</li>
<li>
<div class="sprite three"></div>
<div>
<h3>
Dive in and start building.
</h3>
<p>From here, you can modify your models, create custom controller methods as Express middleware, and create custom routes (routes are set up in <span class="boxy">config/routes.js</span>). Visit <a href="http://sailsjs.org">the Sails website</a> for more information on next steps.</p>
</div>
</li>
</ul>
</div>
<div class="delete">
<div class="sprite">You're looking at: <span class="boxy">views/home/index.ejs</span></div>
</div>
</div>
<div class="side-bar">
<h4 id="new">
New to Sails?
</h4>
<ul>
<li>
<a href="http://sailsjs.org">Visit sailsjs.org</a>
</li>
<li>
<a href="http://sailsjs.org/#!documentation">Documentation</a>
</li>
</ul>
<h4>
Next Steps
</h4>
<ul>
<li class="sprite single_page">
<a target="_blank" href="http://sailsjs.org">Build a single page app</a>
</li>
<li class="sprite traditional">
<a target="_blank" href="http://sailsjs.org">Build a traditional webapp</a>
</li>
<li class="sprite realtime">
<a target="_blank" href="http://sailsjs.org">Build a realtime app</a>
</li>
<li class="sprite api">
<a target="_blank" href="http://sailsjs.org">Build an API</a>
</li>
</ul>
</div>
</div>

96
views/layout.ejs Normal file
View File

@@ -0,0 +1,96 @@
<!DOCTYPE html>
<html>
<head>
<!--
For demonstration purposes, the `title` is dynamically set here based on
your `sails.config.appName` to show that you can inject data into layouts
exactly the same way as with your other view templates.
-->
<title><%- title %></title>
<!-- Viewport mobile tag for sensible mobile support -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<!--
Stylesheets
========================
You can link any CSS, LESS, or image files in your `assets` directory
as you would normally: using <link/> tags.
-->
<!--
Support for LESS included by default
================================================
LESS files are automatically compiled when they change using the Gruntfile
located in the top-level of this Sails app. If you run into issues with styles
not reloading, check the Sails log output in your console for LESS compilation errors.
If necessary, shut down and then lift your app again.
-->
</head>
<body>
<!-- Include the view file for the current controller/route -->
<%- body %>
<!--
Client-side Javascript
========================
You can import any js files in your `assets` directory as you would
normally: using <script></script> tags.
Here's an example of importing a few dependencies, in order:
-->
<!-- Bring in the socket.io client -->
<script type="text/javascript" src="/js/socket.io.js"></script>
<!-- then beef it up with some convenience logic for talking to Sails.js -->
<script type="text/javascript" src="/js/sails.io.js"></script>
<!-- listen on socket.io for incoming messages -->
<script type="text/javascript" src="/js/app.js"></script>
<!-- Your scripts here? -->
<!-- Your scripts here? -->
<!-- Your scripts here? -->
<!--
Looking for client-side CoffeeScript or TypeScript?
================================================
CoffeeScript and TypeScript precompilation are not installed by default,
but if you'd like to mix those features in, it is straightforward to
`npm install` the relevant grunt community modules and modify your Gruntfile
to use them.
-->
<!--
Another way: The Asset Linker
========================
Sails supports a Grunt-based asset linker, to automatically inject
<link> and <script> tags, as well any client-side templates you're using
into your HTML layouts and views, respecting dependencies.
You created this Sails app with the asset linker disabled.
If you change your mind, check out the docs on the subject:
http://sailsjs.org/#!documentation/asset-management
-->
</body>
</html>