@dhyasama/totem-models 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.npmignore +14 -0
- package/changeBSON.js +27 -0
- package/dictionaries/nicknames.json +703 -0
- package/index.js +42 -0
- package/lib/Account.js +312 -0
- package/lib/Activity.js +138 -0
- package/lib/CalendarEvent.js +150 -0
- package/lib/Document.js +357 -0
- package/lib/Fund.js +143 -0
- package/lib/LimitedPartner.js +780 -0
- package/lib/List.js +217 -0
- package/lib/News.js +161 -0
- package/lib/Note.js +170 -0
- package/lib/Organization.js +1011 -0
- package/lib/Person.js +1163 -0
- package/package.json +37 -0
- package/test/Account.js +139 -0
- package/test/Activity.js +167 -0
- package/test/CalendarEvent.js +59 -0
- package/test/Fund.js +113 -0
- package/test/LimitedPartner.js +554 -0
- package/test/List.js +270 -0
- package/test/News.js +109 -0
- package/test/Note.js +304 -0
- package/test/Organization.js +750 -0
- package/test/Person.js +1042 -0
- package/test/config.js +49 -0
package/index.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var bootstrap = function(mongoose, config) {
|
|
4
|
+
|
|
5
|
+
var env = process.env.NODE_ENV || 'development';
|
|
6
|
+
|
|
7
|
+
// turn on debug if in development and not explicitly turned off
|
|
8
|
+
mongoose.set('debug', (!config.suppressDebugLog && env == 'development'));//mongoose.set('debug',true);
|
|
9
|
+
|
|
10
|
+
// use bluebird promises globally within mongoose
|
|
11
|
+
mongoose.Promise = require('bluebird');
|
|
12
|
+
|
|
13
|
+
// Validate config
|
|
14
|
+
var configHas_LPS_ENABLED = typeof config['LPS_ENABLED'] != 'undefined';
|
|
15
|
+
if (!configHas_LPS_ENABLED) throw new Error('Config is missing LPS_ENABLED setting');
|
|
16
|
+
|
|
17
|
+
var configHas_CUSTOMER_ID = typeof config['CUSTOMER_ID'] != 'undefined';
|
|
18
|
+
if (!configHas_CUSTOMER_ID) throw new Error('Config is missing CUSTOMER_ID setting');
|
|
19
|
+
|
|
20
|
+
require('./lib/Account.js')(mongoose, config);
|
|
21
|
+
require('./lib/Activity.js')(mongoose, config);
|
|
22
|
+
require('./lib/CalendarEvent.js')(mongoose, config);
|
|
23
|
+
require('./lib/Fund.js')(mongoose, config);
|
|
24
|
+
require('./lib/List.js')(mongoose, config);
|
|
25
|
+
require('./lib/News.js')(mongoose, config);
|
|
26
|
+
|
|
27
|
+
// note must be loaded before limited partner
|
|
28
|
+
// note must be loaded before organization
|
|
29
|
+
// note must be loaded before person
|
|
30
|
+
require('./lib/Note.js')(mongoose, config);
|
|
31
|
+
|
|
32
|
+
// organization must be loaded before limited partner
|
|
33
|
+
require('./lib/Organization.js')(mongoose, config);
|
|
34
|
+
|
|
35
|
+
// limited partner must be loaded before person
|
|
36
|
+
require('./lib/LimitedPartner.js')(mongoose, config);
|
|
37
|
+
|
|
38
|
+
require('./lib/Person.js')(mongoose, config);
|
|
39
|
+
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
module.exports = bootstrap;
|
package/lib/Account.js
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
module.exports = function(mongoose, config) {
|
|
4
|
+
|
|
5
|
+
var
|
|
6
|
+
|
|
7
|
+
Schema = mongoose.Schema,
|
|
8
|
+
Crypto = require('@dhyasama/ffvc-crypto'),
|
|
9
|
+
crypto = new Crypto({'key': config.crypto.key}),
|
|
10
|
+
env = process.env.NODE_ENV || 'development',
|
|
11
|
+
bcrypt = require('bcrypt'),
|
|
12
|
+
_ = require('underscore');
|
|
13
|
+
|
|
14
|
+
var Account = new Schema({
|
|
15
|
+
|
|
16
|
+
username: { type: String, required: true, unique: true },
|
|
17
|
+
password: { type: String, default: '' },
|
|
18
|
+
email: { type: String, required: true, unique: true },
|
|
19
|
+
avatar: { type: String, default: '/media/abstract-cubes.png' },
|
|
20
|
+
reset: {
|
|
21
|
+
token: { type: String, default: '' },
|
|
22
|
+
expiresOn: { type: Date }
|
|
23
|
+
},
|
|
24
|
+
setup: {
|
|
25
|
+
token: { type: String, default: '' },
|
|
26
|
+
expiresOn: { type: Date },
|
|
27
|
+
complete: { type: Boolean, default: false },
|
|
28
|
+
terms: {
|
|
29
|
+
raw: { type: String },
|
|
30
|
+
hash: { type: String }
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
twoFactorAuth: {
|
|
34
|
+
enabled: { type: Boolean, default: true },
|
|
35
|
+
phone: { type: String, default: '' },
|
|
36
|
+
country: {
|
|
37
|
+
callingCode: { type: String, default: '' },
|
|
38
|
+
abbreviation: { type: String, default: '' }
|
|
39
|
+
},
|
|
40
|
+
verified: { type: Boolean, default: false }
|
|
41
|
+
},
|
|
42
|
+
admin: { type: Boolean, default: false },
|
|
43
|
+
active: { type: Boolean, default: false },
|
|
44
|
+
subscriber: { type: Boolean, default: false },
|
|
45
|
+
role: { type: String, enum: ['admin', 'lp-full', 'lp-names', 'none'], default: 'none' },
|
|
46
|
+
|
|
47
|
+
// Enable this and iLevel links will show on portfolio pages
|
|
48
|
+
iLevelUser: { type: Boolean, default: false },
|
|
49
|
+
|
|
50
|
+
person: {
|
|
51
|
+
type: Schema.ObjectId,
|
|
52
|
+
ref: 'Person',
|
|
53
|
+
required: true
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
// todo - change to org
|
|
57
|
+
customer: {
|
|
58
|
+
type: Schema.ObjectId,
|
|
59
|
+
ref: 'Investor',
|
|
60
|
+
required: false
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
// todo - port from customer
|
|
64
|
+
org: {
|
|
65
|
+
type: Schema.ObjectId,
|
|
66
|
+
ref: 'Organization',
|
|
67
|
+
required: false
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
auth: {
|
|
71
|
+
|
|
72
|
+
strategy: { type: String, enum: ['google', 'microsoft', 'none'], default: 'none' },
|
|
73
|
+
|
|
74
|
+
google: {
|
|
75
|
+
accessToken: { type: String, default: '' },
|
|
76
|
+
refreshToken: { type: String, default: '' },
|
|
77
|
+
params: { type: Schema.Types.Mixed },
|
|
78
|
+
profile: { type: Schema.Types.Mixed },
|
|
79
|
+
watch: { type: Schema.Types.Mixed },
|
|
80
|
+
nextSyncToken: { type: String, default: null }
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
microsoft: {
|
|
84
|
+
accessToken: { type: String, default: '' },
|
|
85
|
+
refreshToken: { type: String, default: '' },
|
|
86
|
+
params: { type: Schema.Types.Mixed },
|
|
87
|
+
profile: { type: Schema.Types.Mixed },
|
|
88
|
+
watch: { type: Schema.Types.Mixed }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
calendar: {
|
|
94
|
+
active: { type: Boolean, default: false }, // only pull if this is active
|
|
95
|
+
forcePullAll: { type: Boolean, default: false } // override newest event date as min and go back all the way
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
Account.methods.phone = function() {
|
|
101
|
+
return this.twoFactorAuth.country.callingCode + this.twoFactorAuth.phone;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
Account.statics.getById = function (id, cb) {
|
|
105
|
+
|
|
106
|
+
this
|
|
107
|
+
.findById(id)
|
|
108
|
+
.populate('person org')
|
|
109
|
+
.exec(function (err, account) {
|
|
110
|
+
if (err) {
|
|
111
|
+
return cb(err, null);
|
|
112
|
+
}
|
|
113
|
+
else if (account && account.person) {
|
|
114
|
+
account.deepPopulate('person.lps.lp', function (err, result) {
|
|
115
|
+
return cb(err, account);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
else if (!account) {
|
|
119
|
+
return cb(new Error('No account with id ' + id.toString()), null);
|
|
120
|
+
}
|
|
121
|
+
else if (account && !account.person) {
|
|
122
|
+
return cb(new Error('Missing person reference on account with id ' + id.toString()), null);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
return cb(new Error('Unknown error'), null);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
Account.statics.getByPersonIds = function (ids, cb) {
|
|
132
|
+
this
|
|
133
|
+
.find({ 'person': { $in : ids } })
|
|
134
|
+
.populate('person', 'name')
|
|
135
|
+
.exec(cb);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
Account.statics.upsert = function(account, cb) {
|
|
139
|
+
account.save(cb);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
Account.statics.findByUsername = function(username, cb) {
|
|
143
|
+
this
|
|
144
|
+
.findOne({ 'username': new RegExp(username, 'i') })
|
|
145
|
+
.populate('person')
|
|
146
|
+
.exec(function (err, account) {
|
|
147
|
+
cb(err, account);
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
Account.statics.findByEmail = function(email, cb) {
|
|
152
|
+
this
|
|
153
|
+
.findOne({ 'email': email})
|
|
154
|
+
.populate('person')
|
|
155
|
+
.exec(function (err, account) {
|
|
156
|
+
cb(err, account);
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
Account.statics.findByffEmail = function(email, cb) {
|
|
161
|
+
email = email.toLowerCase();
|
|
162
|
+
|
|
163
|
+
if (email.indexOf('@ffvc.com') > -1) {
|
|
164
|
+
this
|
|
165
|
+
.findOne({ 'email': email})
|
|
166
|
+
.populate('person')
|
|
167
|
+
.exec(function (err, account) {
|
|
168
|
+
cb(err, account);
|
|
169
|
+
});
|
|
170
|
+
} else {
|
|
171
|
+
//passing in null err so passport hits failureRedirect: '/login'
|
|
172
|
+
cb(null, null);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
Account.statics.findByResetToken = function(token, cb) {
|
|
177
|
+
this
|
|
178
|
+
.findOne({ 'reset.token': token})
|
|
179
|
+
.populate('person')
|
|
180
|
+
.exec(function (err, account) {
|
|
181
|
+
cb(err, account);
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
Account.statics.findBySetupToken = function(token, cb) {
|
|
186
|
+
this
|
|
187
|
+
.findOne({ 'setup.token': token})
|
|
188
|
+
.populate('person')
|
|
189
|
+
.exec(function (err, account) {
|
|
190
|
+
cb(err, account);
|
|
191
|
+
});
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
Account.statics.findByCredentials = function (username, cleartextPassword, cb) {
|
|
195
|
+
|
|
196
|
+
this
|
|
197
|
+
.findOne({ 'username': new RegExp(username, 'i') })
|
|
198
|
+
.populate('person')
|
|
199
|
+
.exec(function (err, account) {
|
|
200
|
+
|
|
201
|
+
if (err) {
|
|
202
|
+
return cb(err, null);
|
|
203
|
+
}
|
|
204
|
+
else if (account != null) {
|
|
205
|
+
|
|
206
|
+
// found user, now check password
|
|
207
|
+
|
|
208
|
+
bcrypt.compare(cleartextPassword, account.password, function(err, res) {
|
|
209
|
+
if (err) { return cb(err, null); } // error
|
|
210
|
+
else if (res) { return cb(null, account); } // correct password
|
|
211
|
+
else { return cb(null, null); } // wrong password
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
return cb(null, null); // user not found
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
Account.statics.listGoogleAccounts = function(cb) {
|
|
224
|
+
|
|
225
|
+
this
|
|
226
|
+
.find({
|
|
227
|
+
'active': true,
|
|
228
|
+
'calendar.active': true,
|
|
229
|
+
'auth.strategy': 'google',
|
|
230
|
+
'auth.google.refreshToken': { $ne: null }
|
|
231
|
+
})
|
|
232
|
+
.exec(cb);
|
|
233
|
+
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
Account.statics.listAllAccounts = function (cb) {
|
|
237
|
+
|
|
238
|
+
// sort is done post retrieval because query sort is done pre-decryption
|
|
239
|
+
|
|
240
|
+
this
|
|
241
|
+
.find({'active': true})
|
|
242
|
+
.populate('person')
|
|
243
|
+
.exec(function (err, accounts) {
|
|
244
|
+
return cb(err, _.sortBy(accounts, function(account) {
|
|
245
|
+
if (account.person && account.person.name && account.person.name.last) {
|
|
246
|
+
return account.person.name.last;
|
|
247
|
+
}
|
|
248
|
+
else return '';
|
|
249
|
+
}));
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
Account.statics.listAllAccountsSkinny = function (cb) {
|
|
255
|
+
|
|
256
|
+
// sort is done post retrieval because query sort is done pre-decryption
|
|
257
|
+
this
|
|
258
|
+
.find({'active': true})
|
|
259
|
+
.exec(cb);
|
|
260
|
+
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
//Account.pre('save', function(next) {
|
|
264
|
+
//
|
|
265
|
+
// // Encrypt sensitive data
|
|
266
|
+
//
|
|
267
|
+
// if (this.auth) {
|
|
268
|
+
// if (this.auth.google) {
|
|
269
|
+
// if (this.auth.google.accessToken) this.auth.google.accessToken = crypto.encrypt(this.auth.google.accessToken);
|
|
270
|
+
// if (this.auth.google.refreshToken) this.auth.google.refreshToken = crypto.encrypt(this.auth.google.refreshToken);
|
|
271
|
+
// if (this.auth.google.params) {
|
|
272
|
+
// if (this.auth.google.params.access_token) this.auth.google.params.access_token = crypto.encrypt(this.auth.google.params.access_token);
|
|
273
|
+
// if (this.auth.google.params.id_token) this.auth.google.params.id_token = crypto.encrypt(this.auth.google.params.id_token);
|
|
274
|
+
// }
|
|
275
|
+
// }
|
|
276
|
+
// }
|
|
277
|
+
//
|
|
278
|
+
// next();
|
|
279
|
+
//
|
|
280
|
+
//});
|
|
281
|
+
//
|
|
282
|
+
//Account.post('init', function() {
|
|
283
|
+
//
|
|
284
|
+
// // Decrypt sensitive data
|
|
285
|
+
//
|
|
286
|
+
// if (this.auth) {
|
|
287
|
+
// if (this.auth.google) {
|
|
288
|
+
// if (this.auth.google.accessToken) this.auth.google.accessToken = crypto.decrypt(this.auth.google.accessToken);
|
|
289
|
+
// if (this.auth.google.refreshToken) this.auth.google.refreshToken = crypto.decrypt(this.auth.google.refreshToken);
|
|
290
|
+
// if (this.auth.google.params) {
|
|
291
|
+
// if (this.auth.google.params.access_token) this.auth.google.params.access_token = crypto.decrypt(this.auth.google.params.access_token);
|
|
292
|
+
// if (this.auth.google.params.id_token) this.auth.google.params.id_token = crypto.decrypt(this.auth.google.params.id_token);
|
|
293
|
+
// }
|
|
294
|
+
// }
|
|
295
|
+
// }
|
|
296
|
+
//
|
|
297
|
+
//});
|
|
298
|
+
|
|
299
|
+
Account.post('remove', function(doc) {
|
|
300
|
+
// CalendarEvent.account
|
|
301
|
+
// Person.account
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
Account.set('autoIndex', true);
|
|
305
|
+
Account.on('index', function(err) { console.log('error building account indexes: ' + err); });
|
|
306
|
+
|
|
307
|
+
var deepPopulate = require('mongoose-deep-populate')(mongoose);
|
|
308
|
+
Account.plugin(deepPopulate);
|
|
309
|
+
|
|
310
|
+
mongoose.model('Account', Account);
|
|
311
|
+
|
|
312
|
+
};
|
package/lib/Activity.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
module.exports = function(mongoose, config) {
|
|
4
|
+
|
|
5
|
+
var
|
|
6
|
+
|
|
7
|
+
Schema = mongoose.Schema,
|
|
8
|
+
env = process.env.NODE_ENV || 'development',
|
|
9
|
+
Geoip = require('@dhyasama/ffvc-geoip'),
|
|
10
|
+
geoip = new Geoip(),
|
|
11
|
+
async = require('async'),
|
|
12
|
+
rangeCheck = require('range_check');
|
|
13
|
+
|
|
14
|
+
var Activity = new Schema({
|
|
15
|
+
|
|
16
|
+
username: { type: String, required: true, index: true },
|
|
17
|
+
|
|
18
|
+
items: [{
|
|
19
|
+
|
|
20
|
+
type: {
|
|
21
|
+
type: String,
|
|
22
|
+
required: true,
|
|
23
|
+
enum: [
|
|
24
|
+
'SETUP ACCOUNT',
|
|
25
|
+
'LOGIN SUCCESS',
|
|
26
|
+
'LOGIN FAILURE',
|
|
27
|
+
'NEW DEVICE',
|
|
28
|
+
'PASSWORD RESET',
|
|
29
|
+
'PASSWORD CHANGE',
|
|
30
|
+
'DOCUMENT DOWNLOAD',
|
|
31
|
+
'ACCOUNT CHANGE'
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
timestamp: { type: Date },
|
|
36
|
+
|
|
37
|
+
remoteAddress: { type: String },
|
|
38
|
+
|
|
39
|
+
geolocation: {},
|
|
40
|
+
|
|
41
|
+
document: {
|
|
42
|
+
url: { type: String },
|
|
43
|
+
filename: { type: String }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
}]
|
|
47
|
+
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
Activity.statics.add = function(username, type, remoteAddress, url, filename, callback) {
|
|
53
|
+
|
|
54
|
+
var self = this;
|
|
55
|
+
|
|
56
|
+
if (!username) throw new Error('username is required');
|
|
57
|
+
if (!type) throw new Error('type is required');
|
|
58
|
+
if (!callback) throw new Error('callback is required');
|
|
59
|
+
|
|
60
|
+
username = username.toLowerCase();
|
|
61
|
+
|
|
62
|
+
var getActivity = function(cb) {
|
|
63
|
+
|
|
64
|
+
self.findOne({ 'username': username }, function(err, activity) {
|
|
65
|
+
|
|
66
|
+
if (err) return cb(err, null);
|
|
67
|
+
|
|
68
|
+
if (!activity) {
|
|
69
|
+
var A = mongoose.model('Activity');
|
|
70
|
+
activity = new A();
|
|
71
|
+
activity.username = username;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return cb(null, activity);
|
|
75
|
+
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
var getGeolocation = function(cb) {
|
|
81
|
+
if (!rangeCheck.validIp(remoteAddress)) return cb(null, {});
|
|
82
|
+
geoip.locate(remoteAddress, cb);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
var asyncFinally = function(err, results) {
|
|
86
|
+
|
|
87
|
+
var activity = results ? results[0] : {};
|
|
88
|
+
var geolocation = results ? results[1] : {};
|
|
89
|
+
|
|
90
|
+
var item = {
|
|
91
|
+
type: type,
|
|
92
|
+
timestamp: new Date(),
|
|
93
|
+
geolocation: geolocation
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
if (url) {
|
|
97
|
+
item.document = {
|
|
98
|
+
url: url,
|
|
99
|
+
filename: filename
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (activity) {
|
|
104
|
+
activity.items.unshift(item);
|
|
105
|
+
activity.save(function(err, result) {
|
|
106
|
+
return callback(err, item);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
return callback(null, null);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
async.parallel([getActivity, getGeolocation], asyncFinally);
|
|
116
|
+
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
Activity.statics.forMessage = function(username, messageId, cb) {
|
|
120
|
+
this.find({ 'username': username.toLowerCase(), 'items.document.messageId': messageId }, cb);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
Activity.statics.list = function(username, maxItems, cb) {
|
|
124
|
+
|
|
125
|
+
var query = this.findOne({ 'username': username.toLowerCase() });
|
|
126
|
+
|
|
127
|
+
if (maxItems && maxItems >= 1) { query = query.slice('items', maxItems); }
|
|
128
|
+
|
|
129
|
+
query.exec(cb);
|
|
130
|
+
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
Activity.set('autoIndex', true);
|
|
134
|
+
Activity.on('index', function(err) { console.log('error building activity indexes: ' + err); });
|
|
135
|
+
|
|
136
|
+
mongoose.model('Activity', Activity);
|
|
137
|
+
|
|
138
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
module.exports = function(mongoose, config) {
|
|
4
|
+
|
|
5
|
+
var
|
|
6
|
+
|
|
7
|
+
Schema = mongoose.Schema,
|
|
8
|
+
env = process.env.NODE_ENV || 'development',
|
|
9
|
+
_ = require('underscore');
|
|
10
|
+
|
|
11
|
+
var CalendarEvent = new Schema({
|
|
12
|
+
|
|
13
|
+
account: { type: Schema.ObjectId, ref: 'Account', index: true, required: true },
|
|
14
|
+
|
|
15
|
+
email: { type: String, index: true, required: true },
|
|
16
|
+
|
|
17
|
+
provider: { type: String, index: false, required: true },
|
|
18
|
+
|
|
19
|
+
eventId: { type: String, index: true, required: true },
|
|
20
|
+
|
|
21
|
+
status: { type: String, index: true, required: true },
|
|
22
|
+
|
|
23
|
+
summary: { type: String, index: false, required: true },
|
|
24
|
+
|
|
25
|
+
start: { type: Date, index: false, required: true },
|
|
26
|
+
|
|
27
|
+
end: { type: Date, index: false, required: true },
|
|
28
|
+
|
|
29
|
+
attendees: [{
|
|
30
|
+
|
|
31
|
+
email: { type: String, index: true, required: true },
|
|
32
|
+
|
|
33
|
+
displayName: { type: String, index: true, required: true }
|
|
34
|
+
|
|
35
|
+
}],
|
|
36
|
+
|
|
37
|
+
allInternal: { type: Boolean, index: true, required: true },
|
|
38
|
+
|
|
39
|
+
processed: { type: Boolean, index: true, required: true },
|
|
40
|
+
|
|
41
|
+
createdOn: { type: Date, index: false, required: true },
|
|
42
|
+
|
|
43
|
+
updatedOn: { type: Date, index: false, required: true },
|
|
44
|
+
|
|
45
|
+
importTransactionId: { type: String, index: true, required: false },
|
|
46
|
+
|
|
47
|
+
raw: { type: String, index: false, required: true }
|
|
48
|
+
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
CalendarEvent.virtual('original').get(function () {
|
|
52
|
+
return JSON.parse(this.raw);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
CalendarEvent.statics.findByAccountId = function (accountId, cb) {
|
|
56
|
+
this
|
|
57
|
+
.find({ account: accountId })
|
|
58
|
+
.exec(cb);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
CalendarEvent.statics.findByCreatedRange = function (since, until, cb) {
|
|
62
|
+
this
|
|
63
|
+
.find({ createdOn: { $gt: since, $lt: since } })
|
|
64
|
+
.exec(cb);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
CalendarEvent.statics.findByCreatedSince = function (since, cb) {
|
|
68
|
+
this
|
|
69
|
+
.find({ createdOn: { $gt: since } })
|
|
70
|
+
.exec(cb);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
CalendarEvent.statics.findByEmail = function (email, cb) {
|
|
74
|
+
this
|
|
75
|
+
.find({ email: email })
|
|
76
|
+
.exec(cb);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
CalendarEvent.statics.findByEventId = function (eventId, cb) {
|
|
80
|
+
this
|
|
81
|
+
.findOne({ 'eventId': eventId })
|
|
82
|
+
.exec(cb);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
CalendarEvent.statics.findUnprocessedForAccount = function (id, cb) {
|
|
86
|
+
this
|
|
87
|
+
.find({ account: id, processed: false })
|
|
88
|
+
.populate('account', 'email customer')
|
|
89
|
+
.exec(cb);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
CalendarEvent.statics.findByUsername = function (username, cb) {
|
|
93
|
+
this
|
|
94
|
+
.find({ 'account.username': username })
|
|
95
|
+
.exec(cb);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
CalendarEvent.statics.getById = function (id, cb) {
|
|
99
|
+
this
|
|
100
|
+
.findOne({ '_id': id })
|
|
101
|
+
.populate('account', 'email customer')
|
|
102
|
+
.exec(cb);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
CalendarEvent.statics.getNewest = function (email, cb) {
|
|
106
|
+
this
|
|
107
|
+
.findOne({email: email})
|
|
108
|
+
.sort({createdOn:-1})
|
|
109
|
+
.exec(cb);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
CalendarEvent.statics.upsert = function(calendarEvent, cb) {
|
|
113
|
+
|
|
114
|
+
var self = this;
|
|
115
|
+
|
|
116
|
+
// Prevent dupe events
|
|
117
|
+
|
|
118
|
+
var query = { 'eventId': calendarEvent.eventId };
|
|
119
|
+
var options = { 'upsert': true, 'new': true };
|
|
120
|
+
var update = {
|
|
121
|
+
account: calendarEvent.account,
|
|
122
|
+
email: calendarEvent.email,
|
|
123
|
+
provider: calendarEvent.provider,
|
|
124
|
+
eventId: calendarEvent.eventId,
|
|
125
|
+
status: calendarEvent.status,
|
|
126
|
+
summary: calendarEvent.summary,
|
|
127
|
+
start: calendarEvent.start,
|
|
128
|
+
end: calendarEvent.end,
|
|
129
|
+
attendees: calendarEvent.attendees,
|
|
130
|
+
allInternal: calendarEvent.allInternal,
|
|
131
|
+
processed: calendarEvent.processed,
|
|
132
|
+
createdOn: calendarEvent.createdOn,
|
|
133
|
+
updatedOn: calendarEvent.updatedOn,
|
|
134
|
+
raw: calendarEvent.raw
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
self.findOneAndUpdate(query, update, options, cb);
|
|
138
|
+
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
CalendarEvent.post('remove', function(doc) {
|
|
142
|
+
// Person.calendarEventId
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
CalendarEvent.set('autoIndex', true);
|
|
146
|
+
CalendarEvent.on('index', function(err) { console.log('error building calendar event indexes: ' + err); });
|
|
147
|
+
|
|
148
|
+
mongoose.model('CalendarEvent', CalendarEvent);
|
|
149
|
+
|
|
150
|
+
};
|