standardify

pull/19/head
unknown 2018-11-07 15:06:36 -05:00
parent 7972729f9e
commit f3d3d8a5a8
58 changed files with 482 additions and 549 deletions

View File

@ -11,15 +11,15 @@ module.exports = {
emailExists: async function (req, res) { emailExists: async function (req, res) {
const user = await User.findOne({ const user = await User.findOne({
email: req.param('email') email: req.param('email')
}); })
if (!user) { if (!user) {
return res.status(404).json({ return res.status(404).json({
error: 'user does not exist' error: 'user does not exist'
}); })
} else { } else {
return res.json({ return res.json({
status: 'ok' status: 'ok'
}); })
} }
}, },
/** /**
@ -28,15 +28,15 @@ module.exports = {
emailAvailable: async function (req, res) { emailAvailable: async function (req, res) {
const user = await User.findOne({ const user = await User.findOne({
email: req.param('email') email: req.param('email')
}); })
if (user) { if (user) {
return res.status(401).json({ return res.status(401).json({
error: 'that email address is not available' error: 'that email address is not available'
}); })
} else { } else {
return res.json({ return res.json({
status: 'ok' status: 'ok'
}); })
} }
}, },
@ -55,15 +55,15 @@ module.exports = {
* @param {Object} res * @param {Object} res
*/ */
logout: function (req, res) { logout: function (req, res) {
req.logout(); req.logout()
delete req.user; delete req.user
delete req.session.passport; delete req.session.passport
req.session.authenticated = false; req.session.authenticated = false
if (!req.isSocket) { if (!req.isSocket) {
res.redirect(req.query.next || '/'); res.redirect(req.query.next || '/')
} else { } else {
res.ok(); res.ok()
} }
}, },
@ -74,8 +74,8 @@ module.exports = {
* @param {Object} res * @param {Object} res
*/ */
provider: async function (req, res) { provider: async function (req, res) {
const passportHelper = await sails.helpers.passport(); const passportHelper = await sails.helpers.passport()
passportHelper.endpoint(req, res); passportHelper.endpoint(req, res)
}, },
/** /**
@ -95,51 +95,51 @@ module.exports = {
* @param {Object} res * @param {Object} res
*/ */
callback: async function (req, res) { callback: async function (req, res) {
const action = req.param('action'); const action = req.param('action')
const passportHelper = await sails.helpers.passport(); const passportHelper = await sails.helpers.passport()
function negotiateError (err) { function negotiateError (err) {
if (action === 'register') { if (action === 'register') {
res.redirect('/register'); res.redirect('/register')
} else if (action === 'login') { } else if (action === 'login') {
res.redirect('/login'); res.redirect('/login')
} else if (action === 'disconnect') { } else if (action === 'disconnect') {
res.redirect('back'); res.redirect('back')
} else { } else {
// make sure the server always returns a response to the client // make sure the server always returns a response to the client
// i.e passport-local bad username/email or password // i.e passport-local bad username/email or password
res.status(401).json({ res.status(401).json({
'error': err.toString() 'error': err.toString()
}); })
} }
} }
passportHelper.callback(req, res, (err, user, info, status) => { passportHelper.callback(req, res, (err, user, info, status) => {
if (err || !user) { if (err || !user) {
sails.log.warn(user, err, info, status); sails.log.warn(user, err, info, status)
if (!err && info) { if (!err && info) {
return negotiateError(info); return negotiateError(info)
} }
return negotiateError(err); return negotiateError(err)
} }
req.login(user, (err) => { req.login(user, (err) => {
if (err) { if (err) {
sails.log.warn(err); sails.log.warn(err)
return negotiateError(err); return negotiateError(err)
} }
req.session.authenticated = true; req.session.authenticated = true
// redirect if there is a 'next' param // redirect if there is a 'next' param
if (req.query.next) { if (req.query.next) {
res.status(302).set('Location', req.query.next); res.status(302).set('Location', req.query.next)
} }
sails.log.info('user', user, 'authenticated successfully'); sails.log.info('user', user, 'authenticated successfully')
return res.json(user); return res.json(user)
}); })
}); })
}, },
/** /**
@ -149,7 +149,7 @@ module.exports = {
* @param {Object} res * @param {Object} res
*/ */
disconnect: async function (req, res) { disconnect: async function (req, res) {
const passportHelper = await sails.helpers.passport(); const passportHelper = await sails.helpers.passport()
passportHelper.disconnect(req, res); passportHelper.disconnect(req, res)
} }
}; }

View File

@ -8,48 +8,48 @@
module.exports = { module.exports = {
publish: async function (req, res) { publish: async function (req, res) {
try { try {
const body = req.body; const body = req.body
const host = req.hostname; const host = req.hostname
let result; let result
if (!host) { throw new Error('Missing hostname'); } if (!host) { throw new Error('Missing hostname') }
if (!body) {throw new Error('Missing body'); } if (!body) { throw new Error('Missing body') }
const bookExists = await Book.findOne(body); const bookExists = await Book.findOne(body)
if (bookExists) { if (bookExists) {
throw new Error('Version already exists'); throw new Error('Version already exists')
} else { } else {
result = await Book.create(body); result = await Book.create(body)
} }
return res.json({ return res.json({
...result ...result
}); })
} catch (e) { } catch (e) {
return res.status(400).json({ return res.status(400).json({
error: e.message error: e.message
}); })
} }
}, },
list: async function (req, res) { list: async function (req, res) {
try { try {
const body = req.allParams(); const body = req.allParams()
if (!body) { throw new Error('Missing parameters'); } if (!body) { throw new Error('Missing parameters') }
const books = await Book.find(body); const books = await Book.find(body)
if (!books.length) { if (!books.length) {
return res.status(404).json({ return res.status(404).json({
error: 'No books matching those parameters were found.' error: 'No books matching those parameters were found.'
}); })
} }
return res.json(books); return res.json(books)
} catch (e) { } catch (e) {
return res.status(500).json({ return res.status(500).json({
error: e.message error: e.message
}); })
} }
} }
}; }

View File

@ -3,6 +3,6 @@ module.exports = {
res.view('pages/temp', { res.view('pages/temp', {
email: req.user.email email: req.user.email
}); })
} }
}; }

View File

@ -10,31 +10,31 @@ module.exports = {
* @override * @override
*/ */
create: async function (req, res, next) { create: async function (req, res, next) {
const passportHelper = await sails.helpers.passport(); const passportHelper = await sails.helpers.passport()
passportHelper.protocols.local.register(req.body, (err, user) => { passportHelper.protocols.local.register(req.body, (err, user) => {
if (err) { if (err) {
return res.status(500).json({ return res.status(500).json({
error: err.toString() }); error: err.toString() })
} }
res.json(user); res.json(user)
}); })
}, },
update: async function (req, res, next) { update: async function (req, res, next) {
const passportHelper = await sails.helpers.passport(); const passportHelper = await sails.helpers.passport()
passportHelper.protocols.local.update(req.body, (err, user) => { passportHelper.protocols.local.update(req.body, (err, user) => {
if (err) { if (err) {
return res.status(500).json({ return res.status(500).json({
error: err.toString() error: err.toString()
}); })
} }
res.json(user); res.json(user)
}); })
}, },
me: function (req, res) { me: function (req, res) {
res.json(req.user); res.json(req.user)
} }
}; }

View File

@ -1,7 +1,7 @@
// api/helpers/passport.js // api/helpers/passport.js
// from https://github.com/trailsjs/sails-auth/blob/master/api/services/passport.js // from https://github.com/trailsjs/sails-auth/blob/master/api/services/passport.js
const url = require('url'); const url = require('url')
module.exports = { module.exports = {
friendlyName: 'Load PassportHelper', friendlyName: 'Load PassportHelper',
@ -14,158 +14,158 @@ module.exports = {
} }
}, },
fn: async function (inputs, exits) { fn: async function (inputs, exits) {
return exits.success(new PassportHelper()); return exits.success(new PassportHelper())
} }
}; }
const passport = require('passport'); const passport = require('passport')
passport.serializeUser((user, next) => { passport.serializeUser((user, next) => {
next(null, user.id); next(null, user.id)
}); })
passport.deserializeUser((id, next) => { passport.deserializeUser((id, next) => {
return User.findOne({id: id}) return User.findOne({id: id})
.then((user) => { .then((user) => {
next(null, user); next(null, user)
return user; return user
}).catch(next); }).catch(next)
}); })
function PassportHelper () { function PassportHelper () {
this.protocols = sails.config.protocols; this.protocols = sails.config.protocols
this.loadStrategies = function () { this.loadStrategies = function () {
const strategies = sails.config.passport; const strategies = sails.config.passport
for (const key in strategies) { for (const key in strategies) {
let options = {passReqToCallback: true}; let options = {passReqToCallback: true}
let Strategy = strategies[key].strategy; let Strategy = strategies[key].strategy
if (key === 'local') { if (key === 'local') {
_.extend(options, { _.extend(options, {
usernameField: 'identifier' usernameField: 'identifier'
}); })
passport.use(new Strategy(options, this.protocols.local.login)); passport.use(new Strategy(options, this.protocols.local.login))
} else { } else {
const protocol = strategies[key].protocol; const protocol = strategies[key].protocol
const callbackURL = strategies[key].callback; const callbackURL = strategies[key].callback
let baseURL = ''; let baseURL = ''
if (sails.config.appUrl && sails.config.appUrl !== null) { if (sails.config.appUrl && sails.config.appUrl !== null) {
baseURL = sails.config.appUrl; baseURL = sails.config.appUrl
} else { } else {
sails.log.warn('Please add \'appUrl\' to configuration'); sails.log.warn('Please add \'appUrl\' to configuration')
baseURL = sails.getBaseurl(); baseURL = sails.getBaseurl()
} }
switch (protocol) { switch (protocol) {
case 'oauth2': case 'oauth2':
options.callbackURL = url.resolve(baseURL, callbackURL); options.callbackURL = url.resolve(baseURL, callbackURL)
break; break
// other protocols (openid, etc can go here) // other protocols (openid, etc can go here)
} }
_.extend(options, strategies[key].options); _.extend(options, strategies[key].options)
passport.use(new Strategy(options, this.protocols[protocol].login)); passport.use(new Strategy(options, this.protocols[protocol].login))
}
} }
} }
};
this.endpoint = function (req, res) { this.endpoint = function (req, res) {
const strategies = sails.config.passport; const strategies = sails.config.passport
const provider = req.param('provider'); const provider = req.param('provider')
if (!_.has(strategies, provider)) {return res.redirect('/login');} if (!_.has(strategies, provider)) { return res.redirect('/login') }
passport.authenticate(provider, {})(req, res, req.next); passport.authenticate(provider, {})(req, res, req.next)
}; }
// a callback helper to split by req // a callback helper to split by req
this.callback = function (req, res, next) { this.callback = function (req, res, next) {
var provider = req.param('provider', 'local'); var provider = req.param('provider', 'local')
var action = req.param('action'); var action = req.param('action')
if (provider === 'local' && action !== undefined) { if (provider === 'local' && action !== undefined) {
if (action === 'register' && !req.user) { if (action === 'register' && !req.user) {
this.protocols.local.register(req, res, next); this.protocols.local.register(req, res, next)
} else if (action === 'connect' && req.user) { } else if (action === 'connect' && req.user) {
this.protocols.local.connect(req, res, next); this.protocols.local.connect(req, res, next)
} else if (action === 'disconnect' && req.user) { } else if (action === 'disconnect' && req.user) {
this.protocols.local.disconnect(req, res, next); this.protocols.local.disconnect(req, res, next)
} else { } else {
return next(new Error('Invalid action')); return next(new Error('Invalid action'))
} }
} else { } else {
if (action === 'disconnect' && req.user) { if (action === 'disconnect' && req.user) {
this.disconnect(req, res, next); this.disconnect(req, res, next)
} else { } else {
passport.authenticate(provider, next)(req, res, req.next); passport.authenticate(provider, next)(req, res, req.next)
}
} }
} }
};
this.connect = async function (req, q, profile, next) { this.connect = async function (req, q, profile, next) {
let userAttrs = {}; let userAttrs = {}
let provider = profile.provider || req.param('provider'); let provider = profile.provider || req.param('provider')
req.session.tokens = q.tokens; req.session.tokens = q.tokens
q.provider = provider; q.provider = provider
if (!provider) { if (!provider) {
return next(new Error('No provider identified')); return next(new Error('No provider identified'))
} }
// if the profile object from passport has an email, use it // if the profile object from passport has an email, use it
if (profile.emails && profile.emails[0]) {userAttrs.email = profile.emails[0].value;} if (profile.emails && profile.emails[0]) { userAttrs.email = profile.emails[0].value }
if (!userAttrs.email) {return next(new Error('No email available'));} if (!userAttrs.email) { return next(new Error('No email available')) }
const pass = await Passport.findOne({ const pass = await Passport.findOne({
provider, provider,
identifier: q.identifier.toString() identifier: q.identifier.toString()
}); })
let user; let user
if (!req.user) { if (!req.user) {
if (!passport) { // new user signing up, create a new user if (!passport) { // new user signing up, create a new user
user = await User.create(userAttrs).fetch(); user = await User.create(userAttrs).fetch()
await Passport.create({ await Passport.create({
...q, ...q,
user: user.id user: user.id
}); })
return next(null, user); return next(null, user)
} else { // existing user logging in } else { // existing user logging in
if (_.has(q, 'tokens') && q.tokens !== passport.tokens) { if (_.has(q, 'tokens') && q.tokens !== passport.tokens) {
passport.tokens = q.tokens; passport.tokens = q.tokens
} }
await passport.save(); await passport.save()
user = User.findOne(passport.user); user = User.findOne(passport.user)
return next(null, user); return next(null, user)
} }
} else { // user logged in and trying to add new Passport } else { // user logged in and trying to add new Passport
if (!passport) { if (!passport) {
await Passport.create({ await Passport.create({
...q, ...q,
user: req.user.id user: req.user.id
}); })
return next(null, req.user); return next(null, req.user)
} else { // no action, user already logged in and passport exists } else { // no action, user already logged in and passport exists
return next(null, user); return next(null, user)
}
} }
} }
};
this.disconnect = async function (req, res, next) { this.disconnect = async function (req, res, next) {
try { try {
const user = req.user; const user = req.user
const provider = req.param('provider'); const provider = req.param('provider')
const pass = Passport.findOne({ const pass = Passport.findOne({
provider, provider,
user: user.id user: user.id
}); })
await Passport.destroy(pass.id); await Passport.destroy(pass.id)
next(null, user); next(null, user)
return user; return user
} catch (e) { } catch (e) {
return next(e); return next(e)
}
} }
};
this.getPassport = function () { this.getPassport = function () {
return passport; return passport
}; }
} }

View File

@ -1,16 +1,15 @@
let passportHook = sails.hooks.passport; let passportHook = sails.hooks.passport
if (!passportHook) { if (!passportHook) {
passportHook = function (sails) { passportHook = function (sails) {
return { return {
initialize: async function (cb) { initialize: async function (cb) {
const helper = await sails.helpers.passport(); const helper = await sails.helpers.passport()
helper.loadStrategies(); helper.loadStrategies()
return cb(); return cb()
}
}
} }
};
};
} }
module.exports = passportHook; module.exports = passportHook

View File

@ -20,17 +20,16 @@ module.exports = {
title: {type: 'string', required: true}, title: {type: 'string', required: true},
author: {type: 'string'}, author: {type: 'string'},
isbn: {type: 'string'}, isbn: {type: 'string'},
version: {type: 'string'}, version: {type: 'string'}
// ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗ // ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗
// ║╣ ║║║╠╩╗║╣ ║║╚═╗ // ║╣ ║║║╠╩╗║╣ ║║╚═╗
// ╚═╝╩ ╩╚═╝╚═╝═╩╝╚═╝ // ╚═╝╩ ╩╚═╝╚═╝═╩╝╚═╝
// ╔═╗╔═╗╔═╗╔═╗╔═╗╦╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ // ╔═╗╔═╗╔═╗╔═╗╔═╗╦╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
// ╠═╣╚═╗╚═╗║ ║║ ║╠═╣ ║ ║║ ║║║║╚═╗ // ╠═╣╚═╗╚═╗║ ║║ ║╠═╣ ║ ║║ ║║║║╚═╗
// ╩ ╩╚═╝╚═╝╚═╝╚═╝╩╩ ╩ ╩ ╩╚═╝╝╚╝╚═╝ // ╩ ╩╚═╝╚═╝╚═╝╚═╝╩╩ ╩ ╩ ╩╚═╝╝╚╝╚═╝
}, }
}; }

View File

@ -1,18 +1,18 @@
const bcrypt = require('bcrypt'); const bcrypt = require('bcrypt')
async function hashPassword (passport) { async function hashPassword (passport) {
try { try {
var config = sails.config.auth.bcrypt; var config = sails.config.auth.bcrypt
var salt = config.rounds; var salt = config.rounds
if (passport.password) { if (passport.password) {
const hash = await bcrypt.hash(passport.password, salt); const hash = await bcrypt.hash(passport.password, salt)
passport.password = hash; passport.password = hash
} }
return passport; return passport
} catch (e) { } catch (e) {
delete passport.password; delete passport.password
sails.log.error(e); sails.log.error(e)
throw e; throw e
} }
} }
@ -52,20 +52,20 @@ module.exports = {
* callback run before creating a Passport * callback run before creating a Passport
*/ */
beforeCreate: async function (passport, next) { beforeCreate: async function (passport, next) {
await hashPassword(passport); await hashPassword(passport)
return next(); return next()
}, },
/** /**
* callback run before updating * callback run before updating
*/ */
beforeUpdate: async function (passport, next) { beforeUpdate: async function (passport, next) {
await hashPassword(passport); await hashPassword(passport)
return next(); return next()
}, },
// methods // methods
validatePassword: async function (password, passport) { validatePassword: async function (password, passport) {
return bcrypt.compare(password, passport.password); return bcrypt.compare(password, passport.password)
} }
}; }

View File

@ -32,4 +32,4 @@ module.exports = {
} }
}; }

View File

@ -7,8 +7,8 @@
*/ */
module.exports = function (req, res, next) { module.exports = function (req, res, next) {
if (req.session.authenticated) { if (req.session.authenticated) {
return next(); return next()
} }
res.status(403).json({ error: 'You are not permitted to perform this action.' }); res.status(403).json({ error: 'You are not permitted to perform this action.' })
// res.redirect('/login') // res.redirect('/login')
}; }

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict'
import Ajax from '../lib/Ajax'; import Ajax from '../lib/Ajax'
const ACTIONS = { const ACTIONS = {
set_working: 'set_working', set_working: 'set_working',
@ -9,50 +9,50 @@ const ACTIONS = {
set_carousel: 'set_carousel', set_carousel: 'set_carousel',
set_error: 'set_error', set_error: 'set_error',
clear_error: 'clear_error' clear_error: 'clear_error'
}; }
export default ACTIONS; export default ACTIONS
export const setWorking = working => ({ export const setWorking = working => ({
type: ACTIONS.set_working, type: ACTIONS.set_working,
data: working data: working
}); })
export const setEmail = email => ({ export const setEmail = email => ({
type: ACTIONS.set_user, type: ACTIONS.set_user,
data: email data: email
}); })
export const setPassword = pass => ({ export const setPassword = pass => ({
type: ACTIONS.set_password, type: ACTIONS.set_password,
data: pass data: pass
}); })
export const setCarousel = pos => (dispatch, getState) => { export const setCarousel = pos => (dispatch, getState) => {
dispatch(clearError()); dispatch(clearError())
dispatch({ dispatch({
type: ACTIONS.set_carousel, type: ACTIONS.set_carousel,
data: pos data: pos
}); })
}; }
export const setError = data => ({ export const setError = data => ({
type: ACTIONS.set_error, type: ACTIONS.set_error,
data: data data: data
}); })
export const clearError = () => ({ export const clearError = () => ({
type: ACTIONS.clear_error type: ACTIONS.clear_error
}); })
export const setLoggedIn = (data) => (dispatch, getState) => { export const setLoggedIn = (data) => (dispatch, getState) => {
window.localStorage.setItem('roe-token', JSON.stringify(data)); window.localStorage.setItem('roe-token', JSON.stringify(data))
window.location.href = '/app'; window.location.href = '/app'
}; }
export const checkEmail = email => async (dispatch, getState) => { export const checkEmail = email => async (dispatch, getState) => {
dispatch(setWorking(true)); dispatch(setWorking(true))
dispatch(clearError()); dispatch(clearError())
if (/^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/.test(email)) { if (/^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/.test(email)) {
try { try {
const res = await Ajax.post({ const res = await Ajax.post({
@ -60,25 +60,25 @@ export const checkEmail = email => async (dispatch, getState) => {
data: { data: {
email email
} }
}); })
dispatch(setCarousel(2)); dispatch(setCarousel(2))
} catch (e) { } catch (e) {
dispatch(setError({ dispatch(setError({
type: 'email', type: 'email',
error: 'An account with that email does not exist.' error: 'An account with that email does not exist.'
})); }))
} }
} else { } else {
dispatch(setError({ dispatch(setError({
type: 'email', type: 'email',
error: 'Please enter a valid email address.' error: 'Please enter a valid email address.'
})); }))
} }
dispatch(setWorking(false)); dispatch(setWorking(false))
}; }
export const checkPassword = (email, password) => async (dispatch, getState) => { export const checkPassword = (email, password) => async (dispatch, getState) => {
dispatch(setWorking(true)); dispatch(setWorking(true))
// do email + password check // do email + password check
try { try {
@ -88,21 +88,21 @@ export const checkPassword = (email, password) => async (dispatch, getState) =>
identifier: email, identifier: email,
password password
} }
}); })
dispatch(setLoggedIn(res)); dispatch(setLoggedIn(res))
// dispatch(setWorking(false)) // dispatch(setWorking(false))
} catch (e) { } catch (e) {
dispatch(setError({ dispatch(setError({
type: 'password', type: 'password',
error: e.toString() error: e.toString()
})); }))
dispatch(setWorking(false)); dispatch(setWorking(false))
} }
}; }
export const signup = (email, password) => async (dispatch, getState) => { export const signup = (email, password) => async (dispatch, getState) => {
dispatch(setWorking(true)); dispatch(setWorking(true))
dispatch(clearError()); dispatch(clearError())
if (/^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/.test(email)) { if (/^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/.test(email)) {
try { try {
await Ajax.post({ await Ajax.post({
@ -110,26 +110,26 @@ export const signup = (email, password) => async (dispatch, getState) => {
data: { data: {
email email
} }
}); })
await Ajax.post({ await Ajax.post({
url: '/register', url: '/register',
data: { data: {
email, email,
password password
} }
}); })
dispatch(setCarousel(2)); dispatch(setCarousel(2))
} catch (e) { } catch (e) {
dispatch(setError({ dispatch(setError({
type: 'email', type: 'email',
error: e.toString() error: e.toString()
})); }))
} }
} else { } else {
dispatch(setError({ dispatch(setError({
type: 'email', type: 'email',
error: 'Please enter a valid email address.' error: 'Please enter a valid email address.'
})); }))
} }
dispatch(setWorking(false)); dispatch(setWorking(false))
}; }

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict'
import React from 'react'; import React from 'react'
const Progress = props => ( const Progress = props => (
<div className={'progress' + (props.bound ? ' bound' : '')}> <div className={'progress' + (props.bound ? ' bound' : '')}>

View File

@ -1,162 +1,162 @@
/* global XMLHttpRequest FormData */ /* global XMLHttpRequest FormData */
'use strict'; 'use strict'
let ajaxcfg = {}; let ajaxcfg = {}
class AjaxError extends Error { class AjaxError extends Error {
constructor (reason, data, xhr) { constructor (reason, data, xhr) {
super(reason); super(reason)
this.data = data; this.data = data
this.xhr = xhr; this.xhr = xhr
} }
} }
export default class Ajax { export default class Ajax {
static async get (opts) { static async get (opts) {
if (!opts) {opts = {};} if (!opts) { opts = {} }
opts.method = 'get'; opts.method = 'get'
return Ajax.ajax(opts); return Ajax.ajax(opts)
} }
static async post (opts) { static async post (opts) {
if (!opts) {opts = {};} if (!opts) { opts = {} }
opts.method = 'post'; opts.method = 'post'
return Ajax.ajax(opts); return Ajax.ajax(opts)
} }
static async put (opts) { static async put (opts) {
if (!opts) {opts = {};} if (!opts) { opts = {} }
opts.method = 'put'; opts.method = 'put'
return Ajax.ajax(opts); return Ajax.ajax(opts)
} }
static async patch (opts) { static async patch (opts) {
if (!opts) {opts = {};} if (!opts) { opts = {} }
opts.method = 'patch'; opts.method = 'patch'
return Ajax.ajax(opts); return Ajax.ajax(opts)
} }
static async delete (opts) { static async delete (opts) {
if (!opts) {opts = {};} if (!opts) { opts = {} }
opts.method = 'delete'; opts.method = 'delete'
return Ajax.ajax(opts); return Ajax.ajax(opts)
} }
static async head (opts) { static async head (opts) {
if (!opts) {opts = {};} if (!opts) { opts = {} }
opts.method = 'head'; opts.method = 'head'
return Ajax.ajax(opts); return Ajax.ajax(opts)
} }
static async options (opts) { static async options (opts) {
if (!opts) {opts = {};} if (!opts) { opts = {} }
opts.method = 'options'; opts.method = 'options'
return Ajax.ajax(opts); return Ajax.ajax(opts)
} }
static ajax (opts) { static ajax (opts) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!opts) {reject(new Error('Missing required options parameter.'));} if (!opts) { reject(new Error('Missing required options parameter.')) }
if (opts.method) { if (opts.method) {
if (!['get', 'post', 'put', 'patch', 'delete', 'head', 'options'].includes(opts.method.toLowerCase())) {reject(new Error('opts.method must be one of: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS.'));} if (!['get', 'post', 'put', 'patch', 'delete', 'head', 'options'].includes(opts.method.toLowerCase())) { reject(new Error('opts.method must be one of: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS.')) }
opts.method = opts.method.toUpperCase(); opts.method = opts.method.toUpperCase()
} }
var xhr = opts.xhr || new XMLHttpRequest(); var xhr = opts.xhr || new XMLHttpRequest()
var fd = null; var fd = null
var qs = ''; var qs = ''
if (opts.data && opts.method.toLowerCase() !== 'get') { if (opts.data && opts.method.toLowerCase() !== 'get') {
fd = new FormData(); fd = new FormData()
for (let key in opts.data) { for (let key in opts.data) {
fd.append(key, opts.data[key]); fd.append(key, opts.data[key])
} }
} else if (opts.data) { } else if (opts.data) {
qs += '?'; qs += '?'
let params = []; let params = []
for (let key in opts.data) { for (let key in opts.data) {
params.push([key, opts.data[key]].join('=')); params.push([key, opts.data[key]].join('='))
} }
qs += params.join('&'); qs += params.join('&')
} }
xhr.onload = () => { xhr.onload = () => {
if (xhr.status !== 200) {return xhr.onerror();} if (xhr.status !== 200) { return xhr.onerror() }
var data = xhr.response; var data = xhr.response
resolve({ resolve({
data, data,
xhr xhr
}); })
}; }
xhr.onerror = () => { xhr.onerror = () => {
var data = xhr.response; var data = xhr.response
// method not allowed // method not allowed
if (xhr.status === 405) { if (xhr.status === 405) {
reject(new AjaxError('405 Method Not Allowed', data, xhr)); reject(new AjaxError('405 Method Not Allowed', data, xhr))
return; return
} else if (xhr.status === 404) { } else if (xhr.status === 404) {
reject(new AjaxError('404 Not Found', data, xhr)); reject(new AjaxError('404 Not Found', data, xhr))
return; return
} }
try { try {
// if the access token is invalid, try to use the refresh token // if the access token is invalid, try to use the refresh token
var json = JSON.parse(data); var json = JSON.parse(data)
if (json.error === 'access_denied' && json.hint.includes('token') && json.hint.includes('invalid') && ajaxcfg.refresh_token) { if (json.error === 'access_denied' && json.hint.includes('token') && json.hint.includes('invalid') && ajaxcfg.refresh_token) {
return Ajax.refresh(opts); return Ajax.refresh(opts)
} else if (json.error === 'access_denied' && json.hint.includes('token') && json.hint.includes('revoked')) { } else if (json.error === 'access_denied' && json.hint.includes('token') && json.hint.includes('revoked')) {
reject(new AjaxError('token revoked', data, xhr)); reject(new AjaxError('token revoked', data, xhr))
} }
} catch (e) { } catch (e) {
reject(new AjaxError(e.toString(), data, xhr)); reject(new AjaxError(e.toString(), data, xhr))
} finally { } finally {
reject(new AjaxError(data, xhr.status, xhr)); reject(new AjaxError(data, xhr.status, xhr))
}
} }
};
xhr.open(opts.method || 'GET', opts.url + qs || window.location.href); xhr.open(opts.method || 'GET', opts.url + qs || window.location.href)
if (opts.headers) { if (opts.headers) {
for (let key in opts.headers) {xhr.setRequestHeader(key, opts.headers[key]);} for (let key in opts.headers) { xhr.setRequestHeader(key, opts.headers[key]) }
} }
if (ajaxcfg.access_token && !(opts.headers || {}).Authorization) {xhr.setRequestHeader('Authorization', 'Bearer ' + ajaxcfg.access_token);} if (ajaxcfg.access_token && !(opts.headers || {}).Authorization) { xhr.setRequestHeader('Authorization', 'Bearer ' + ajaxcfg.access_token) }
xhr.send(fd); xhr.send(fd)
}); })
} }
static refresh (opts) { static refresh (opts) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest()
var fd = new FormData(); var fd = new FormData()
const OAUTH_TOKEN_REQUEST = { const OAUTH_TOKEN_REQUEST = {
grant_type: 'refresh_token', grant_type: 'refresh_token',
refresh_token: ajaxcfg.refresh_token, refresh_token: ajaxcfg.refresh_token,
client_id: 'foxfile', client_id: 'foxfile',
client_secret: 1 client_secret: 1
}; }
for (var key in OAUTH_TOKEN_REQUEST) { for (var key in OAUTH_TOKEN_REQUEST) {
fd.append(key, OAUTH_TOKEN_REQUEST[key]); fd.append(key, OAUTH_TOKEN_REQUEST[key])
} }
// try original request // try original request
xhr.onload = () => { xhr.onload = () => {
if (xhr.status !== 200) {return xhr.onerror();} if (xhr.status !== 200) { return xhr.onerror() }
if (ajaxcfg.refresh) {ajaxcfg.refresh(xhr.response);} if (ajaxcfg.refresh) { ajaxcfg.refresh(xhr.response) }
var json = JSON.parse(xhr.response); var json = JSON.parse(xhr.response)
ajaxcfg.access_token = json.access_token; ajaxcfg.access_token = json.access_token
ajaxcfg.refresh_token = json.refresh_token; ajaxcfg.refresh_token = json.refresh_token
return Ajax.ajax(opts); return Ajax.ajax(opts)
}; }
// if this fails, dont try again // if this fails, dont try again
xhr.onerror = () => { xhr.onerror = () => {
var data = xhr.response; var data = xhr.response
reject(new AjaxError(xhr.status, data, xhr)); reject(new AjaxError(xhr.status, data, xhr))
}; }
xhr.open('POST', ajaxcfg.refresh_url); xhr.open('POST', ajaxcfg.refresh_url)
xhr.send(fd); xhr.send(fd)
}); })
} }
static setTokenData (tokens) { static setTokenData (tokens) {
if (!tokens) {throw new Error('Missing tokens.');} if (!tokens) { throw new Error('Missing tokens.') }
if (!tokens.access_token && !tokens.refresh_token && !tokens.refresh_url) {throw new Error('Missing at least one of: access_token, refresh_token, refresh_url.');} if (!tokens.access_token && !tokens.refresh_token && !tokens.refresh_url) { throw new Error('Missing at least one of: access_token, refresh_token, refresh_url.') }
if (tokens.access_token) {ajaxcfg.access_token = tokens.access_token;} if (tokens.access_token) { ajaxcfg.access_token = tokens.access_token }
if (tokens.refresh_token) {ajaxcfg.refresh_token = tokens.refresh_token;} if (tokens.refresh_token) { ajaxcfg.refresh_token = tokens.refresh_token }
if (tokens.refresh_url) {ajaxcfg.refresh_url = tokens.refresh_url;} if (tokens.refresh_url) { ajaxcfg.refresh_url = tokens.refresh_url }
return true; return true
} }
static onRefresh (func) { static onRefresh (func) {
ajaxcfg.refresh = func; ajaxcfg.refresh = func
} }
} }

View File

@ -1,9 +1,9 @@
'use strict'; 'use strict'
import Actions from '../actions/login'; import Actions from '../actions/login'
const reducer = (state = {}, action) => { const reducer = (state = {}, action) => {
const {type, data} = action; const {type, data} = action
switch (type) { switch (type) {
case Actions.set_user: case Actions.set_user:
return { return {
@ -11,40 +11,40 @@ const reducer = (state = {}, action) => {
...state.user, ...state.user,
email: data email: data
} }
}; }
case Actions.set_password: case Actions.set_password:
return { return {
user: { user: {
...state.user, ...state.user,
password: data password: data
} }
}; }
case Actions.set_carousel: case Actions.set_carousel:
return { return {
carouselPosition: data carouselPosition: data
}; }
case Actions.set_working: case Actions.set_working:
return { return {
working: data working: data
}; }
case Actions.set_error: case Actions.set_error:
switch (data.type) { switch (data.type) {
case 'email': case 'email':
return { return {
emailError: data.error emailError: data.error
}; }
case 'password': case 'password':
return { return {
passwordError: data.error passwordError: data.error
}; }
default: return {}; default: return {}
} }
case Actions.clear_error: case Actions.clear_error:
return { return {
emailError: '', emailError: '',
passwordError: '' passwordError: ''
};
} }
}; }
}
export default reducer; export default reducer

View File

@ -2,4 +2,4 @@ module.exports.auth = {
bcrypt: { bcrypt: {
rounds: 8 rounds: 8
} }
}; }

View File

@ -19,7 +19,6 @@ module.exports.blueprints = {
// actions: false, // actions: false,
/*************************************************************************** /***************************************************************************
* * * *
* Automatically expose RESTful routes for your models? * * Automatically expose RESTful routes for your models? *
@ -28,7 +27,6 @@ module.exports.blueprints = {
// rest: true, // rest: true,
/*************************************************************************** /***************************************************************************
* * * *
* Automatically expose CRUD "shortcut" routes to GET requests? * * Automatically expose CRUD "shortcut" routes to GET requests? *
@ -38,4 +36,4 @@ module.exports.blueprints = {
// shortcuts: true, // shortcuts: true,
}; }

View File

@ -9,8 +9,7 @@
* https://sailsjs.com/config/bootstrap * https://sailsjs.com/config/bootstrap
*/ */
module.exports.bootstrap = async function(done) { module.exports.bootstrap = async function (done) {
// By convention, this is a good place to set up fake data during development. // By convention, this is a good place to set up fake data during development.
// //
// For example: // For example:
@ -29,6 +28,5 @@ module.exports.bootstrap = async function(done) {
// Don't forget to trigger `done()` when this bootstrap function's logic is finished. // Don't forget to trigger `done()` when this bootstrap function's logic is finished.
// (otherwise your server will never lift, since it's waiting on the bootstrap) // (otherwise your server will never lift, since it's waiting on the bootstrap)
return done(); return done()
}
};

View File

@ -20,4 +20,4 @@ module.exports.custom = {
// stripeSecret: 'sk_test_Zzd814nldl91104qor5911gjald', // stripeSecret: 'sk_test_Zzd814nldl91104qor5911gjald',
// … // …
}; }

View File

@ -15,7 +15,6 @@
module.exports.datastores = { module.exports.datastores = {
/*************************************************************************** /***************************************************************************
* * * *
* Your app's default datastore. * * Your app's default datastore. *
@ -51,7 +50,6 @@ module.exports.datastores = {
// adapter: 'sails-mysql', // adapter: 'sails-mysql',
// url: 'mysql://user:password@host:port/database', // url: 'mysql://user:password@host:port/database',
}, }
}
};

View File

@ -367,4 +367,4 @@ module.exports = {
} }
}; }

View File

@ -238,8 +238,8 @@ module.exports = {
* * * *
***************************************************************************/ ***************************************************************************/
onlyAllowOrigins: [ onlyAllowOrigins: [
'https://ec2-18-219-76-43.us-east-2.compute.amazonaws.com', 'https://ec2-18-219-76-43.us-east-2.compute.amazonaws.com'
], ]
/*************************************************************************** /***************************************************************************
* * * *
@ -366,4 +366,4 @@ module.exports = {
} }
}; }

View File

@ -47,6 +47,6 @@ module.exports.globals = {
* * * *
****************************************************************************/ ****************************************************************************/
sails: true, sails: true
}; }

View File

@ -9,14 +9,14 @@
* https://sailsjs.com/config/http * https://sailsjs.com/config/http
*/ */
const rateLimit = require('express-rate-limit'); const rateLimit = require('express-rate-limit')
const rateLimiter = rateLimit({ const rateLimiter = rateLimit({
windowMs: 10 * 60 * 1000, // 10 minutes windowMs: 10 * 60 * 1000, // 10 minutes
max: 100, // limit each IP to 100 requests per windowMs max: 100, // limit each IP to 100 requests per windowMs
skip (req, res) { skip (req, res) {
return !req.path.startsWith('/api'); return !req.path.startsWith('/api')
} }
}); })
module.exports.http = { module.exports.http = {
@ -53,7 +53,7 @@ module.exports.http = {
], ],
rateLimit: rateLimiter, rateLimit: rateLimiter,
passportInit: require('passport').initialize(), passportInit: require('passport').initialize(),
passportSession: require('passport').session(), passportSession: require('passport').session()
/*************************************************************************** /***************************************************************************
* * * *
@ -69,4 +69,4 @@ module.exports.http = {
// return middlewareFn; // return middlewareFn;
// })(), // })(),
} }
}; }

View File

@ -20,7 +20,7 @@ module.exports.i18n = {
* * * *
***************************************************************************/ ***************************************************************************/
locales: ['en', 'es', 'fr', 'de'], locales: ['en', 'es', 'fr', 'de']
/**************************************************************************** /****************************************************************************
* * * *
@ -42,4 +42,4 @@ module.exports.i18n = {
// localesDirectory: 'config/locales' // localesDirectory: 'config/locales'
}; }

View File

@ -26,4 +26,4 @@ module.exports.log = {
// level: 'info' // level: 'info'
}; }

View File

@ -15,7 +15,6 @@
module.exports.models = { module.exports.models = {
/*************************************************************************** /***************************************************************************
* * * *
* Whether the `.create()` and `.update()` model methods should ignore * * Whether the `.create()` and `.update()` model methods should ignore *
@ -37,7 +36,6 @@ module.exports.models = {
// schema: true, // schema: true,
/*************************************************************************** /***************************************************************************
* * * *
* How and whether Sails will attempt to automatically rebuild the * * How and whether Sails will attempt to automatically rebuild the *
@ -55,7 +53,6 @@ module.exports.models = {
// migrate: 'alter', // migrate: 'alter',
/*************************************************************************** /***************************************************************************
* * * *
* Base attributes that are included in all of your models by default. * * Base attributes that are included in all of your models by default. *
@ -69,10 +66,10 @@ module.exports.models = {
***************************************************************************/ ***************************************************************************/
attributes: { attributes: {
createdAt: { type: 'number', autoCreatedAt: true, }, createdAt: { type: 'number', autoCreatedAt: true },
updatedAt: { type: 'number', autoUpdatedAt: true, }, updatedAt: { type: 'number', autoUpdatedAt: true },
id: { type: 'number', autoIncrement: true, }, id: { type: 'number', autoIncrement: true }
//-------------------------------------------------------------------------- // --------------------------------------------------------------------------
// /\ Using MongoDB? // /\ Using MongoDB?
// || Replace `id` above with this instead: // || Replace `id` above with this instead:
// //
@ -82,10 +79,9 @@ module.exports.models = {
// //
// Plus, don't forget to configure MongoDB as your default datastore: // Plus, don't forget to configure MongoDB as your default datastore:
// https://sailsjs.com/docs/tutorials/using-mongo-db // https://sailsjs.com/docs/tutorials/using-mongo-db
//-------------------------------------------------------------------------- // --------------------------------------------------------------------------
}, },
/****************************************************************************** /******************************************************************************
* * * *
* The set of DEKs (data encryption keys) for at-rest encryption. * * The set of DEKs (data encryption keys) for at-rest encryption. *
@ -104,7 +100,6 @@ module.exports.models = {
default: 'nuF29j3StsGhRTut9dIrCxCNyYegcwH30FxnZ3kkdiA=' default: 'nuF29j3StsGhRTut9dIrCxCNyYegcwH30FxnZ3kkdiA='
}, },
/*************************************************************************** /***************************************************************************
* * * *
* Whether or not implicit records for associations should be cleaned up * * Whether or not implicit records for associations should be cleaned up *
@ -120,5 +115,4 @@ module.exports.models = {
cascadeOnDestroy: true cascadeOnDestroy: true
}
};

View File

@ -6,4 +6,4 @@ module.exports.passport = {
local: { local: {
strategy: require('passport-local').Strategy strategy: require('passport-local').Strategy
} }
}; }

View File

@ -34,4 +34,4 @@ module.exports.policies = {
TargetController: { TargetController: {
'*': [ 'sessionAuth' ] '*': [ 'sessionAuth' ]
} }
}; }

View File

@ -1,6 +1,6 @@
// Passport protocol configurations // Passport protocol configurations
const crypto = require('crypto'); const crypto = require('crypto')
const base64URL = require('base64url'); const base64URL = require('base64url')
module.exports.protocols = { module.exports.protocols = {
local: { local: {
@ -18,87 +18,87 @@ module.exports.protocols = {
*/ */
login: async function (req, identifier, password, next) { login: async function (req, identifier, password, next) {
if (!validateEmail(identifier)) { if (!validateEmail(identifier)) {
return next(new Error('invalid email address'), false); return next(new Error('invalid email address'), false)
} }
try { try {
const user = await User.findOne({ const user = await User.findOne({
email: identifier email: identifier
}); })
if (!user) {throw new Error('an account with that email was not found');} if (!user) { throw new Error('an account with that email was not found') }
const passport = await Passport.findOne({ const passport = await Passport.findOne({
protocol: 'local', protocol: 'local',
user: user.id user: user.id
}); })
if (passport) { if (passport) {
const res = await Passport.validatePassword(password, passport); const res = await Passport.validatePassword(password, passport)
if (!res) {throw new Error('incorrect password');} if (!res) { throw new Error('incorrect password') }
return next(null, user); return next(null, user)
} else { } else {
throw new Error('that account does not have password login enabled'); throw new Error('that account does not have password login enabled')
} }
} catch (e) { } catch (e) {
return next(e, false); return next(e, false)
} }
}, },
register: async function (user, next) { register: async function (user, next) {
try { try {
const token = generateToken(); const token = generateToken()
const password = user.password; const password = user.password
if (!password.length) {throw new Error('password cannot be blank');} if (!password.length) { throw new Error('password cannot be blank') }
delete user.password; delete user.password
const newUser = await User.create(user).fetch(); const newUser = await User.create(user).fetch()
try { try {
await Passport.create({ await Passport.create({
protocol: 'local', protocol: 'local',
password, password,
user: newUser.id, user: newUser.id,
accessToken: token accessToken: token
}); })
return next(null, token); return next(null, token)
} catch (e) { } catch (e) {
await User.destroy(newUser.id); await User.destroy(newUser.id)
throw e; throw e
} }
} catch (e) { } catch (e) {
return next(e); return next(e)
} }
}, },
update: async function (user, next) { update: async function (user, next) {
throw new Error('not implemented'); throw new Error('not implemented')
}, },
connect: async function (req, res, next) { connect: async function (req, res, next) {
try { try {
const user = req.user; const user = req.user
const password = req.param('password'); const password = req.param('password')
const pass = await Passport.findOne({ const pass = await Passport.findOne({
protocol: 'local', protocol: 'local',
user: user.id user: user.id
}); })
if (!pass) { if (!pass) {
await Passport.create({ await Passport.create({
protocol: 'local', protocol: 'local',
password, password,
user: user.id user: user.id
}); })
} else { } else {
return next(null, user); return next(null, user)
} }
} catch (e) { } catch (e) {
return next(e); return next(e)
} }
} }
} }
}; }
const EMAIL_REGEX = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i; const EMAIL_REGEX = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i
function validateEmail (email) { function validateEmail (email) {
return EMAIL_REGEX.test(email); return EMAIL_REGEX.test(email)
} }
function generateToken () { function generateToken () {
return base64URL(crypto.randomBytes(48)); return base64URL(crypto.randomBytes(48))
} }

View File

@ -66,17 +66,14 @@ module.exports.routes = {
'POST /api/publish': 'BooksController.publish', 'POST /api/publish': 'BooksController.publish',
'GET /api/books': 'BooksController.list', 'GET /api/books': 'BooksController.list',
'GET /api/me': 'UserController.me', 'GET /api/me': 'UserController.me'
// ╦ ╦╔═╗╔╗ ╦ ╦╔═╗╔═╗╦╔═╔═╗ // ╦ ╦╔═╗╔╗ ╦ ╦╔═╗╔═╗╦╔═╔═╗
// ║║║║╣ ╠╩╗╠═╣║ ║║ ║╠╩╗╚═╗ // ║║║║╣ ╠╩╗╠═╣║ ║║ ║╠╩╗╚═╗
// ╚╩╝╚═╝╚═╝╩ ╩╚═╝╚═╝╩ ╩╚═╝ // ╚╩╝╚═╝╚═╝╩ ╩╚═╝╚═╝╩ ╩╚═╝
// ╔╦╗╦╔═╗╔═╗ // ╔╦╗╦╔═╗╔═╗
// ║║║║╚═╗║ // ║║║║╚═╗║
// ╩ ╩╩╚═╝╚═╝ // ╩ ╩╩╚═╝╚═╝
}
};

View File

@ -34,7 +34,6 @@ module.exports.security = {
// allowCredentials: false, // allowCredentials: false,
// }, // },
/**************************************************************************** /****************************************************************************
* * * *
* By default, Sails' built-in CSRF protection is disabled to facilitate * * By default, Sails' built-in CSRF protection is disabled to facilitate *
@ -51,4 +50,4 @@ module.exports.security = {
// csrf: false // csrf: false
}; }

View File

@ -18,8 +18,7 @@ module.exports.session = {
* of your users, forcing them to log in again. * * of your users, forcing them to log in again. *
* * * *
***************************************************************************/ ***************************************************************************/
secret: 'b7f0374251c4d79227067c286fe97ea5', secret: 'b7f0374251c4d79227067c286fe97ea5'
/*************************************************************************** /***************************************************************************
* * * *
@ -36,4 +35,4 @@ module.exports.session = {
// return !!req.path.match(req._sails.LOOKS_LIKE_ASSET_RX); // return !!req.path.match(req._sails.LOOKS_LIKE_ASSET_RX);
// }, // },
}; }

View File

@ -29,7 +29,6 @@ module.exports.sockets = {
// transports: [ 'websocket' ], // transports: [ 'websocket' ],
/*************************************************************************** /***************************************************************************
* * * *
* `beforeConnect` * * `beforeConnect` *
@ -50,7 +49,6 @@ module.exports.sockets = {
// //
// }, // },
/*************************************************************************** /***************************************************************************
* * * *
* `afterDisconnect` * * `afterDisconnect` *
@ -68,7 +66,6 @@ module.exports.sockets = {
// //
// }, // },
/*************************************************************************** /***************************************************************************
* * * *
* Whether to expose a 'GET /__getcookie' route that sets an HTTP-only * * Whether to expose a 'GET /__getcookie' route that sets an HTTP-only *
@ -78,5 +75,4 @@ module.exports.sockets = {
// grant3rdPartyCookie: true, // grant3rdPartyCookie: true,
}
};

View File

@ -38,4 +38,4 @@ module.exports.views = {
layout: 'layouts/layout' layout: 'layouts/layout'
}; }

View File

@ -9,8 +9,7 @@
* https://sailsjs.com/anatomy/tasks/config/babel.js * https://sailsjs.com/anatomy/tasks/config/babel.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.config.set('babel', { grunt.config.set('babel', {
dist: { dist: {
options: { options: {
@ -25,7 +24,7 @@ module.exports = function(grunt) {
} }
] ]
} }
}); })
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// This Grunt plugin is part of the default asset pipeline in Sails, // This Grunt plugin is part of the default asset pipeline in Sails,
@ -50,5 +49,4 @@ module.exports = function(grunt) {
// grunt.loadNpmTasks('grunt-babel'); // grunt.loadNpmTasks('grunt-babel');
// ``` // ```
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}
};

View File

@ -9,8 +9,7 @@
* https://sailsjs.com/anatomy/tasks/config/clean.js * https://sailsjs.com/anatomy/tasks/config/clean.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.config.set('clean', { grunt.config.set('clean', {
dev: ['.tmp/public/**'], dev: ['.tmp/public/**'],
build: ['www'], build: ['www'],
@ -23,7 +22,7 @@ module.exports = function(grunt) {
'www/templates', 'www/templates',
'www/dependencies' 'www/dependencies'
] ]
}); })
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// This Grunt plugin is part of the default asset pipeline in Sails, // This Grunt plugin is part of the default asset pipeline in Sails,
@ -48,5 +47,4 @@ module.exports = function(grunt) {
// grunt.loadNpmTasks('grunt-contrib-clean'); // grunt.loadNpmTasks('grunt-contrib-clean');
// ``` // ```
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}
};

View File

@ -10,8 +10,7 @@
* https://sailsjs.com/anatomy/tasks/config/coffee.js * https://sailsjs.com/anatomy/tasks/config/coffee.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.config.set('coffee', { grunt.config.set('coffee', {
dev: { dev: {
options: { options: {
@ -27,7 +26,7 @@ module.exports = function(grunt) {
ext: '.js' ext: '.js'
}] }]
} }
}); })
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// This Grunt plugin is part of the default asset pipeline in Sails, // This Grunt plugin is part of the default asset pipeline in Sails,
@ -52,5 +51,4 @@ module.exports = function(grunt) {
// grunt.loadNpmTasks('grunt-contrib-coffee'); // grunt.loadNpmTasks('grunt-contrib-coffee');
// ``` // ```
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}
};

View File

@ -10,8 +10,7 @@
* https://sailsjs.com/anatomy/tasks/config/concat.js * https://sailsjs.com/anatomy/tasks/config/concat.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.config.set('concat', { grunt.config.set('concat', {
js: { js: {
src: require('../pipeline').jsFilesToInject, src: require('../pipeline').jsFilesToInject,
@ -21,7 +20,7 @@ module.exports = function(grunt) {
src: require('../pipeline').cssFilesToInject, src: require('../pipeline').cssFilesToInject,
dest: '.tmp/public/concat/production.css' dest: '.tmp/public/concat/production.css'
} }
}); })
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// This Grunt plugin is part of the default asset pipeline in Sails, // This Grunt plugin is part of the default asset pipeline in Sails,
@ -46,5 +45,4 @@ module.exports = function(grunt) {
// grunt.loadNpmTasks('grunt-contrib-concat'); // grunt.loadNpmTasks('grunt-contrib-concat');
// ``` // ```
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}
};

View File

@ -9,8 +9,7 @@
* https://sailsjs.com/anatomy/tasks/config/copy.js * https://sailsjs.com/anatomy/tasks/config/copy.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.config.set('copy', { grunt.config.set('copy', {
dev: { dev: {
files: [{ files: [{
@ -35,8 +34,8 @@ module.exports = function(grunt) {
src: ['**/*'], src: ['**/*'],
dest: '.tmp/public/dist' dest: '.tmp/public/dist'
}] }]
}, }
}); })
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// This Grunt plugin is part of the default asset pipeline in Sails, // This Grunt plugin is part of the default asset pipeline in Sails,
@ -61,5 +60,4 @@ module.exports = function(grunt) {
// grunt.loadNpmTasks('grunt-contrib-copy'); // grunt.loadNpmTasks('grunt-contrib-copy');
// ``` // ```
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}
};

View File

@ -11,14 +11,13 @@
* https://sailsjs.com/anatomy/tasks/config/cssmin.js * https://sailsjs.com/anatomy/tasks/config/cssmin.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.config.set('cssmin', { grunt.config.set('cssmin', {
dist: { dist: {
src: ['.tmp/public/concat/production.css'], src: ['.tmp/public/concat/production.css'],
dest: '.tmp/public/min/production.min.css' dest: '.tmp/public/min/production.min.css'
} }
}); })
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// This Grunt plugin is part of the default asset pipeline in Sails, // This Grunt plugin is part of the default asset pipeline in Sails,
@ -43,5 +42,4 @@ module.exports = function(grunt) {
// grunt.loadNpmTasks('grunt-contrib-cssmin'); // grunt.loadNpmTasks('grunt-contrib-cssmin');
// ``` // ```
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}
};

View File

@ -9,8 +9,7 @@
* https://sailsjs.com/anatomy/tasks/config/hash.js * https://sailsjs.com/anatomy/tasks/config/hash.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.config.set('hash', { grunt.config.set('hash', {
options: { options: {
mapping: '', mapping: '',
@ -18,11 +17,11 @@ module.exports = function(grunt) {
destBasePath: '', destBasePath: '',
flatten: false, flatten: false,
hashLength: 8, hashLength: 8,
hashFunction: function(source, encoding){ hashFunction: function (source, encoding) {
if (!source || !encoding) { if (!source || !encoding) {
throw new Error('Consistency violation: Cannot compute unique hash for production .css/.js cache-busting suffix, because `source` and/or `encoding` are falsey-- but they should be truthy strings! Here they are, respectively:\nsource: '+require('util').inspect(source, {depth:null})+'\nencoding: '+require('util').inspect(encoding, {depth:null})); throw new Error('Consistency violation: Cannot compute unique hash for production .css/.js cache-busting suffix, because `source` and/or `encoding` are falsey-- but they should be truthy strings! Here they are, respectively:\nsource: ' + require('util').inspect(source, {depth: null}) + '\nencoding: ' + require('util').inspect(encoding, {depth: null}))
} }
return require('crypto').createHash('sha1').update(source, encoding).digest('hex'); return require('crypto').createHash('sha1').update(source, encoding).digest('hex')
} }
}, },
js: { js: {
@ -33,7 +32,7 @@ module.exports = function(grunt) {
src: '.tmp/public/min/*.css', src: '.tmp/public/min/*.css',
dest: '.tmp/public/hash/' dest: '.tmp/public/hash/'
} }
}); })
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// This Grunt plugin is part of the default asset pipeline in Sails, // This Grunt plugin is part of the default asset pipeline in Sails,
@ -58,5 +57,4 @@ module.exports = function(grunt) {
// grunt.loadNpmTasks('grunt-hash'); // grunt.loadNpmTasks('grunt-hash');
// ``` // ```
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}
};

View File

@ -10,8 +10,7 @@
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.config.set('jst', { grunt.config.set('jst', {
dev: { dev: {
@ -26,14 +25,13 @@ module.exports = function(grunt) {
// default interpolation. If you want to parse templates with the default _.template behavior // default interpolation. If you want to parse templates with the default _.template behavior
// (i.e. using <div><%= this.id %></div>), there's no need to overwrite `templateSettings.interpolate`. // (i.e. using <div><%= this.id %></div>), there's no need to overwrite `templateSettings.interpolate`.
files: { files: {
// e.g. // e.g.
// 'relative/path/from/gruntfile/to/compiled/template/destination' : ['relative/path/to/sourcefiles/**/*.html'] // 'relative/path/from/gruntfile/to/compiled/template/destination' : ['relative/path/to/sourcefiles/**/*.html']
'.tmp/public/jst.js': require('../pipeline').templateFilesToInject '.tmp/public/jst.js': require('../pipeline').templateFilesToInject
} }
} }
}); })
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// This Grunt plugin is part of the default asset pipeline in Sails, // This Grunt plugin is part of the default asset pipeline in Sails,
@ -58,5 +56,4 @@ module.exports = function(grunt) {
// grunt.loadNpmTasks('grunt-contrib-jst'); // grunt.loadNpmTasks('grunt-contrib-jst');
// ``` // ```
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}
};

View File

@ -9,8 +9,7 @@
* https://sailsjs.com/anatomy/tasks/config/less.js * https://sailsjs.com/anatomy/tasks/config/less.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.config.set('less', { grunt.config.set('less', {
dev: { dev: {
files: [{ files: [{
@ -21,7 +20,7 @@ module.exports = function(grunt) {
ext: '.css' ext: '.css'
}] }]
} }
}); })
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// This Grunt plugin is part of the default asset pipeline in Sails, // This Grunt plugin is part of the default asset pipeline in Sails,
@ -46,5 +45,4 @@ module.exports = function(grunt) {
// grunt.loadNpmTasks('grunt-contrib-less'); // grunt.loadNpmTasks('grunt-contrib-less');
// ``` // ```
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}
};

View File

@ -11,11 +11,9 @@
* https://sailsjs.com/anatomy/tasks/config/sails-linker.js * https://sailsjs.com/anatomy/tasks/config/sails-linker.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.config.set('sails-linker', { grunt.config.set('sails-linker', {
// ╦╔═╗╦ ╦╔═╗╔═╗╔═╗╦═╗╦╔═╗╔╦╗ // ╦╔═╗╦ ╦╔═╗╔═╗╔═╗╦═╗╦╔═╗╔╦╗
// ║╠═╣╚╗╔╝╠═╣╚═╗║ ╠╦╝║╠═╝ ║ // ║╠═╣╚╗╔╝╠═╣╚═╗║ ╠╦╝║╠═╝ ║
// ╚╝╩ ╩ ╚╝ ╩ ╩╚═╝╚═╝╩╚═╩╩ ╩ // ╚╝╩ ╩ ╚╝ ╩ ╩╚═╝╚═╝╩╚═╩╩ ╩
@ -41,7 +39,7 @@ module.exports = function(grunt) {
startTag: '<!--SCRIPTS-->', startTag: '<!--SCRIPTS-->',
endTag: '<!--SCRIPTS END-->', endTag: '<!--SCRIPTS END-->',
fileTmpl: '<script src="%s"></script>', fileTmpl: '<script src="%s"></script>',
appRoot: '.tmp/public', appRoot: '.tmp/public'
// relative: true // relative: true
// ^^ Uncomment this if compiling assets for use in PhoneGap, CDN, etc. // ^^ Uncomment this if compiling assets for use in PhoneGap, CDN, etc.
// (but be note that this can break custom font URLs) // (but be note that this can break custom font URLs)
@ -72,7 +70,7 @@ module.exports = function(grunt) {
startTag: '<!--SCRIPTS-->', startTag: '<!--SCRIPTS-->',
endTag: '<!--SCRIPTS END-->', endTag: '<!--SCRIPTS END-->',
fileTmpl: '<script src="%s"></script>', fileTmpl: '<script src="%s"></script>',
appRoot: '.tmp/public', appRoot: '.tmp/public'
// relative: true // relative: true
// ^^ Uncomment this if compiling assets for use in PhoneGap, CDN, etc. // ^^ Uncomment this if compiling assets for use in PhoneGap, CDN, etc.
// (but be note that this can break custom font URLs) // (but be note that this can break custom font URLs)
@ -84,7 +82,6 @@ module.exports = function(grunt) {
} }
}, },
// ╔═╗╔╦╗╦ ╦╦ ╔═╗╔═╗╦ ╦╔═╗╔═╗╔╦╗╔═╗ // ╔═╗╔╦╗╦ ╦╦ ╔═╗╔═╗╦ ╦╔═╗╔═╗╔╦╗╔═╗
// ╚═╗ ║ ╚╦╝║ ║╣ ╚═╗╠═╣║╣ ║╣ ║ ╚═╗ // ╚═╗ ║ ╚╦╝║ ║╣ ╚═╗╠═╣║╣ ║╣ ║ ╚═╗
// ╚═╝ ╩ ╩ ╩═╝╚═╝╚═╝╩ ╩╚═╝╚═╝ ╩ ╚═╝ // ╚═╝ ╩ ╩ ╩═╝╚═╝╚═╝╩ ╩╚═╝╚═╝ ╩ ╚═╝
@ -111,7 +108,7 @@ module.exports = function(grunt) {
startTag: '<!--STYLES-->', startTag: '<!--STYLES-->',
endTag: '<!--STYLES END-->', endTag: '<!--STYLES END-->',
fileTmpl: '<link rel="stylesheet" href="%s">', fileTmpl: '<link rel="stylesheet" href="%s">',
appRoot: '.tmp/public', appRoot: '.tmp/public'
// relative: true // relative: true
// ^^ Uncomment this if compiling assets for use in PhoneGap, CDN, etc. // ^^ Uncomment this if compiling assets for use in PhoneGap, CDN, etc.
// (but be note that this can break custom font URLs) // (but be note that this can break custom font URLs)
@ -143,7 +140,7 @@ module.exports = function(grunt) {
startTag: '<!--STYLES-->', startTag: '<!--STYLES-->',
endTag: '<!--STYLES END-->', endTag: '<!--STYLES END-->',
fileTmpl: '<link rel="stylesheet" href="%s">', fileTmpl: '<link rel="stylesheet" href="%s">',
appRoot: '.tmp/public', appRoot: '.tmp/public'
// relative: true // relative: true
// ^^ Uncomment this if compiling assets for use in PhoneGap, CDN, etc. // ^^ Uncomment this if compiling assets for use in PhoneGap, CDN, etc.
// (but be note that this can break custom font URLs) // (but be note that this can break custom font URLs)
@ -155,7 +152,6 @@ module.exports = function(grunt) {
} }
}, },
// ╔═╗╦═╗╔═╗╔═╗╔═╗╔╦╗╔═╗╦╦ ╔═╗╔╦╗ ╦ ╦╔╦╗╔╦╗╦ ╔╦╗╔═╗╔╦╗╔═╗╦ ╔═╗╔╦╗╔═╗╔═╗ // ╔═╗╦═╗╔═╗╔═╗╔═╗╔╦╗╔═╗╦╦ ╔═╗╔╦╗ ╦ ╦╔╦╗╔╦╗╦ ╔╦╗╔═╗╔╦╗╔═╗╦ ╔═╗╔╦╗╔═╗╔═╗
// ╠═╝╠╦╝║╣ ║ ║ ║║║║╠═╝║║ ║╣ ║║ ╠═╣ ║ ║║║║ ║ ║╣ ║║║╠═╝║ ╠═╣ ║ ║╣ ╚═╗ // ╠═╝╠╦╝║╣ ║ ║ ║║║║╠═╝║║ ║╣ ║║ ╠═╣ ║ ║║║║ ║ ║╣ ║║║╠═╝║ ╠═╣ ║ ║╣ ╚═╗
// ╩ ╩╚═╚═╝╚═╝╚═╝╩ ╩╩ ╩╩═╝╚═╝═╩╝ ╩ ╩ ╩ ╩ ╩╩═╝ ╩ ╚═╝╩ ╩╩ ╩═╝╩ ╩ ╩ ╚═╝╚═╝ // ╩ ╩╚═╚═╝╚═╝╚═╝╩ ╩╩ ╩╩═╝╚═╝═╩╝ ╩ ╩ ╩ ╩ ╩╩═╝ ╩ ╚═╝╩ ╩╩ ╩═╝╩ ╩ ╩ ╚═╝╚═╝
@ -180,7 +176,7 @@ module.exports = function(grunt) {
startTag: '<!--TEMPLATES-->', startTag: '<!--TEMPLATES-->',
endTag: '<!--TEMPLATES END-->', endTag: '<!--TEMPLATES END-->',
fileTmpl: '<script type="text/javascript" src="%s"></script>', fileTmpl: '<script type="text/javascript" src="%s"></script>',
appRoot: '.tmp/public', appRoot: '.tmp/public'
// relative: true // relative: true
// ^^ Uncomment this if compiling assets for use in PhoneGap, CDN, etc. // ^^ Uncomment this if compiling assets for use in PhoneGap, CDN, etc.
// (but be note that this can break custom font URLs) // (but be note that this can break custom font URLs)
@ -190,9 +186,9 @@ module.exports = function(grunt) {
'views/**/*.html': ['.tmp/public/jst.js'], 'views/**/*.html': ['.tmp/public/jst.js'],
'views/**/*.ejs': ['.tmp/public/jst.js'] 'views/**/*.ejs': ['.tmp/public/jst.js']
} }
}, }
});//</ grunt.config.set() > })// </ grunt.config.set() >
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// This Grunt plugin is part of the default asset pipeline in Sails, // This Grunt plugin is part of the default asset pipeline in Sails,
@ -217,5 +213,4 @@ module.exports = function(grunt) {
// grunt.loadNpmTasks('grunt-sails-linker'); // grunt.loadNpmTasks('grunt-sails-linker');
// ``` // ```
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}
};

View File

@ -10,8 +10,7 @@
* https://sailsjs.com/anatomy/tasks/config/sync.js * https://sailsjs.com/anatomy/tasks/config/sync.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.config.set('sync', { grunt.config.set('sync', {
dev: { dev: {
files: [{ files: [{
@ -20,7 +19,7 @@ module.exports = function(grunt) {
dest: '.tmp/public' dest: '.tmp/public'
}] }]
} }
}); })
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// This Grunt plugin is part of the default asset pipeline in Sails, // This Grunt plugin is part of the default asset pipeline in Sails,
@ -45,5 +44,4 @@ module.exports = function(grunt) {
// grunt.loadNpmTasks('grunt-sync'); // grunt.loadNpmTasks('grunt-sync');
// ``` // ```
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}
};

View File

@ -9,8 +9,7 @@
* https://sailsjs.com/anatomy/tasks/config/uglify.js * https://sailsjs.com/anatomy/tasks/config/uglify.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.config.set('uglify', { grunt.config.set('uglify', {
dist: { dist: {
src: ['.tmp/public/concat/production.js'], src: ['.tmp/public/concat/production.js'],
@ -24,7 +23,7 @@ module.exports = function(grunt) {
'Promise', 'Promise',
'File', 'File',
'Location', 'Location',
'RttcRefPlaceholder', 'RttcRefPlaceholder'
], ],
keep_fnames: true//eslint-disable-line keep_fnames: true//eslint-disable-line
}, },
@ -32,7 +31,7 @@ module.exports = function(grunt) {
keep_fnames: true//eslint-disable-line keep_fnames: true//eslint-disable-line
} }
} }
}); })
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// This Grunt plugin is part of the default asset pipeline in Sails, // This Grunt plugin is part of the default asset pipeline in Sails,
@ -57,6 +56,4 @@ module.exports = function(grunt) {
// grunt.loadNpmTasks('grunt-contrib-uglify'); // grunt.loadNpmTasks('grunt-contrib-uglify');
// ``` // ```
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}
};

View File

@ -9,8 +9,7 @@
* https://sailsjs.com/anatomy/tasks/config/watch.js * https://sailsjs.com/anatomy/tasks/config/watch.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.config.set('watch', { grunt.config.set('watch', {
assets: { assets: {
@ -27,7 +26,7 @@ module.exports = function(grunt) {
'linkAssets' 'linkAssets'
] ]
} }
}); })
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// This Grunt plugin is part of the default asset pipeline in Sails, // This Grunt plugin is part of the default asset pipeline in Sails,
@ -52,5 +51,4 @@ module.exports = function(grunt) {
// grunt.loadNpmTasks('grunt-contrib-watch'); // grunt.loadNpmTasks('grunt-contrib-watch');
// ``` // ```
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}
};

View File

@ -10,8 +10,6 @@
* https://sailsjs.com/anatomy/tasks/pipeline.js * https://sailsjs.com/anatomy/tasks/pipeline.js
*/ */
// ██████╗ ██╗ █████╗ ██╗███╗ ██╗ ██████╗███████╗███████╗ // ██████╗ ██╗ █████╗ ██╗███╗ ██╗ ██████╗███████╗███████╗
// ██╔══██╗██║ ██╔══██╗██║████╗ ██║ ██╔════╝██╔════╝██╔════╝ // ██╔══██╗██║ ██╔══██╗██║████╗ ██║ ██╔════╝██╔════╝██╔════╝
// ██████╔╝██║ ███████║██║██╔██╗ ██║ ██║ ███████╗███████╗ // ██████╔╝██║ ███████║██║██╔██╗ ██║ ██║ ███████╗███████╗
@ -40,8 +38,7 @@ var cssFilesToInject = [
// in no particular order. To customize the ordering, add additional // in no particular order. To customize the ordering, add additional
// items here, _above_ this one. // items here, _above_ this one.
'styles/**/*.css' 'styles/**/*.css'
]; ]
// ██████╗██╗ ██╗███████╗███╗ ██╗████████╗ ███████╗██╗██████╗ ███████╗ // ██████╗██╗ ██╗███████╗███╗ ██╗████████╗ ███████╗██╗██████╗ ███████╗
// ██╔════╝██║ ██║██╔════╝████╗ ██║╚══██╔══╝ ██╔════╝██║██╔══██╗██╔════╝ // ██╔════╝██║ ██║██╔════╝████╗ ██║╚══██╔══╝ ██╔════╝██║██╔══██╗██╔════╝
@ -73,8 +70,7 @@ var jsFilesToInject = [
// in no particular order. To customize the ordering, add additional items // in no particular order. To customize the ordering, add additional items
// here, _above_ this one. // here, _above_ this one.
'js/**/*.js' 'js/**/*.js'
]; ]
// ██████╗██╗ ██╗███████╗███╗ ██╗████████╗ ███████╗██╗██████╗ ███████╗ // ██████╗██╗ ██╗███████╗███╗ ██╗████████╗ ███████╗██╗██████╗ ███████╗
// ██╔════╝██║ ██║██╔════╝████╗ ██║╚══██╔══╝ ██╔════╝██║██╔══██╗██╔════╝ // ██╔════╝██║ ██║██╔════╝████╗ ██║╚══██╔══╝ ██╔════╝██║██╔══██╗██╔════╝
@ -103,9 +99,7 @@ var jsFilesToInject = [
// //
var templateFilesToInject = [ var templateFilesToInject = [
'templates/**/*.html' 'templates/**/*.html'
]; ]
// ███╗ ███╗██╗███████╗ ██████╗ ███████╗███████╗████████╗██╗ ██╗██████╗ // ███╗ ███╗██╗███████╗ ██████╗ ███████╗███████╗████████╗██╗ ██╗██████╗
// ████╗ ████║██║██╔════╝██╔════╝ ██╔════╝██╔════╝╚══██╔══╝██║ ██║██╔══██╗ // ████╗ ████║██║██╔════╝██╔════╝ ██╔════╝██╔════╝╚══██╔══╝██║ ██║██╔══██╗
@ -119,29 +113,29 @@ var templateFilesToInject = [
// the code below, unless you are modifying the default asset pipeline.** // the code below, unless you are modifying the default asset pipeline.**
// Default path for public folder (see documentation on sailsjs.com for more information) // Default path for public folder (see documentation on sailsjs.com for more information)
var tmpPath = '.tmp/public/'; var tmpPath = '.tmp/public/'
// Prefix relative paths to source files so they point to the proper locations // Prefix relative paths to source files so they point to the proper locations
// (i.e. where the other Grunt tasks spit them out, or in some cases, where // (i.e. where the other Grunt tasks spit them out, or in some cases, where
// they reside in the first place) // they reside in the first place)
module.exports.cssFilesToInject = cssFilesToInject.map((cssPath)=>{ module.exports.cssFilesToInject = cssFilesToInject.map((cssPath) => {
// If we're ignoring the file, make sure the ! is at the beginning of the path // If we're ignoring the file, make sure the ! is at the beginning of the path
if (cssPath[0] === '!') { if (cssPath[0] === '!') {
return require('path').join('!' + tmpPath, cssPath.substr(1)); return require('path').join('!' + tmpPath, cssPath.substr(1))
} }
return require('path').join(tmpPath, cssPath); return require('path').join(tmpPath, cssPath)
}); })
module.exports.jsFilesToInject = jsFilesToInject.map((jsPath)=>{ module.exports.jsFilesToInject = jsFilesToInject.map((jsPath) => {
// If we're ignoring the file, make sure the ! is at the beginning of the path // If we're ignoring the file, make sure the ! is at the beginning of the path
if (jsPath[0] === '!') { if (jsPath[0] === '!') {
return require('path').join('!' + tmpPath, jsPath.substr(1)); return require('path').join('!' + tmpPath, jsPath.substr(1))
} }
return require('path').join(tmpPath, jsPath); return require('path').join(tmpPath, jsPath)
}); })
module.exports.templateFilesToInject = templateFilesToInject.map((tplPath)=>{ module.exports.templateFilesToInject = templateFilesToInject.map((tplPath) => {
// If we're ignoring the file, make sure the ! is at the beginning of the path // If we're ignoring the file, make sure the ! is at the beginning of the path
if (tplPath[0] === '!') { if (tplPath[0] === '!') {
return require('path').join('!assets/', tplPath.substr(1)); return require('path').join('!assets/', tplPath.substr(1))
} }
return require('path').join('assets/', tplPath); return require('path').join('assets/', tplPath)
}); })

View File

@ -10,7 +10,7 @@
* https://sailsjs.com/anatomy/tasks/register/build.js * https://sailsjs.com/anatomy/tasks/register/build.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.registerTask('build', [ grunt.registerTask('build', [
// 'polyfill:dev', //« uncomment to ALSO transpile during development (for broader browser compat.) // 'polyfill:dev', //« uncomment to ALSO transpile during development (for broader browser compat.)
'compileAssets', 'compileAssets',
@ -18,5 +18,5 @@ module.exports = function(grunt) {
'linkAssetsBuild', 'linkAssetsBuild',
'clean:build', 'clean:build',
'copy:build' 'copy:build'
]); ])
}; }

View File

@ -11,20 +11,19 @@
* https://sailsjs.com/anatomy/tasks/register/build-prod.js * https://sailsjs.com/anatomy/tasks/register/build-prod.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.registerTask('buildProd', [ grunt.registerTask('buildProd', [
'polyfill:prod', //« Remove this to skip transpilation in production (not recommended) 'polyfill:prod', // « Remove this to skip transpilation in production (not recommended)
'compileAssets', 'compileAssets',
'babel', //« Remove this to skip transpilation in production (not recommended) 'babel', // « Remove this to skip transpilation in production (not recommended)
'concat', 'concat',
'uglify', 'uglify',
'cssmin', 'cssmin',
'hash',//« Cache-busting 'hash', // « Cache-busting
'copy:beforeLinkBuildProd',//« For prettier URLs after cache-busting 'copy:beforeLinkBuildProd', // « For prettier URLs after cache-busting
'linkAssetsBuildProd', 'linkAssetsBuildProd',
'clean:build', 'clean:build',
'copy:build', 'copy:build',
'clean:afterBuildProd' 'clean:afterBuildProd'
]); ])
}; }

View File

@ -7,12 +7,12 @@
* https://sailsjs.com/anatomy/tasks/register/compile-assets.js * https://sailsjs.com/anatomy/tasks/register/compile-assets.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.registerTask('compileAssets', [ grunt.registerTask('compileAssets', [
'clean:dev', 'clean:dev',
'jst:dev', 'jst:dev',
'less:dev', 'less:dev',
'copy:dev', 'copy:dev',
'coffee:dev' 'coffee:dev'
]); ])
}; }

View File

@ -13,15 +13,11 @@
* *
*/ */
module.exports = function (grunt) { module.exports = function (grunt) {
grunt.registerTask('default', [ grunt.registerTask('default', [
// 'polyfill:dev', //« uncomment to ALSO transpile during development (for broader browser compat.) // 'polyfill:dev', //« uncomment to ALSO transpile during development (for broader browser compat.)
'compileAssets', 'compileAssets',
// 'babel', //« uncomment to ALSO transpile during development (for broader browser compat.) // 'babel', //« uncomment to ALSO transpile during development (for broader browser compat.)
'linkAssets', 'linkAssets',
'watch' 'watch'
]); ])
}
};

View File

@ -7,10 +7,10 @@
* https://sailsjs.com/anatomy/tasks/register/link-assets.js * https://sailsjs.com/anatomy/tasks/register/link-assets.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.registerTask('linkAssets', [ grunt.registerTask('linkAssets', [
'sails-linker:devJs', 'sails-linker:devJs',
'sails-linker:devStyles', 'sails-linker:devStyles',
'sails-linker:clientSideTemplates' 'sails-linker:clientSideTemplates'
]); ])
}; }

View File

@ -7,10 +7,10 @@
* https://sailsjs.com/anatomy/tasks/register/link-assets-build.js * https://sailsjs.com/anatomy/tasks/register/link-assets-build.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.registerTask('linkAssetsBuild', [ grunt.registerTask('linkAssetsBuild', [
'sails-linker:devJsBuild', 'sails-linker:devJsBuild',
'sails-linker:devStylesBuild', 'sails-linker:devStylesBuild',
'sails-linker:clientSideTemplatesBuild' 'sails-linker:clientSideTemplatesBuild'
]); ])
}; }

View File

@ -7,10 +7,10 @@
* https://sailsjs.com/anatomy/tasks/register/link-assets-build-prod.js * https://sailsjs.com/anatomy/tasks/register/link-assets-build-prod.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.registerTask('linkAssetsBuildProd', [ grunt.registerTask('linkAssetsBuildProd', [
'sails-linker:prodJsBuild', 'sails-linker:prodJsBuild',
'sails-linker:prodStylesBuild', 'sails-linker:prodStylesBuild',
'sails-linker:clientSideTemplatesBuild' 'sails-linker:clientSideTemplatesBuild'
]); ])
}; }

View File

@ -7,22 +7,21 @@
* https://sailsjs.com/anatomy/tasks/register/polyfill.js * https://sailsjs.com/anatomy/tasks/register/polyfill.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.registerTask('polyfill:prod', 'Add the polyfill file to the top of the list of files to concatenate', ()=>{ grunt.registerTask('polyfill:prod', 'Add the polyfill file to the top of the list of files to concatenate', () => {
grunt.config.set('concat.js.src', [require('sails-hook-grunt/accessible/babel-polyfill')].concat(grunt.config.get('concat.js.src'))); grunt.config.set('concat.js.src', [require('sails-hook-grunt/accessible/babel-polyfill')].concat(grunt.config.get('concat.js.src')))
}); })
grunt.registerTask('polyfill:dev', 'Add the polyfill file to the top of the list of files to copy and link', ()=>{ grunt.registerTask('polyfill:dev', 'Add the polyfill file to the top of the list of files to copy and link', () => {
grunt.config.set('copy.dev.files', grunt.config.get('copy.dev.files').concat({ grunt.config.set('copy.dev.files', grunt.config.get('copy.dev.files').concat({
expand: true, expand: true,
cwd: require('path').dirname(require('sails-hook-grunt/accessible/babel-polyfill')), cwd: require('path').dirname(require('sails-hook-grunt/accessible/babel-polyfill')),
src: require('path').basename(require('sails-hook-grunt/accessible/babel-polyfill')), src: require('path').basename(require('sails-hook-grunt/accessible/babel-polyfill')),
dest: '.tmp/public/polyfill' dest: '.tmp/public/polyfill'
})); }))
var devLinkFiles = grunt.config.get('sails-linker.devJs.files'); var devLinkFiles = grunt.config.get('sails-linker.devJs.files')
grunt.config.set('sails-linker.devJs.files', Object.keys(devLinkFiles).reduce((linkerConfigSoFar, glob)=>{ grunt.config.set('sails-linker.devJs.files', Object.keys(devLinkFiles).reduce((linkerConfigSoFar, glob) => {
linkerConfigSoFar[glob] = ['.tmp/public/polyfill/polyfill.min.js'].concat(devLinkFiles[glob]); linkerConfigSoFar[glob] = ['.tmp/public/polyfill/polyfill.min.js'].concat(devLinkFiles[glob])
return linkerConfigSoFar; return linkerConfigSoFar
}, {})); }, {}))
}); })
}; }

View File

@ -11,17 +11,16 @@
* https://sailsjs.com/anatomy/tasks/register/prod.js * https://sailsjs.com/anatomy/tasks/register/prod.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.registerTask('prod', [ grunt.registerTask('prod', [
'polyfill:prod', //« Remove this to skip transpilation in production (not recommended) 'polyfill:prod', // « Remove this to skip transpilation in production (not recommended)
'compileAssets', 'compileAssets',
'babel', //« Remove this to skip transpilation in production (not recommended) 'babel', // « Remove this to skip transpilation in production (not recommended)
'concat', 'concat',
'uglify', 'uglify',
'cssmin', 'cssmin',
'sails-linker:prodJs', 'sails-linker:prodJs',
'sails-linker:prodStyles', 'sails-linker:prodStyles',
'sails-linker:clientSideTemplates', 'sails-linker:clientSideTemplates'
]); ])
}; }

View File

@ -7,11 +7,11 @@
* https://sailsjs.com/anatomy/tasks/register/sync-assets.js * https://sailsjs.com/anatomy/tasks/register/sync-assets.js
* *
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.registerTask('syncAssets', [ grunt.registerTask('syncAssets', [
'jst:dev', 'jst:dev',
'less:dev', 'less:dev',
'sync:dev', 'sync:dev',
'coffee:dev' 'coffee:dev'
]); ])
}; }