Initial commit. Modified the Meteor todos app to create the Petit Teton data tracking app. Has working data for sales. Requires a Mongo database.
This commit is contained in:
130
imports/api/Measure.js
Normal file
130
imports/api/Measure.js
Normal file
@@ -0,0 +1,130 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { Mongo } from 'meteor/mongo';
|
||||
import { check } from 'meteor/check';
|
||||
import {SimpleSchema} from 'meteor/aldeed:simple-schema';
|
||||
|
||||
Measures = new Mongo.Collection('Measures');
|
||||
Measures.attachSchema(new SimpleSchema({
|
||||
name: {
|
||||
type: String,
|
||||
label: "Name",
|
||||
optional: false,
|
||||
trim: true,
|
||||
index: 1,
|
||||
unique: true
|
||||
},
|
||||
postfix: {
|
||||
type: String,
|
||||
label: "Postfix",
|
||||
optional: true, //Note: Each does not have a postfix.
|
||||
trim: true
|
||||
},
|
||||
order: {
|
||||
type: SimpleSchema.Integer,
|
||||
label: "Order",
|
||||
optional: false,
|
||||
min: 0
|
||||
},
|
||||
createdAt: { //Force the value to the current date on the server.
|
||||
type: Date,
|
||||
label: "Created On",
|
||||
// autoValue: function() { //The disadvantage of autoValue is that it sets the date after insertion, causing the UI to update twice - once where the item has no date, and then again where the date is set. Sorted lists will cause the item to bounce around.
|
||||
// if(this.isInsert) return new Date();
|
||||
// else if(this.isUpsert) return {$setOnInsert: new Date()};
|
||||
// else this.unset();
|
||||
// },
|
||||
// denyUpdate: true,
|
||||
optional: false
|
||||
},
|
||||
updatedAt: {
|
||||
type: Date,
|
||||
label: "Updated On",
|
||||
// autoValue: function() {
|
||||
// if(this.isUpdate) return new Date();
|
||||
// },
|
||||
// denyInsert: true,
|
||||
optional: true
|
||||
},
|
||||
deletedAt: {
|
||||
type: Date,
|
||||
label: "Deleted On",
|
||||
optional: true
|
||||
},
|
||||
deletedBy: {
|
||||
type: String,
|
||||
label: "Deleted By",
|
||||
optional: true
|
||||
},
|
||||
restoredAt: {
|
||||
type: Date,
|
||||
label: "Restored On",
|
||||
optional: true
|
||||
},
|
||||
restoredBy: {
|
||||
type: String,
|
||||
label: "Restored By",
|
||||
optional: true
|
||||
}
|
||||
}));
|
||||
|
||||
//https://github.com/zimme/meteor-collection-softremovable
|
||||
Measures.attachBehaviour("softRemovable", {
|
||||
removed: 'deleted',
|
||||
removedAt: 'deletedAt',
|
||||
removedBy: 'removedBy',
|
||||
restoredAt: 'restoredAt',
|
||||
restoredBy: 'restoredBy'
|
||||
});
|
||||
|
||||
if(Meteor.isServer) Meteor.publish('measures', function() {
|
||||
return Measures.find({});
|
||||
});
|
||||
|
||||
// Requires: meteor add matb33:collection-hooks
|
||||
Measures.before.insert(function(userId, doc) {
|
||||
// check(userId, String);
|
||||
doc.createdAt = new Date();
|
||||
});
|
||||
Measures.before.update(function(userId, doc, fieldNames, modifier, options) {
|
||||
modifier.$set = modifier.$set || {}; //Make sure there is an object.
|
||||
modifier.$set.updatedAt = new Date();
|
||||
});
|
||||
|
||||
if(Meteor.isServer) {
|
||||
Meteor.methods({
|
||||
insertMeasure: function(measure) {
|
||||
check(measure, {
|
||||
name: String,
|
||||
order: Number,
|
||||
postfix: String
|
||||
});
|
||||
|
||||
measure.createdAt = new Date();
|
||||
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||
Measures.insert(measure);
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized.");
|
||||
},
|
||||
deleteMeasure: function(id) {
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||
Measures.remove(id);
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized.");
|
||||
},
|
||||
updateMeasure: function(measure) {
|
||||
check(measure, {
|
||||
name: String,
|
||||
order: Number,
|
||||
postfix: String
|
||||
});
|
||||
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||
Products.update(id, {$set: {name: measure.name, order: measure.order, postfix: measure.postfix, updateAt: new Date()}});
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default Measures;
|
||||
268
imports/api/Product.js
Normal file
268
imports/api/Product.js
Normal file
@@ -0,0 +1,268 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { Mongo } from 'meteor/mongo';
|
||||
import { check } from 'meteor/check';
|
||||
import {SimpleSchema} from 'meteor/aldeed:simple-schema';
|
||||
|
||||
Products = new Mongo.Collection('Products');
|
||||
|
||||
const ProductsSchema = new SimpleSchema({
|
||||
name: {
|
||||
type: String,
|
||||
label: "Name",
|
||||
optional: false,
|
||||
trim: true,
|
||||
index: 1,
|
||||
unique: true
|
||||
},
|
||||
tags: { //An array of ProductTag names. Note that we are not using the ProductTag ID's because I want a looser connection (if a ProductTag is deleted, it isn't a big deal if it isn't maintained in the Product records).
|
||||
type: [String],
|
||||
label: "Tags",
|
||||
optional: false,
|
||||
defaultValue: []
|
||||
},
|
||||
measures: { //A JSON array of Measure ID's.
|
||||
type: Array,
|
||||
label: "Measures",
|
||||
optional: false,
|
||||
defaultValue: []
|
||||
},
|
||||
'measures.$': {
|
||||
type: String,
|
||||
label: "Measure ID",
|
||||
regEx: SimpleSchema.RegEx.Id
|
||||
},
|
||||
aliases: { //A JSON array of alternate names.
|
||||
type: Array,
|
||||
label: "Aliases",
|
||||
optional: false,
|
||||
defaultValue: []
|
||||
},
|
||||
'aliases.$': {
|
||||
type: String
|
||||
},
|
||||
prices: { //A JSON object mapping Measure ID's to price data for this item. Price data is an object with price, effectiveDate, previousPrice. Example: prices: {XZ5Z3CM49NDrJNADA: {price: 10.5, effectiveDate: ISODate("2017-01-12T13:14:18.876-08:00"), previousPrice: 9}, ...}
|
||||
type: Object,
|
||||
//blackbox: true,
|
||||
custom: function() {
|
||||
console.log("In custom validation for prices");
|
||||
return true;
|
||||
// if(this.value != undefined) {
|
||||
// console.log(this.value);
|
||||
// //check(this, Object);
|
||||
// if(!_.isObject(this.value)) {
|
||||
// return "expectObject";
|
||||
// }
|
||||
//
|
||||
// for(let measureId of _.allKeys(this.value)) {
|
||||
// //check(measureId, String); //Should be a Mongo ID
|
||||
// if(!_.isString(measureId)) {
|
||||
// console.log("Expected a Mongo ID as attribute names of the Product.prices object.");
|
||||
// return "expectString";
|
||||
// }
|
||||
// //check(this.value[measureId], Object);
|
||||
// let measureData = this.value[measureId];
|
||||
// if(!_.isObject(measureData)) {
|
||||
// console.log("Expected an Object containing price, and optionally (previousPrice & effectiveDate).");
|
||||
// return "expectObject";
|
||||
// }
|
||||
//
|
||||
// if(_.has(measureData, "price")) {
|
||||
// //check(measureData.price, Number);
|
||||
// if(!_.isNumber(measureData.price)) {
|
||||
// console.log("Expected a Number for 'price'.");
|
||||
// return "expectNumber";
|
||||
// }
|
||||
//
|
||||
// //If previous price exists then it must be a number, and effective date must exist and be a date.
|
||||
// if(_.has(measureData, "previousPrice")) {
|
||||
// //check(measureData.effectiveDate, Date);
|
||||
// if(!_.isDate(measureData.effectiveDate)) {
|
||||
// console.log("Expected a Date for 'effectiveDate'.");
|
||||
// return "expectDate";
|
||||
// }
|
||||
// //check(measureData.previousPrice, Number);
|
||||
// if(!_.isDate(measureData.previousPrice)) {
|
||||
// console.log("Expected a Number for 'previousPrice'.");
|
||||
// return "expectNumber";
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// //check(measureData.effectiveDate, undefined);
|
||||
// if(_.isSet(measureData.effectiveDate)) {
|
||||
// console.log("Expected 'effectiveDate' to be undefined.");
|
||||
// return "notAllowed";
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// //check(measureData.effectiveDate, undefined);
|
||||
// if(_.isSet(measureData.effectiveDate)) {
|
||||
// console.log("Expected 'effectiveDate' to be undefined.");
|
||||
// return "notAllowed";
|
||||
// }
|
||||
// //check(measureData.previousPrice, undefined);
|
||||
// if(_.isSet(measureData.previousPrice)) {
|
||||
// console.log("Expected 'previousPrice' to be undefined.");
|
||||
// return "notAllowed";
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
},
|
||||
createdAt: {
|
||||
type: Date,
|
||||
label: "Created On",
|
||||
optional: false
|
||||
},
|
||||
updatedAt: {
|
||||
type: Date,
|
||||
label: "Updated On",
|
||||
optional: true
|
||||
},
|
||||
deletedAt: {
|
||||
type: Date,
|
||||
label: "Deleted On",
|
||||
optional: true
|
||||
},
|
||||
deletedBy: {
|
||||
type: String,
|
||||
label: "Deleted By",
|
||||
optional: true
|
||||
},
|
||||
restoredAt: {
|
||||
type: Date,
|
||||
label: "Restored On",
|
||||
optional: true
|
||||
},
|
||||
restoredBy: {
|
||||
type: String,
|
||||
label: "Restored By",
|
||||
optional: true
|
||||
}
|
||||
});
|
||||
|
||||
//Note: Could not figure out how to setup a schema for prices where prices is an object whose keys are id's of measures, and whose values are objects containing price data (see comments above for more details on price data).
|
||||
// 'prices.$': {
|
||||
// type: new SimpleSchema({
|
||||
// measureId: {
|
||||
// type: String,
|
||||
// label: "Measure Id",
|
||||
// trim: false,
|
||||
// regEx: SimpleSchema.RegEx.Id,
|
||||
// optional: false
|
||||
// },
|
||||
// price: {
|
||||
// type: Number,
|
||||
// label: "Price",
|
||||
// min: 0,
|
||||
// exclusiveMin: true,
|
||||
// optional: false
|
||||
// }
|
||||
// })
|
||||
// },
|
||||
|
||||
//Products.attachSchema(ProductsSchema);
|
||||
|
||||
//https://github.com/zimme/meteor-collection-softremovable
|
||||
Products.attachBehaviour("softRemovable", {
|
||||
removed: 'deleted',
|
||||
removedAt: 'deletedAt',
|
||||
removedBy: 'removedBy',
|
||||
restoredAt: 'restoredAt',
|
||||
restoredBy: 'restoredBy'
|
||||
});
|
||||
|
||||
if(Meteor.isServer) {
|
||||
Meteor.publish('products', function() {
|
||||
// let dbQuery = {};
|
||||
//
|
||||
// if(query) {
|
||||
// _.each(_.keys(query), function(key) {
|
||||
// if(_.isObject(query[key])) dbQuery[key] = query[key];
|
||||
// else if(_.isNumber(query[key])) dbQuery[key] = query[key];
|
||||
// else dbQuery[key] = {$regex: RegExp.escape(query[key]), $options: 'i'};
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// return Products.find(dbQuery, {sort: {date: -1}});
|
||||
return Products.find({}, {sort: {name: 1}});
|
||||
});
|
||||
|
||||
Meteor.methods({
|
||||
insertProduct: function(product) {
|
||||
check(product, {
|
||||
name: String
|
||||
});
|
||||
|
||||
product.createdAt = new Date();
|
||||
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||
Products.insert(product, function(err, id) {
|
||||
if(err) console.log(err);
|
||||
});
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized.");
|
||||
},
|
||||
deleteProduct: function(id) {
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||
Products.remove(id);
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized.");
|
||||
},
|
||||
updateProduct: function(product) {
|
||||
check(product, {
|
||||
_id: String,
|
||||
name: String
|
||||
});
|
||||
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||
Products.update(id, {$set: {name: product.name, updateAt: new Date()}});
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized.");
|
||||
},
|
||||
setProductPrice: function(productId, measureId, price, setPrevious, effectiveDate) {
|
||||
check(productId, String);
|
||||
check(measureId, String);
|
||||
check(price, Number);
|
||||
check(setPrevious, Boolean);
|
||||
check(effectiveDate, Date);
|
||||
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||
let product = Products.findOne(productId, {fields: {prices: 1}});
|
||||
|
||||
if(product) {
|
||||
console.log("Product: " + JSON.stringify(product));
|
||||
let prices = product.prices ? product.prices : {};
|
||||
let measurePriceData = prices[measureId];
|
||||
|
||||
if(!measurePriceData) {
|
||||
measurePriceData = {};
|
||||
prices[measureId] = measurePriceData;
|
||||
}
|
||||
console.log("Old Price Data: " + JSON.stringify(prices));
|
||||
|
||||
if(setPrevious && measurePriceData.price) {
|
||||
measurePriceData.previousPrice = measurePriceData.price;
|
||||
measurePriceData.effectiveDate = effectiveDate;
|
||||
}
|
||||
|
||||
measurePriceData.price = price;
|
||||
console.log("New Price Data: " + JSON.stringify(prices));
|
||||
//console.log(ProductsSchema.validate(product));
|
||||
//
|
||||
// check(prices, ProductsSchema);
|
||||
if(ProductsSchema.newContext().isValid()) {
|
||||
console.log("Valid schema for product");
|
||||
Products.update(productId, {$set: {prices: prices, updateAt: new Date()}}, {validate: false, bypassCollection2: true});
|
||||
}
|
||||
else console.log("Invalid schema for product");
|
||||
}
|
||||
else throw new Meteor.ERROR(400, "Could not find the requested product: " + productId);
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default Products;
|
||||
74
imports/api/ProductTag.js
Normal file
74
imports/api/ProductTag.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { Mongo } from 'meteor/mongo';
|
||||
import { check } from 'meteor/check'
|
||||
import {SimpleSchema} from 'meteor/aldeed:simple-schema';
|
||||
|
||||
ProductTags = new Mongo.Collection('ProductTags', {
|
||||
schema: new SimpleSchema({
|
||||
name: {
|
||||
type: String,
|
||||
label: "Name",
|
||||
optional: false,
|
||||
trim: true,
|
||||
index: 1,
|
||||
unique: true
|
||||
},
|
||||
createdAt: {
|
||||
type: Date,
|
||||
label: "Created On",
|
||||
optional: false
|
||||
},
|
||||
updatedAt: {
|
||||
type: Date,
|
||||
label: "Updated On",
|
||||
optional: true
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
//Allows the client to do DB interaction without calling server side methods, while still retaining control over whether the user can make changes.
|
||||
ProductTags.allow({
|
||||
insert: function() {return false;},
|
||||
update: function() {return false;},
|
||||
remove: function() {return false;}
|
||||
});
|
||||
|
||||
if(Meteor.isServer) {
|
||||
Meteor.publish('productTags', function() {
|
||||
return ProductTags.find({});
|
||||
});
|
||||
|
||||
Meteor.methods({
|
||||
insertProductTag: function(productTag) {
|
||||
check(productTag, String);
|
||||
productTag = {name: productTag, createdAt: new Date()};
|
||||
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||
ProductTags.insert(productTag, function(err, id) {
|
||||
if(err) console.log(err);
|
||||
});
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized.");
|
||||
},
|
||||
deleteProductTag: function(id) {
|
||||
check(id, String);
|
||||
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||
ProductTags.remove(id);
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized.");
|
||||
},
|
||||
updateProductTag: function(tag) {
|
||||
check(tag, {
|
||||
_id: String,
|
||||
name: String
|
||||
});
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||
ProductTags.update(tag._id, {$set: {name: tag.name, updateAt: new Date()}});
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default ProductTags;
|
||||
17
imports/api/Roles.js
Normal file
17
imports/api/Roles.js
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
if(Meteor.isServer) {
|
||||
Meteor.publish('roles', function() {
|
||||
if(Roles.userIsInRole(this.userId, ['manage'])) {
|
||||
return Meteor.roles.find({}, {fields: {name: 1}});
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized to view roles.");
|
||||
});
|
||||
}
|
||||
|
||||
let ROLE_MANAGE = "manage";
|
||||
let ROLE_UPDATE = "update";
|
||||
|
||||
Meteor.UserRoles = {ROLE_MANAGE, ROLE_UPDATE};
|
||||
|
||||
|
||||
export default Meteor.roles;
|
||||
111
imports/api/Sale.js
Normal file
111
imports/api/Sale.js
Normal file
@@ -0,0 +1,111 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { Mongo } from 'meteor/mongo';
|
||||
import { check } from 'meteor/check';
|
||||
import {SimpleSchema} from 'meteor/aldeed:simple-schema';
|
||||
|
||||
Sales = new Mongo.Collection('Sales');
|
||||
let SalesSchema = new SimpleSchema({
|
||||
date: {
|
||||
type: Date,
|
||||
label: "Date",
|
||||
optional: false,
|
||||
index: 1
|
||||
},
|
||||
amount: {
|
||||
type: Number,
|
||||
label: "Amount",
|
||||
optional: false,
|
||||
decimal: true
|
||||
},
|
||||
price: {
|
||||
type: Number,
|
||||
label: "Price",
|
||||
optional: false,
|
||||
min: 0,
|
||||
exclusiveMin: true,
|
||||
decimal: true
|
||||
},
|
||||
measureId: {
|
||||
type: String,
|
||||
label: "Measure Id",
|
||||
trim: false,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
index: 1
|
||||
},
|
||||
productId: {
|
||||
type: String,
|
||||
label: "Product Id",
|
||||
trim: false,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
index: 1
|
||||
},
|
||||
venueId: {
|
||||
type: String,
|
||||
label: "Vendor Id",
|
||||
trim: false,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
index: 1
|
||||
// autoform: {
|
||||
// type: 'relation',
|
||||
// settings: {
|
||||
// collection: 'Venues',
|
||||
// fields: ['name']
|
||||
// }
|
||||
// }
|
||||
},
|
||||
createdAt: {
|
||||
type: Date,
|
||||
label: "Created On",
|
||||
optional: false
|
||||
}
|
||||
});
|
||||
Sales.attachSchema(SalesSchema);
|
||||
|
||||
if(Meteor.isServer) {
|
||||
Meteor.publish('sales', function(query, limit = 100) {
|
||||
let dbQuery = {};
|
||||
|
||||
if(query) {
|
||||
_.each(_.keys(query), function(key) {
|
||||
if(_.isObject(query[key])) dbQuery[key] = query[key];
|
||||
else if(_.isNumber(query[key])) dbQuery[key] = query[key];
|
||||
else dbQuery[key] = {$regex: RegExp.escape(query[key]), $options: 'i'};
|
||||
})
|
||||
}
|
||||
|
||||
if(!_.isNumber(limit)) limit = 100;
|
||||
|
||||
return Meteor.collections.Sales.find(dbQuery, {limit: limit, sort: {date: -1}});
|
||||
});
|
||||
|
||||
Meteor.methods({
|
||||
insertSale: function(sale) {
|
||||
//TODO: Check the structure of sale. Use: check(sale, {name: String, ...});
|
||||
sale.createdAt = new Date();
|
||||
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||
Sales.insert(sale, function(err, id) {
|
||||
if(err) console.log(err);
|
||||
});
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized.");
|
||||
},
|
||||
deleteSale: function(id) {
|
||||
check(id, String);
|
||||
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||
Sales.remove(id);
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//Allows the client to do DB interaction without calling server side methods, while still retaining control over whether the user can make changes.
|
||||
Sales.allow({
|
||||
insert: function() {return false;},
|
||||
update: function() {return false;},
|
||||
remove: function() {return false;}
|
||||
});
|
||||
|
||||
export default Sales;
|
||||
69
imports/api/User.js
Normal file
69
imports/api/User.js
Normal file
@@ -0,0 +1,69 @@
|
||||
import {Random} from 'meteor/random';
|
||||
|
||||
if(Meteor.isServer) {
|
||||
Meteor.publish('users', function() {
|
||||
if(Roles.userIsInRole(this.userId, ['manage'])) {
|
||||
return Meteor.users.find({}, {fields: {username: 1, emails: 1, roles: 1}});
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized to view users.");
|
||||
});
|
||||
|
||||
Meteor.methods({
|
||||
"insertUser": function(user, roles) {
|
||||
check(user, {
|
||||
username: String,
|
||||
email: String
|
||||
});
|
||||
check(roles, [String]);
|
||||
|
||||
//Verify the currently logged in user has authority to manage users.
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_MANAGE])) {
|
||||
//Verify the user name isn't already used.
|
||||
if(Meteor.collections.Users.findOne({username: user.username}) == undefined) {
|
||||
let pwd = Random.secret(20);
|
||||
let id = Accounts.createUser({password: pwd, username: user.username, email: user.email});
|
||||
|
||||
//Requires the alanning:roles package.
|
||||
Roles.addUsersToRoles(id, roles);
|
||||
}
|
||||
else {
|
||||
throw new Meteor.Error(400, "User already exists.");
|
||||
}
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized to add users.");
|
||||
},
|
||||
"updateUser": function(user) {
|
||||
check(user, {
|
||||
_id: String,
|
||||
username: String,
|
||||
emails: [{
|
||||
address: String,
|
||||
verified: Boolean
|
||||
}],
|
||||
roles: [String]
|
||||
});
|
||||
|
||||
//Verify the currently logged in user has authority to manage users.
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_MANAGE])) {
|
||||
//Verify the user name isn't already used with a different ID.
|
||||
if(Meteor.collections.Users.findOne({username: user.username, _id: {$ne: user._id}}) == undefined) {
|
||||
//Update the user. Note: I am using direct mongo modification, versus attempting to go through the Accounts and Roles objects. This could cause problems in the future if these packages change their data structures.
|
||||
Meteor.collections.Users.update(user._id, {$set: {username: user.username, emails: user.emails, roles: user.roles}});
|
||||
}
|
||||
else {
|
||||
throw new Meteor.Error(400, "User name already exists.");
|
||||
}
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized to update users.");
|
||||
},
|
||||
"deleteUser": function(id) {
|
||||
check(id, String);
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_MANAGE])) {
|
||||
Meteor.collections.Users.remove(id);
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized to remove users.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default Meteor.users;
|
||||
130
imports/api/Venue.js
Normal file
130
imports/api/Venue.js
Normal file
@@ -0,0 +1,130 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { Mongo } from 'meteor/mongo';
|
||||
import { check } from 'meteor/check';
|
||||
import {SimpleSchema} from 'meteor/aldeed:simple-schema';
|
||||
|
||||
Venues = new Mongo.Collection('Venues');
|
||||
let VenuesSchema = new SimpleSchema({
|
||||
name: {
|
||||
type: String,
|
||||
label: "Name",
|
||||
optional: false,
|
||||
trim: true,
|
||||
index: 1,
|
||||
unique: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
label: "Type",
|
||||
optional: false,
|
||||
trim: true
|
||||
},
|
||||
createdAt: {
|
||||
type: Date,
|
||||
label: "Created On",
|
||||
optional: false
|
||||
},
|
||||
updatedAt: {
|
||||
type: Date,
|
||||
label: "Updated On",
|
||||
optional: true
|
||||
},
|
||||
deletedAt: {
|
||||
type: Date,
|
||||
label: "Deleted On",
|
||||
optional: true
|
||||
},
|
||||
deletedBy: {
|
||||
type: String,
|
||||
label: "Deleted By",
|
||||
optional: true
|
||||
},
|
||||
restoredAt: {
|
||||
type: Date,
|
||||
label: "Restored On",
|
||||
optional: true
|
||||
},
|
||||
restoredBy: {
|
||||
type: String,
|
||||
label: "Restored By",
|
||||
optional: true
|
||||
}
|
||||
});
|
||||
Venues.attachSchema(VenuesSchema);
|
||||
|
||||
//https://github.com/zimme/meteor-collection-softremovable
|
||||
Venues.attachBehaviour("softRemovable", {
|
||||
removed: 'deleted',
|
||||
removedAt: 'deletedAt',
|
||||
removedBy: 'removedBy',
|
||||
restoredAt: 'restoredAt',
|
||||
restoredBy: 'restoredBy'
|
||||
});
|
||||
|
||||
if(Meteor.isServer) Meteor.publish('venues', function() {
|
||||
return Venues.find({});
|
||||
});
|
||||
|
||||
// //Requires: meteor add matb33:collection-hooks
|
||||
if(Meteor.isServer) {
|
||||
Venues.before.insert(function(userId, doc) {
|
||||
// check(userId, String);
|
||||
doc.createdAt = new Date();
|
||||
});
|
||||
Venues.before.update(function(userId, doc, fieldNames, modifier, options) {
|
||||
modifier.$set = modifier.$set || {}; //Make sure there is an object.
|
||||
modifier.$set.updatedAt = new Date();
|
||||
});
|
||||
|
||||
Meteor.methods({
|
||||
insertVenue: function(venue) {
|
||||
check(venue, {
|
||||
name: String,
|
||||
type: String
|
||||
});
|
||||
|
||||
venue.createdAt = new Date();
|
||||
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||
Venues.insert(venue);
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized.");
|
||||
},
|
||||
deleteVenue: function(id) {
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||
Venues.remove(id);
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized.");
|
||||
},
|
||||
updateVenue: function(venue) {
|
||||
check(venue, {
|
||||
name: String,
|
||||
type: String
|
||||
});
|
||||
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||
Venues.update(id, {$set: {name: venue.name, type: venue.type, updateAt: new Date()}});
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//Allows the client to do DB interaction without calling server side methods, while still retaining control over whether the user can make changes.
|
||||
// Meteor.allow({
|
||||
// insert: true,
|
||||
// update: ()->{return true},
|
||||
// remove: checkUser
|
||||
// };
|
||||
// checkUser = function(userId, doc) {
|
||||
// return doc && doc.userId === userId;
|
||||
// };
|
||||
|
||||
//Allows the client to interact with the db through the server via custom methods.
|
||||
// Meteor.methods({
|
||||
// deleteMeasure: function(id) {
|
||||
// Measures.remove(id);
|
||||
// }
|
||||
// });
|
||||
|
||||
export default Venues;
|
||||
26
imports/api/index.js
Normal file
26
imports/api/index.js
Normal file
@@ -0,0 +1,26 @@
|
||||
//import Categories from "./Category.js";
|
||||
//import Subcategories from "./Subcategory.js";
|
||||
import Measures from "./Measure.js";
|
||||
import Venues from "./Venue.js";
|
||||
import Products from "./Product.js";
|
||||
import ProductTags from "./ProductTag.js";
|
||||
import Sales from "./Sale.js";
|
||||
import Users from "./User.js";
|
||||
import UserRoles from "./Roles.js";
|
||||
|
||||
Meteor.collections = {Measures, Venues, Products, ProductTags, Sales, Users, UserRoles};
|
||||
if(Meteor.isServer) {
|
||||
//Change this to find admin users, create a default admin user if none exists.
|
||||
if(Users.find({}).count() == 0) {
|
||||
try {
|
||||
console.log("Creating a default admin user: admin/admin");
|
||||
|
||||
let id = Accounts.createUser({password: 'admin', username: 'admin'});
|
||||
//Requires the alanning:roles package.
|
||||
Roles.addUsersToRoles(id, [Meteor.UserRoles.ROLE_MANAGE, Meteor.UserRoles.ROLE_UPDATE]);
|
||||
}
|
||||
catch(err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user