*/
.messages p {
font-size: 14px;
font-weight: 400;
line-height: 1.3;
color: #d83f45;
padding-left: 20px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23d83f45' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-alert-circle'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: center left;
}
================================================
FILE: routes/auth.js
================================================
var express = require('express');
var passport = require('passport');
var FacebookStrategy = require('passport-facebook');
var db = require('../db');
passport.use(new FacebookStrategy({
clientID: process.env['FACEBOOK_CLIENT_ID'],
clientSecret: process.env['FACEBOOK_CLIENT_SECRET'],
callbackURL: '/oauth2/redirect/facebook',
state: true
}, function verify(accessToken, refreshToken, profile, cb) {
db.get('SELECT * FROM federated_credentials WHERE provider = ? AND subject = ?', [
'https://www.facebook.com',
profile.id
], function(err, row) {
if (err) { return cb(err); }
if (!row) {
db.run('INSERT INTO users (name) VALUES (?)', [
profile.displayName
], function(err) {
if (err) { return cb(err); }
var id = this.lastID;
db.run('INSERT INTO federated_credentials (user_id, provider, subject) VALUES (?, ?, ?)', [
id,
'https://www.facebook.com',
profile.id
], function(err) {
if (err) { return cb(err); }
var user = {
id: id,
name: profile.displayName
};
return cb(null, user);
});
});
} else {
db.get('SELECT * FROM users WHERE id = ?', [ row.user_id ], function(err, row) {
if (err) { return cb(err); }
if (!row) { return cb(null, false); }
return cb(null, row);
});
}
});
}));
passport.serializeUser(function(user, cb) {
process.nextTick(function() {
cb(null, { id: user.id, username: user.username, name: user.name });
});
});
passport.deserializeUser(function(user, cb) {
process.nextTick(function() {
return cb(null, user);
});
});
var router = express.Router();
router.get('/login', function(req, res, next) {
res.render('login');
});
router.get('/login/federated/facebook', passport.authenticate('facebook'));
router.get('/oauth2/redirect/facebook', passport.authenticate('facebook', {
successReturnToOrRedirect: '/',
failureRedirect: '/login'
}));
router.post('/logout', function(req, res, next) {
req.logout(function(err) {
if (err) { return next(err); }
res.redirect('/');
});
});
module.exports = router;
================================================
FILE: routes/index.js
================================================
var express = require('express');
var ensureLogIn = require('connect-ensure-login').ensureLoggedIn;
var db = require('../db');
var ensureLoggedIn = ensureLogIn();
function fetchTodos(req, res, next) {
db.all('SELECT * FROM todos WHERE owner_id = ?', [
req.user.id
], function(err, rows) {
if (err) { return next(err); }
var todos = rows.map(function(row) {
return {
id: row.id,
title: row.title,
completed: row.completed == 1 ? true : false,
url: '/' + row.id
}
});
res.locals.todos = todos;
res.locals.activeCount = todos.filter(function(todo) { return !todo.completed; }).length;
res.locals.completedCount = todos.length - res.locals.activeCount;
next();
});
}
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
if (!req.user) { return res.render('home'); }
next();
}, fetchTodos, function(req, res, next) {
res.locals.filter = null;
res.render('index', { user: req.user });
});
router.get('/active', ensureLoggedIn, fetchTodos, function(req, res, next) {
res.locals.todos = res.locals.todos.filter(function(todo) { return !todo.completed; });
res.locals.filter = 'active';
res.render('index', { user: req.user });
});
router.get('/completed', ensureLoggedIn, fetchTodos, function(req, res, next) {
res.locals.todos = res.locals.todos.filter(function(todo) { return todo.completed; });
res.locals.filter = 'completed';
res.render('index', { user: req.user });
});
router.post('/', ensureLoggedIn, function(req, res, next) {
req.body.title = req.body.title.trim();
next();
}, function(req, res, next) {
if (req.body.title !== '') { return next(); }
return res.redirect('/' + (req.body.filter || ''));
}, function(req, res, next) {
db.run('INSERT INTO todos (owner_id, title, completed) VALUES (?, ?, ?)', [
req.user.id,
req.body.title,
req.body.completed == true ? 1 : null
], function(err) {
if (err) { return next(err); }
return res.redirect('/' + (req.body.filter || ''));
});
});
router.post('/:id(\\d+)', ensureLoggedIn, function(req, res, next) {
req.body.title = req.body.title.trim();
next();
}, function(req, res, next) {
if (req.body.title !== '') { return next(); }
db.run('DELETE FROM todos WHERE id = ? AND owner_id = ?', [
req.params.id,
req.user.id
], function(err) {
if (err) { return next(err); }
return res.redirect('/' + (req.body.filter || ''));
});
}, function(req, res, next) {
db.run('UPDATE todos SET title = ?, completed = ? WHERE id = ? AND owner_id = ?', [
req.body.title,
req.body.completed !== undefined ? 1 : null,
req.params.id,
req.user.id
], function(err) {
if (err) { return next(err); }
return res.redirect('/' + (req.body.filter || ''));
});
});
router.post('/:id(\\d+)/delete', ensureLoggedIn, function(req, res, next) {
db.run('DELETE FROM todos WHERE id = ? AND owner_id = ?', [
req.params.id,
req.user.id
], function(err) {
if (err) { return next(err); }
return res.redirect('/' + (req.body.filter || ''));
});
});
router.post('/toggle-all', ensureLoggedIn, function(req, res, next) {
db.run('UPDATE todos SET completed = ? WHERE owner_id = ?', [
req.body.completed !== undefined ? 1 : null,
req.user.id
], function(err) {
if (err) { return next(err); }
return res.redirect('/' + (req.body.filter || ''));
});
});
router.post('/clear-completed', ensureLoggedIn, function(req, res, next) {
db.run('DELETE FROM todos WHERE owner_id = ? AND completed = ?', [
req.user.id,
1
], function(err) {
if (err) { return next(err); }
return res.redirect('/' + (req.body.filter || ''));
});
});
module.exports = router;
================================================
FILE: views/error.ejs
================================================
<%= message %>
<%= error.status %>
<%= error.stack %>
================================================
FILE: views/home.ejs
================================================
Express • TodoMVC
todos helps you get things done
Sign in
================================================
FILE: views/index.ejs
================================================
Express • TodoMVC
<% if (activeCount + completedCount > 0) { %>
<% todos.forEach(function(todo) { %>
- >
<% }); %>
<% } %>
<% if (activeCount + completedCount > 0) { %>
<% } %>
================================================
FILE: views/login.ejs
================================================
Express • TodoMVC
todos
Sign in
<% if (hasMessages) { %>
<% messages.forEach(function(message) { %>
<%= message %>
<% }); %>
<% } %>
Sign in with Facebook