ff-stream-web

git clone git://archive.git.mtrnord.blog/MTRNord/ff-stream-web.git
Log | Files | Refs | README | LICENSE

commit e550786dbf40d1f968b5fc9055fdf7760367782e
parent 78dc6a916b867d1e85a75ea78064a63d795b816c
Author: Rafael Arquero Gimeno <rafael@arque.ro>
Date:   Sun, 27 Mar 2016 20:00:16 +0200

Introduce Sequelize as ORM. Facebook login works as espected.

Diffstat:
Mpackage.json | 2++
Msrc/config.js | 2+-
Msrc/core/db.js | 70++++++----------------------------------------------------------------
Msrc/core/passport.js | 139+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Asrc/data/models/User.js | 16++++++++++++++++
Asrc/data/models/UserClaim.js | 10++++++++++
Asrc/data/models/UserLogin.js | 10++++++++++
Asrc/data/models/UserProfile.js | 11+++++++++++
Asrc/data/models/index.js | 22++++++++++++++++++++++
9 files changed, 154 insertions(+), 128 deletions(-)

diff --git a/package.json b/package.json @@ -35,6 +35,7 @@ "react": "15.0.0-rc.2", "react-dom": "15.0.0-rc.2", "react-routing": "0.0.7", + "sequelize": "^3.20.0", "source-map-support": "0.4.0", "whatwg-fetch": "0.11.0" }, @@ -83,6 +84,7 @@ "redbox-react": "^1.2.2", "stylelint": "^5.2.1", "stylelint-config-standard": "^4.0.1", + "sqlite3": "^3.1.2", "url-loader": "^0.5.7", "webpack": "^1.12.14", "webpack-hot-middleware": "^2.10.0", diff --git a/src/config.js b/src/config.js @@ -13,7 +13,7 @@ export const port = process.env.PORT || 3000; export const host = process.env.WEBSITE_HOSTNAME || `localhost:${port}`; -export const databaseUrl = process.env.DATABASE_URL || 'postgresql://demo:Lqk62xg6TBm5UhfR@demo.ctbl5itzitm4.us-east-1.rds.amazonaws.com:5432/membership01'; +export const databaseUrl = process.env.DATABASE_URL || 'sqlite:database.sqlite'; export const analytics = { diff --git a/src/core/db.js b/src/core/db.js @@ -7,75 +7,17 @@ * LICENSE.txt file in the root directory of this source tree. */ -import db from 'pg'; -import Promise from 'bluebird'; +import Sequelize from 'sequelize'; import { databaseUrl } from '../config'; // TODO: Customize database connection settings /* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */ -db.defaults.ssl = true; -db.defaults.poolSize = 2; -db.defaults.application_name = 'RSK'; -/* jscs:enable requireCamelCaseOrUpperCaseIdentifiers */ - -/** - * Promise-based wrapper for pg.Client - * https://github.com/brianc/node-postgres/wiki/Client - */ -function AsyncClient(client) { - this.client = client; - this.query = this.query.bind(this); - this.end = this.end.bind(this); -} - -AsyncClient.prototype.query = function query(sql, ...args) { - return new Promise((resolve, reject) => { - if (args.length) { - this.client.query(sql, args, (err, result) => { - if (err) { - reject(err); - } else { - resolve(result); - } - }); - } else { - this.client.query(sql, (err, result) => { - if (err) { - reject(err); - } else { - resolve(result); - } - }); - } - }); -}; -AsyncClient.prototype.end = function end() { - this.client.end(); -}; - -/** - * Promise-based wrapper for pg.connect() - * https://github.com/brianc/node-postgres/wiki/pg - */ -db.connect = (connect => callback => new Promise((resolve, reject) => { - connect.call(db, databaseUrl, (err, client, done) => { - if (err) { - if (client) { - done(client); - } +// db.defaults.ssl = true; +// db.defaults.poolSize = 2; +// db.defaults.application_name = 'RSK'; +/* jscs:enable requireCamelCaseOrUpperCaseIdentifiers */ - reject(err); - } else { - callback(new AsyncClient(client)).then(() => { - done(); - resolve(); - }).catch(error => { - done(client); - reject(error); - }); - } - }); -}))(db.connect); +const db = new Sequelize(databaseUrl); export default db; diff --git a/src/core/passport.js b/src/core/passport.js @@ -15,7 +15,7 @@ import passport from 'passport'; import { Strategy as FacebookStrategy } from 'passport-facebook'; -import db from './db'; +import { User, UserLogin, UserClaim, UserProfile } from '../data/models'; import { auth as config } from '../config'; /** @@ -29,83 +29,96 @@ passport.use(new FacebookStrategy({ passReqToCallback: true, }, (req, accessToken, refreshToken, profile, done) => { const loginName = 'facebook'; - db.connect(async ({ query }) => { + const claimType = 'urn:facebook:access_token'; + const fooBar = async () => { if (req.user) { - let result = await query( - 'SELECT 1 FROM user_login WHERE name = $1 AND key = $2', - loginName, profile.id - ); - if (result.rowCount) { + const userLogin = await UserLogin.findOne({ + attributes: ['id'], + where: { name: loginName, key: profile.id }, + }); + if (userLogin) { // There is already a Facebook account that belongs to you. // Sign in with that account or delete it, then link it with your current account. done(); } else { - await query(` - INSERT INTO user_account (id, email) SELECT $1, $2::character - WHERE NOT EXISTS (SELECT 1 FROM user_account WHERE id = $1);`, - req.user.id, profile._json.email); - await query(` - INSERT INTO user_login (user_id, name, key) VALUES ($1, 'facebook', $2);`, - req.user.id, profile.id); - await query(` - INSERT INTO user_claim (user_id, type, value) VALUES - ($1, 'urn:facebook:access_token', $3);`, - req.user.id, profile.id); - await query(` - INSERT INTO user_profile (user_id) SELECT $1 - WHERE NOT EXISTS (SELECT 1 FROM user_profile WHERE user_id = $1);`, - req.user.id); - await query(` - UPDATE user_profile SET - display_name = COALESCE(NULLIF(display_name, ''), $2), - gender = COALESCE(NULLIF(gender, ''), $3), - picture = COALESCE(NULLIF(picture, ''), $4), - WHERE user_id = $1;`, - req.user.id, profile.displayName, profile._json.gender, - `https://graph.facebook.com/${profile.id}/picture?type=large`); - result = await query(` - SELECT id, email FROM user_account WHERE id = $1;`, - req.user.id); - done(null, result.rows[0]); + const user = await User.create({ + id: req.user.id, + email: profile._json.email, + logins: [ + { name: loginName, key: profile.id }, + ], + claims: [ + { type: claimType, value: profile.id }, + ], + profile: { + displayName: profile.displayName, + gender: profile._json.gender, + picture: `https://graph.facebook.com/${profile.id}/picture?type=large`, + }, + }, { + include: [ + { model: UserLogin, as: 'logins' }, + { model: UserClaim, as: 'claims' }, + { model: UserProfile, as: 'profile' }, + ], + }); + done(null, { + id: user.id, + email: user.email, + }); } } else { - let result = await query(` - SELECT u.id, u.email FROM user_account AS u - LEFT JOIN user_login AS l ON l.user_id = u.id - WHERE l.name = $1 AND l.key = $2`, loginName, profile.id); - if (result.rowCount) { - done(null, result.rows[0]); + const users = await User.findAll({ + attributes: ['id', 'email'], + where: { '$logins.name$': loginName, '$logins.key$': profile.id }, + include: [ + { + attributes: ['name', 'key'], + model: UserLogin, + as: 'logins', + required: true, + }, + ], + }); + if (users.length) { + done(null, users[0]); } else { - result = await query('SELECT 1 FROM user_account WHERE email = $1', profile._json.email); - if (result.rowCount) { + let user = await User.findOne({ where: { email: profile._json.email } }); + if (user) { // There is already an account using this email address. Sign in to // that account and link it with Facebook manually from Account Settings. done(null); } else { - result = await query(` - INSERT INTO user_account (email) VALUES ($1) RETURNING (id)`, - profile._json.email - ); - const userId = result.rows[0].id; - await query(` - INSERT INTO user_login (user_id, name, key) VALUES ($1, 'facebook', $2)`, - userId, profile.id); - await query(` - INSERT INTO user_claim (user_id, type, value) VALUES - ($1, 'urn:facebook:access_token', $2);`, - userId, accessToken); - await query(` - INSERT INTO user_profile (user_id, display_name, gender, picture) - VALUES ($1, $2, $3, $4);`, - userId, profile.displayName, profile._json.gender, - `https://graph.facebook.com/${profile.id}/picture?type=large` - ); - result = await query('SELECT id, email FROM user_account WHERE id = $1;', userId); - done(null, result.rows[0]); + user = await User.create({ + email: profile._json.email, + logins: [ + { name: loginName, key: profile.id }, + ], + claims: [ + { type: claimType, value: accessToken }, + ], + profile: { + displaynName: profile.displayName, + gender: profile._json.gender, + picture: `https://graph.facebook.com/${profile.id}/picture?type=large`, + }, + }, { + include: [ + { model: UserLogin, as: 'logins' }, + { model: UserClaim, as: 'claims' }, + { model: UserProfile, as: 'profile' }, + ], + }); + done(null, { + id: user.id, + email: user.email, + }); } } } - }).catch(done); + }; + + fooBar().catch(done); })); export default passport; diff --git a/src/data/models/User.js b/src/data/models/User.js @@ -0,0 +1,16 @@ +export default (sequelize, DataTypes) => sequelize.define('User', { + email: { + type: DataTypes.STRING, + validate: { + isEmail: true, + }, + }, +}, { + classMethods: { + associate({ User, UserLogin, UserClaim, UserProfile }) { + User.hasMany(UserLogin, { as: 'logins' }); + User.hasMany(UserClaim, { as: 'claims' }); + User.hasOne(UserProfile, { as: 'profile' }); + }, + }, +}); diff --git a/src/data/models/UserClaim.js b/src/data/models/UserClaim.js @@ -0,0 +1,10 @@ +export default (sequelize, DataTypes) => sequelize.define('UserClaim', { + type: DataTypes.STRING, // 'urn:facebook:access_token' + value: DataTypes.INTEGER, // profile.id +}, { + classMethods: { + associate({ User, UserClaim }) { + UserClaim.belongsTo(User); + }, + }, +}); diff --git a/src/data/models/UserLogin.js b/src/data/models/UserLogin.js @@ -0,0 +1,10 @@ +export default (sequelize, DataTypes) => sequelize.define('UserLogin', { + name: DataTypes.STRING, // loginName + key: DataTypes.INTEGER, // profile.id +}, { + classMethods: { + associate({ User, UserLogin }) { + UserLogin.belongsTo(User); + }, + }, +}); diff --git a/src/data/models/UserProfile.js b/src/data/models/UserProfile.js @@ -0,0 +1,11 @@ +export default (sequelize, DataTypes) => sequelize.define('UserProfile', { + displayName: DataTypes.INTEGER, // profile.displayName + gender: DataTypes.INTEGER, // profile._json.gender + picture: DataTypes.INTEGER, // `https://graph.facebook.com/${profile.id}/picture?type=large` +}, { + classMethods: { + associate({ User, UserProfile }) { + UserProfile.belongsTo(User); + }, + }, +}); diff --git a/src/data/models/index.js b/src/data/models/index.js @@ -0,0 +1,22 @@ +import Sequelize from 'sequelize'; +import db from '../../core/db'; + +const models = { + User: require('./User').default(db, Sequelize), + UserLogin: require('./UserLogin').default(db, Sequelize), + UserClaim: require('./UserClaim').default(db, Sequelize), + UserProfile: require('./UserProfile').default(db, Sequelize), +}; + +Object.keys(models).forEach((modelName) => { + if (models[modelName].associate) { + models[modelName].associate(models); + } +}); + +db.sync(); + +export const User = models.User; +export const UserLogin = models.UserLogin; +export const UserClaim = models.UserClaim; +export const UserProfile = models.UserProfile;