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:
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;