Reintroduce concept of provider, start work on Gmail

This commit is contained in:
Ben Gotow 2016-06-27 10:28:43 -07:00
parent 1fe7fdf4b1
commit 76d873c2b2
6 changed files with 40 additions and 38 deletions

View file

@ -69,6 +69,6 @@ server.register(plugins, (err) => {
server.start((startErr) => {
if (startErr) { throw startErr; }
console.log('Server running at:', server.info.uri);
console.log('API running at:', server.info.uri);
});
});

View file

@ -8,12 +8,10 @@ const {
IMAPConnection,
DatabaseConnector,
SyncPolicy,
Provider,
} = require('nylas-core');
// TODO: Move these to config somehow / somewhere
const CLIENT_ID = '271342407743-nibas08fua1itr1utq9qjladbkv3esdm.apps.googleusercontent.com';
const CLIENT_SECRET = 'WhmxErj-ei6vJXLocNhBbfBF';
const REDIRECT_URL = 'http://localhost:5100/auth/gmail/oauthcallback';
const {GMAIL_CLIENT_ID, GMAIL_CLIENT_SECRET, GMAIL_REDIRECT_URL} = process.env;
const SCOPES = [
'https://www.googleapis.com/auth/userinfo.email', // email address
@ -41,12 +39,13 @@ const exchangeSettings = Joi.object().keys({
eas_server_host: [Joi.string().ip().required(), Joi.string().hostname().required()],
}).required();
const buildAccountWith = ({name, email, settings, credentials}) => {
const buildAccountWith = ({name, email, provider, settings, credentials}) => {
return DatabaseConnector.forShared().then((db) => {
const {AccountToken, Account} = db;
const account = Account.build({
name: name,
provider: provider,
emailAddress: email,
syncPolicy: SyncPolicy.defaultPolicy(),
connectionSettings: settings,
@ -103,6 +102,7 @@ module.exports = (server) => {
return buildAccountWith({
name,
email,
provider: Provider.IMAP,
settings: _.pick(settings, [
'imap_host', 'imap_port',
'smtp_host', 'smtp_port',
@ -120,9 +120,7 @@ module.exports = (server) => {
reply(Serialization.jsonStringify(response));
})
.catch((err) => {
// TODO: Lots more of this
console.log(err)
reply({error: err.toString()});
reply({error: err.message}).code(400);
})
},
});
@ -137,7 +135,7 @@ module.exports = (server) => {
auth: false,
},
handler: (request, reply) => {
const oauthClient = new OAuth2(CLIENT_ID, CLIENT_SECRET, REDIRECT_URL);
const oauthClient = new OAuth2(GMAIL_CLIENT_ID, GMAIL_CLIENT_SECRET, GMAIL_REDIRECT_URL);
reply.redirect(oauthClient.generateAuthUrl({
access_type: 'offline',
prompt: 'consent',
@ -161,16 +159,16 @@ module.exports = (server) => {
},
},
handler: (request, reply) => {
const oauthClient = new OAuth2(CLIENT_ID, CLIENT_SECRET, REDIRECT_URL);
const oauthClient = new OAuth2(GMAIL_CLIENT_ID, GMAIL_CLIENT_SECRET, GMAIL_REDIRECT_URL);
oauthClient.getToken(request.query.code, (err, tokens) => {
if (err) {
reply(err.message).code(400);
reply({error: err.message}).code(400);
return;
}
oauthClient.setCredentials(tokens);
google.oauth2({version: 'v2', auth: oauthClient}).userinfo.get((error, profile) => {
if (error) {
reply(error.message).code(400);
reply({error: error.message}).code(400);
return;
}
@ -183,15 +181,21 @@ module.exports = (server) => {
const credentials = {
access_token: tokens.access_token,
refresh_token: tokens.refresh_token,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
client_id: GMAIL_CLIENT_ID,
client_secret: GMAIL_CLIENT_SECRET,
}
Promise.all([
IMAPConnection.connect({}, Object.assign({}, settings, credentials)),
])
.then(() =>
buildAccountWith({name: profile.name, email: profile.email, settings, credentials})
buildAccountWith({
name: profile.name,
email: profile.email,
provider: Provider.Gmail,
settings,
credentials,
})
)
.then(({account, token}) => {
const response = account.toJSON();
@ -199,8 +203,7 @@ module.exports = (server) => {
reply(Serialization.jsonStringify(response));
})
.catch((connectionErr) => {
// TODO: Lots more of this
reply({error: connectionErr.toString()});
reply({error: connectionErr.message}).code(400);
});
});
});

View file

@ -1,11 +0,0 @@
{
"storage": {
"database": "account-$ACCOUNTID",
"username": null,
"password": null,
"options": {
"dialect": "sqlite",
"storage": "./account-$ACCOUNTID.sqlite"
}
}
}

View file

@ -1,11 +1,14 @@
global.Promise = require('bluebird');
module.exports = {
Provider: {
Gmail: 'gmail',
IMAP: 'imap',
},
DatabaseConnector: require('./database-connector'),
PubsubConnector: require('./pubsub-connector'),
IMAPConnection: require('./imap-connection'),
SyncPolicy: require('./sync-policy'),
SchedulerUtils: require('./scheduler-utils'),
Config: require(`./config/${process.env.ENV || 'development'}`),
ExtendableError: require('./extendable-error'),
}

View file

@ -1,12 +1,12 @@
const crypto = require('crypto');
const {JSONType, JSONARRAYType} = require('../../database-types');
const algorithm = 'aes-256-ctr';
const password = 'd6F3Efeq';
const {DB_ENCRYPTION_ALGORITHM, DB_ENCRYPTION_PASSWORD} = process.env;
module.exports = (sequelize, Sequelize) => {
const Account = sequelize.define('Account', {
name: Sequelize.STRING,
provider: Sequelize.STRING,
emailAddress: Sequelize.STRING,
connectionSettings: JSONType('connectionSettings'),
connectionCredentials: Sequelize.STRING,
@ -37,7 +37,7 @@ module.exports = (sequelize, Sequelize) => {
if (!(json instanceof Object)) {
throw new Error("Call setCredentials with JSON!")
}
const cipher = crypto.createCipher(algorithm, password)
const cipher = crypto.createCipher(DB_ENCRYPTION_ALGORITHM, DB_ENCRYPTION_PASSWORD)
let crypted = cipher.update(JSON.stringify(json), 'utf8', 'hex')
crypted += cipher.final('hex');
@ -45,7 +45,7 @@ module.exports = (sequelize, Sequelize) => {
},
decryptedCredentials: function decryptedCredentials() {
const decipher = crypto.createDecipher(algorithm, password)
const decipher = crypto.createDecipher(DB_ENCRYPTION_ALGORITHM, DB_ENCRYPTION_PASSWORD)
let dec = decipher.update(this.connectionCredentials, 'hex', 'utf8')
dec += decipher.final('utf8');

View file

@ -1,4 +1,5 @@
const {
Provider,
SchedulerUtils,
IMAPConnection,
PubsubConnector,
@ -129,13 +130,19 @@ class SyncWorker {
return Category.findAll().then((categories) => {
const priority = ['inbox', 'drafts', 'sent'].reverse();
const categoriesToSync = categories.sort((a, b) =>
let categoriesToSync = categories.sort((a, b) =>
(priority.indexOf(a.role) - priority.indexOf(b.role)) * -1
)
// const filtered = sorted.filter(cat =>
// ['[Gmail]/All Mail', '[Gmail]/Trash', '[Gmail]/Spam'].includes(cat.name)
// )
if (this._account.provider === Provider.Gmail) {
categoriesToSync = categoriesToSync.filter(cat =>
['[Gmail]/All Mail', '[Gmail]/Trash', '[Gmail]/Spam'].includes(cat.name)
)
if (categoriesToSync.length !== 3) {
throw new Error(`Account is missing a core Gmail folder: ${categoriesToSync.join(',')}`)
}
}
// TODO Don't accumulate errors, just bail on the first error and clear
// the queue and the connection