Added web and email notifications. Tests. Fire page
This commit is contained in:
parent
df05b33e82
commit
3bf21c8caf
32 changed files with 696 additions and 88 deletions
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import { Mongo } from 'meteor/mongo';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import firesCommonSchema from '../Common/FiresSchema';
|
||||
|
||||
const ActiveFires = new Mongo.Collection('activefires', { idGeneration: 'MONGO' });
|
||||
|
||||
|
|
@ -17,6 +18,13 @@ ActiveFires.deny({
|
|||
remove: () => true
|
||||
});
|
||||
|
||||
|
||||
ActiveFires.schema = new SimpleSchema(firesCommonSchema);
|
||||
|
||||
ActiveFires.attachSchema(ActiveFires.schema);
|
||||
|
||||
export default ActiveFires;
|
||||
|
||||
/* Sample:
|
||||
* {
|
||||
* "_id" : ObjectId("5a208d7579095a1adba48191"),
|
||||
|
|
@ -46,19 +54,3 @@ ActiveFires.deny({
|
|||
* "createdAt" : ISODate("2017-11-30T08:07:33.720Z")
|
||||
* }
|
||||
* */
|
||||
|
||||
ActiveFires.schema = new SimpleSchema({
|
||||
lat: Number,
|
||||
lon: Number,
|
||||
scan: Number,
|
||||
type: String,
|
||||
acq_date: String,
|
||||
acq_time: String,
|
||||
when: Date,
|
||||
createdAt: Date,
|
||||
updatedAt: Date
|
||||
});
|
||||
|
||||
ActiveFires.attachSchema(ActiveFires.schema);
|
||||
|
||||
export default ActiveFires;
|
||||
|
|
|
|||
29
imports/api/Common/FiresSchema.js
Normal file
29
imports/api/Common/FiresSchema.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* eslint-disable import/no-absolute-path */
|
||||
|
||||
import { defaultCreatedAt, defaultUpdateAt } from '/imports/api/Utility/Utils.js';
|
||||
import LocationSchema from '/imports/api/Utility/LocationSchema.js';
|
||||
|
||||
const firesCommonSchema = {
|
||||
ourid: LocationSchema,
|
||||
lat: Number,
|
||||
lon: Number,
|
||||
scan: Number,
|
||||
type: String,
|
||||
when: Date,
|
||||
track: { type: Number, optional: true },
|
||||
acq_date: { type: String, optional: true },
|
||||
acq_time: { type: String, optional: true },
|
||||
satellite: { type: String, optional: true },
|
||||
confidence: { type: Number, optional: true },
|
||||
version: { type: String, optional: true },
|
||||
frp: { type: Number, optional: true },
|
||||
daynight: { type: String, optional: true },
|
||||
brightness: { type: Number, optional: true },
|
||||
bright_t31: { type: Number, optional: true },
|
||||
bright_ti4: { type: Number, optional: true },
|
||||
bright_ti5: { type: Number, optional: true },
|
||||
createdAt: defaultCreatedAt,
|
||||
updatedAt: defaultUpdateAt
|
||||
};
|
||||
|
||||
export default firesCommonSchema;
|
||||
25
imports/api/Fires/Fires.js
Normal file
25
imports/api/Fires/Fires.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/* eslint-disable consistent-return */
|
||||
|
||||
import { Mongo } from 'meteor/mongo';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import firesCommonSchema from '../Common/FiresSchema';
|
||||
|
||||
const Fires = new Mongo.Collection('fires', { idGeneration: 'MONGO' });
|
||||
|
||||
Fires.allow({
|
||||
insert: () => false,
|
||||
update: () => false,
|
||||
remove: () => false
|
||||
});
|
||||
|
||||
Fires.deny({
|
||||
insert: () => true,
|
||||
update: () => true,
|
||||
remove: () => true
|
||||
});
|
||||
|
||||
Fires.schema = new SimpleSchema(firesCommonSchema);
|
||||
|
||||
Fires.attachSchema(Fires.schema);
|
||||
|
||||
export default Fires;
|
||||
22
imports/api/Fires/methods.js
Normal file
22
imports/api/Fires/methods.js
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/* eslint-disable import/no-absolute-path */
|
||||
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { check } from 'meteor/check';
|
||||
import rateLimit from '/imports/modules/rate-limit';
|
||||
import urlEnc from '/imports/modules/url-encode';
|
||||
|
||||
Meteor.methods({
|
||||
'fire.decrypt': async function fireDecode(fireEnc) {
|
||||
check(fireEnc, String);
|
||||
const unsealed = await urlEnc.decrypt(fireEnc);
|
||||
return unsealed;
|
||||
}
|
||||
});
|
||||
|
||||
rateLimit({
|
||||
methods: [
|
||||
'fire.decode'
|
||||
],
|
||||
limit: 5,
|
||||
timeRange: 1000
|
||||
});
|
||||
37
imports/api/Fires/server/publications.js
Normal file
37
imports/api/Fires/server/publications.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* eslint-disable import/no-absolute-path */
|
||||
/* eslint-disable prefer-arrow-callback */
|
||||
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { check } from 'meteor/check';
|
||||
import urlEnc from '/imports/modules/url-encode';
|
||||
import { Promise } from 'meteor/promise';
|
||||
import FiresCollection from '../Fires';
|
||||
|
||||
function findFire(unsealed) {
|
||||
const fire = FiresCollection.find({ ourid: { type: 'Point', coordinates: [unsealed.lon, unsealed.lat] } });
|
||||
return fire;
|
||||
}
|
||||
|
||||
Meteor.publish('fireFromHash', function fireFromHash(fireEnc) {
|
||||
check(fireEnc, String);
|
||||
try {
|
||||
// console.log(fireEnc);
|
||||
const unsealed = Promise.await(urlEnc.decrypt(fireEnc));
|
||||
const w = unsealed.when;
|
||||
// console.log(w);
|
||||
unsealed.when = new Date(w);
|
||||
// console.log(unsealed);
|
||||
FiresCollection.schema.validate(unsealed);
|
||||
const fire = findFire(unsealed);
|
||||
if (fire.count() === 0) {
|
||||
const result = FiresCollection.upsert({ ourid: unsealed.ourid }, { $set: unsealed }, { multi: false, upsert: true });
|
||||
console.log(JSON.stringify(result));
|
||||
}
|
||||
return findFire(unsealed);
|
||||
/* console.log(`fires: ${fire.count()}`);
|
||||
* return fire; */
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw new Meteor.Error('500', e);
|
||||
}
|
||||
});
|
||||
39
imports/api/Notifications/Notifications.js
Normal file
39
imports/api/Notifications/Notifications.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/* eslint-disable consistent-return */
|
||||
/* eslint-disable import/no-absolute-path */
|
||||
|
||||
import { Mongo } from 'meteor/mongo';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { defaultCreatedAt, defaultUpdateAt } from '/imports/api/Utility/Utils.js';
|
||||
import LocationSchema from '/imports/api/Utility/LocationSchema.js';
|
||||
|
||||
const Notifications = new Mongo.Collection('notifications', { idGeneration: 'MONGO' });
|
||||
|
||||
Notifications.allow({
|
||||
insert: () => false,
|
||||
update: () => false,
|
||||
remove: () => false
|
||||
});
|
||||
|
||||
Notifications.deny({
|
||||
insert: () => true,
|
||||
update: () => true,
|
||||
remove: () => true
|
||||
});
|
||||
|
||||
Notifications.schema = new SimpleSchema({
|
||||
userId: String,
|
||||
content: String,
|
||||
geo: LocationSchema,
|
||||
type: String,
|
||||
webNotified: { type: Boolean, optional: true },
|
||||
webNotifiedAt: { type: Date, optional: true },
|
||||
emailNotified: { type: Boolean, optional: true },
|
||||
emailNotifiedAt: { type: Date, optional: true },
|
||||
when: Date,
|
||||
createdAt: defaultCreatedAt,
|
||||
updatedAt: defaultUpdateAt
|
||||
});
|
||||
|
||||
Notifications.attachSchema(Notifications.schema);
|
||||
|
||||
export default Notifications;
|
||||
25
imports/api/Notifications/methods.js
Normal file
25
imports/api/Notifications/methods.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { Meteor } from 'meteor/meteor';
|
||||
import { check } from 'meteor/check';
|
||||
import Notifications from './Notifications';
|
||||
import rateLimit from '../../modules/rate-limit';
|
||||
|
||||
Meteor.methods({
|
||||
'notifications.sent': function notificationsUpdate(notifId) {
|
||||
check(notifId, Meteor.Collection.ObjectID);
|
||||
|
||||
try {
|
||||
Notifications.update(notifId, { $set: { webNotified: true, webNotifiedAt: new Date() } });
|
||||
return notifId;
|
||||
} catch (exception) {
|
||||
throw new Meteor.Error('500', exception);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
rateLimit({
|
||||
methods: [
|
||||
'notifications.sent'
|
||||
],
|
||||
limit: 5,
|
||||
timeRange: 1000
|
||||
});
|
||||
10
imports/api/Notifications/server/publications.js
Normal file
10
imports/api/Notifications/server/publications.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/* eslint-disable prefer-arrow-callback */
|
||||
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import Notifications from '../Notifications';
|
||||
|
||||
Meteor.publish('mynotifications', function notifications() {
|
||||
const notif = Notifications.find({ userId: this.userId, type: 'web', webNotified: null });
|
||||
console.log(`Notifications for user ${this.userId}: ${notif.count()}`);
|
||||
return notif;
|
||||
});
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
import { Mongo } from 'meteor/mongo';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { defaultCreatedAt, defaultUpdateAt } from '/imports/api/Utility/Utils.js';
|
||||
import LocationSchema from '/imports/api/Utility/LocationSchema.js';
|
||||
|
||||
const Subscriptions = new Mongo.Collection('subscriptions', { idGeneration: 'MONGO' });
|
||||
|
||||
|
|
@ -39,36 +40,6 @@ Subscriptions.deny({
|
|||
* }
|
||||
* */
|
||||
|
||||
// https://stackoverflow.com/questions/24492333/meteor-simple-schema-for-mongo-geo-location-data
|
||||
// https://github.com/aldeed/meteor-simple-schema/issues/606
|
||||
const LocationSchema = new SimpleSchema({
|
||||
type: {
|
||||
type: String,
|
||||
allowedValues: ['Point']
|
||||
},
|
||||
coordinates: {
|
||||
type: Array,
|
||||
minCount: 2,
|
||||
maxCount: 2,
|
||||
custom: function custom() {
|
||||
if (!(this.value[0] >= -90 && this.value[0] <= 90)) {
|
||||
return 'lngOutOfRange';
|
||||
}
|
||||
if (!(this.value[1] >= -180 && this.value[1] <= 180)) {
|
||||
return 'latOutOfRange';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
'coordinates.$': {
|
||||
type: Number
|
||||
}
|
||||
});
|
||||
|
||||
LocationSchema.messageBox.messages({
|
||||
lonOutOfRange: '[label] longitude should be between -90 and 90',
|
||||
latOutOfRange: '[label] latitude should be between -180 and 180'
|
||||
});
|
||||
|
||||
Subscriptions.schema = new SimpleSchema({
|
||||
location: Object,
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ export default (options, user) => {
|
|||
templateVars: {
|
||||
applicationName,
|
||||
firstName,
|
||||
welcomeUrl: Meteor.absoluteUrl('documents'), // e.g., returns http://localhost:3000/documents
|
||||
},
|
||||
welcomeUrl: Meteor.absoluteUrl('subscriptions') // e.g., returns http://localhost:3000/documents
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
throw new Meteor.Error('500', `${error}`);
|
||||
});
|
||||
.catch((error) => {
|
||||
throw new Meteor.Error('500', `${error}`);
|
||||
});
|
||||
};
|
||||
|
|
|
|||
34
imports/api/Utility/LocationSchema.js
Normal file
34
imports/api/Utility/LocationSchema.js
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import SimpleSchema from 'simpl-schema';
|
||||
|
||||
// https://stackoverflow.com/questions/24492333/meteor-simple-schema-for-mongo-geo-location-data
|
||||
// https://github.com/aldeed/meteor-simple-schema/issues/606
|
||||
const LocationSchema = new SimpleSchema({
|
||||
type: {
|
||||
type: String,
|
||||
allowedValues: ['Point']
|
||||
},
|
||||
coordinates: {
|
||||
type: Array,
|
||||
minCount: 2,
|
||||
maxCount: 2,
|
||||
custom: function custom() {
|
||||
if (!(this.value[0] >= -90 && this.value[0] <= 90)) {
|
||||
return 'lngOutOfRange';
|
||||
}
|
||||
if (!(this.value[1] >= -180 && this.value[1] <= 180)) {
|
||||
return 'latOutOfRange';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
'coordinates.$': {
|
||||
type: Number
|
||||
}
|
||||
});
|
||||
|
||||
LocationSchema.messageBox.messages({
|
||||
lonOutOfRange: '[label] longitude should be between -90 and 90',
|
||||
latOutOfRange: '[label] latitude should be between -180 and 180'
|
||||
});
|
||||
|
||||
export default LocationSchema;
|
||||
Loading…
Add table
Add a link
Reference in a new issue