Commit 536ccf81 by Ali Arshad

Merge branch 'user-registration' into 'master'

Refactor-Custom-email

See merge request jibc/jibc-meteor-api!2
parents f3b4d7c1 61c560b7
...@@ -22,3 +22,4 @@ accounts-password ...@@ -22,3 +22,4 @@ accounts-password
dburles:collection-helpers dburles:collection-helpers
ostrio:files ostrio:files
tunguska:reactive-aggregate tunguska:reactive-aggregate
percolate:migrations
...@@ -56,6 +56,7 @@ npm-mongo@3.1.2 ...@@ -56,6 +56,7 @@ npm-mongo@3.1.2
ordered-dict@1.1.0 ordered-dict@1.1.0
ostrio:cookies@2.4.1 ostrio:cookies@2.4.1
ostrio:files@1.12.2 ostrio:files@1.12.2
percolate:migrations@1.0.2
promise@0.11.2 promise@0.11.2
random@1.1.0 random@1.1.0
rate-limit@1.0.9 rate-limit@1.0.9
......
import { Meteor } from 'meteor/meteor';
import { Roles } from './collections/role';
import { PERMISSIONS } from './config';
export const seedDB = async () => {
// tslint:disable-next-line:ban-ts-ignore
// @ts-ignore
const allPermissions = Object.values(PERMISSIONS);
try {
if (!Roles.find({})
.fetch().length || Roles.find({})
.fetch().length === 0) {
// Roles
await Promise.all([
Roles.insert({
title: 'Admin',
permissions: allPermissions,
description: '',
}).toPromise(),
Roles.insert({
title: 'User',
permissions: [PERMISSIONS.CAN_LOGIN, PERMISSIONS.CAN_UPDATE_OWN_USER],
description: '',
}).toPromise(),
Roles.insert({
title: 'Blocked',
permissions: [],
description: '',
}),
]);
} else {
// Update Roles Permissions
await Promise.all([
Roles
.update({ title: 'Admin' }, { $set: { permissions: allPermissions } })
.toPromise(),
]);
}
if (!Meteor.users.find({})
.fetch().length && Meteor.users.find({})
.fetch().length === 0) {
Accounts.createUser({
username: 'admin',
password: 'admin',
email: 'ali.arshad@vqode.com',
profile: {
name: 'Admin User',
role: Roles.findOne({ title: 'Admin' })._id, // admin role id
},
});
}
} catch (e) {
throw new Meteor.Error('Error while seeding database.');
}
};
import { MongoObservable } from 'meteor-rxjs';
import { RoleModel } from '../models/role.model';
// tslint:disable-next-line:variable-name
export const rolesCollection = new MongoObservable.Collection<RoleModel>('roles');
import { MongoObservable } from 'meteor-rxjs';
import { Role } from '../models/role';
// tslint:disable-next-line:variable-name
export const Roles = new MongoObservable.Collection<Role>('roles');
import { MongoObservable } from 'meteor-rxjs';
export const settingsCollection = new MongoObservable.Collection('setting');
...@@ -6,3 +6,7 @@ export const PERMISSIONS = { ...@@ -6,3 +6,7 @@ export const PERMISSIONS = {
CAN_SEE_ALL_USERS: 'CAN_SEE_ALL_USERS', CAN_SEE_ALL_USERS: 'CAN_SEE_ALL_USERS',
CAN_ACCESS_DASHBOARD_PAGE: 'CAN_ACCESS_DASHBOARD_PAGE', CAN_ACCESS_DASHBOARD_PAGE: 'CAN_ACCESS_DASHBOARD_PAGE',
}; };
export const CONFIG = {
SiteName: 'JIBC',
};
process.env.S3 = '{"path": "files", "key":"AKIAWEWXPEPGGRRUHLH4","secret":"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","bucket":"siingio","region":"us-east-1"}'; process.env.S3 = '{"path": "files", "key":"AKIAWEWXPEPGGRRUHLH4","secret":"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","bucket":"siingio","region":"us-east-1"}';
process.env.MAIL_URL = 'smtp://postmaster%40mail.vqode.com:VQode1234@smtp.mailgun.org:587'; process.env.MAIL_URL = 'smtp://postmaster%40mail.vqode.com:VQode1234@smtp.mailgun.org:587';
process.env.appUrl = 'http://localhost:4200/';
import { Meteor } from 'meteor/meteor'; import { Meteor } from 'meteor/meteor';
import { seedDB } from './app.seeding';
import { PERMISSIONS } from './config'; import { PERMISSIONS } from './config';
import { UtilsService } from './services/utils.service'; import { UtilsService } from './services/utils.service';
Meteor.startup(() => { Meteor.startup(() => {
const seed = true; // @ts-ignore
Migrations.migrateTo('latest');
if (seed) {
seedDB();
}
// Validate Login Attempt // Validate Login Attempt
Accounts.validateLoginAttempt((data): boolean => { Accounts.validateLoginAttempt((data): boolean => {
const user: Meteor.User = data.user; const user: Meteor.User = data.user;
......
import { Meteor } from 'meteor/meteor';
import { EmailService } from '../services/email.service';
Meteor.methods({
sendForgotPasswordEmail(email: string): boolean {
const user: Meteor.User = Accounts.findUserByEmail(email);
if (user) {
EmailService.sendForgotPasswordEmail(user, email);
return true;
}
throw new Meteor.Error(422, 'Email address doesn\'t exist.');
},
});
import { Meteor } from 'meteor/meteor'; import { Meteor } from 'meteor/meteor';
import { first } from 'rxjs/operators'; import { first } from 'rxjs/operators';
import { Roles } from '../collections/role'; import { rolesCollection } from '../collections/role.collection';
import { PERMISSIONS } from '../config'; import { PERMISSIONS } from '../config';
import { Role } from '../models/role'; import { RoleModel } from '../models/role.model';
import { UtilsService } from '../services/utils.service'; import { UtilsService } from '../services/utils.service';
Meteor.methods({ Meteor.methods({
async saveRole(role: Role): Promise<void> { async saveRole(role: RoleModel): Promise<void> {
try { try {
if (role._id && UtilsService.hasPermission(PERMISSIONS.UPDATE_ROLE)) { if (role._id && UtilsService.hasPermission(PERMISSIONS.UPDATE_ROLE)) {
await Roles.update(role._id, role) await rolesCollection.update(role._id, role)
.pipe(first()) .pipe(first())
.toPromise(); .toPromise();
} }
......
import { Meteor } from 'meteor/meteor'; import { Meteor } from 'meteor/meteor';
import { Roles } from '../collections/role'; import { rolesCollection } from '../collections/role.collection';
import { PERMISSIONS } from '../config'; import { PERMISSIONS } from '../config';
import { UtilsService } from '../services/utils.service'; import { UtilsService } from '../services/utils.service';
import { Query } from '../models/query'; import { QueryModel } from '../models/query.model';
import { EmailService } from '../services/email.service';
Meteor.methods({ Meteor.methods({
usersGetCount(query: Query): any { usersGetCount(query: QueryModel): any {
return { return {
recordsFiltered: Meteor.users.find(query.query).count(), recordsFiltered: Meteor.users.find(query.query).count(),
recordsTotal: Meteor.users.find().count(), recordsTotal: Meteor.users.find().count(),
...@@ -15,9 +16,8 @@ Meteor.methods({ ...@@ -15,9 +16,8 @@ Meteor.methods({
}, },
registerUser(user: any): string { registerUser(user: any): string {
const defaultRole = Roles.findOne({ title: user.role }); const defaultRole = rolesCollection.findOne({ title: user.role });
if (!Accounts.findUserByEmail(user.email)) { if (!Accounts.findUserByEmail(user.email)) {
const userObj = { const userObj = {
email: user.email, email: user.email,
password: user.password, password: user.password,
...@@ -27,7 +27,10 @@ Meteor.methods({ ...@@ -27,7 +27,10 @@ Meteor.methods({
role: defaultRole._id, role: defaultRole._id,
}, },
}; };
return Accounts.createUser(userObj); const userId = Accounts.createUser(userObj);
EmailService.sendVerificationEmail(user, userId);
return userId;
} }
throw new Meteor.Error(422, 'Email address already in use.'); throw new Meteor.Error(422, 'Email address already in use.');
}, },
......
import { settingsCollection } from './collections/setting.collections';
import { PERMISSIONS } from './config';
import { rolesCollection } from './collections/role.collection';
import { Meteor } from 'meteor/meteor';
// @ts-ignore
Migrations.add({
version: 1,
name: 'Adding Roles',
up() {
const allPermissions = Object.values(PERMISSIONS);
if (!rolesCollection.find({}).fetch().length) {
rolesCollection.insert({
title: 'Admin',
permissions: allPermissions,
description: '',
});
rolesCollection.insert({
title: 'User',
permissions: [PERMISSIONS.CAN_LOGIN, PERMISSIONS.CAN_UPDATE_OWN_USER],
description: '',
});
rolesCollection.insert({
title: 'Blocked',
permissions: [],
description: '',
});
} else {
rolesCollection.update({ title: 'Admin' }, { $set: { permissions: allPermissions } });
}
},
});
// @ts-ignore
Migrations.add({
version: 2,
name: 'Adding Admin User',
up() {
if (!Meteor.users.find({}).fetch().length) {
Accounts.createUser({
username: 'admin',
password: 'admin',
email: 'ali.arshad@vqode.com',
profile: {
name: 'Admin User',
role: rolesCollection.findOne({ title: 'Admin' })._id,
},
});
}
},
});
// @ts-ignore
Migrations.add({
version: 3,
name: 'Adding Email Templates',
up() {
if (settingsCollection.find({ REGISTER_HEADING: { $exists: true } }).fetch().length === 0) {
settingsCollection.insert({ REGISTER_HEADING: 'Welcome to $platform' });
}
if (settingsCollection.find({ REGISTER_EMAIL_BODY: { $exists: true } }).fetch().length === 0) {
settingsCollection.insert({ REGISTER_EMAIL_BODY: 'Hi $name! You are our $role now. Please click on the link <a href="$link" target="_blank">$link</a> to verify your account.' });
}
if (settingsCollection.find({ RESET_HEADING: { $exists: true } }).fetch().length === 0) {
settingsCollection.insert({ RESET_HEADING: 'Reset Password for $platform' });
}
if (settingsCollection.find({ RESET_EMAIL_BODY: { $exists: true } }).fetch().length === 0) {
settingsCollection.insert({ RESET_EMAIL_BODY: 'Hi $name! Don\'t worry. Reset your password by clicking on the following link <a href="$link" target="_blank">$link</a>.' });
}
},
});
export class Query { export class QueryModel {
constructor( constructor(
public query = {} as any, public query = {} as any,
......
export interface Role { export interface RoleModel {
_id?: string; _id?: string;
title: string; title: string;
permissions: string[]; permissions: string[];
......
import { Meteor } from 'meteor/meteor';
import { rolesCollection } from '../collections/role.collection';
Meteor.publish('roles', () => rolesCollection.find({}));
import { Meteor } from 'meteor/meteor';
import { Roles } from '../collections/role';
Meteor.publish('roles', () => Roles.find({}));
...@@ -3,13 +3,13 @@ import { Meteor } from 'meteor/meteor'; ...@@ -3,13 +3,13 @@ import { Meteor } from 'meteor/meteor';
// @ts-ignore // @ts-ignore
import { publishComposite } from 'meteor/reywood:publish-composite'; import { publishComposite } from 'meteor/reywood:publish-composite';
import { PERMISSIONS } from '../config'; import { PERMISSIONS } from '../config';
import { Query } from '../models/query'; import { QueryModel } from '../models/query.model';
import { UtilsService } from '../services/utils.service'; import { UtilsService } from '../services/utils.service';
import { Roles } from '../collections/role'; import { rolesCollection } from '../collections/role.collection';
publishComposite('usersList', (filters = {}) => { publishComposite('usersList', (filters = {}) => {
const queryFilters = { ...new Query(), ...filters }; const queryFilters = { ...new QueryModel(), ...filters };
if (!UtilsService.hasPermission([PERMISSIONS.CAN_SEE_ALL_USERS])) { if (!UtilsService.hasPermission([PERMISSIONS.CAN_SEE_ALL_USERS])) {
throw new Meteor.Error(403, 'Not enough permissions to get all users'); throw new Meteor.Error(403, 'Not enough permissions to get all users');
...@@ -22,7 +22,7 @@ publishComposite('usersList', (filters = {}) => { ...@@ -22,7 +22,7 @@ publishComposite('usersList', (filters = {}) => {
children: [ children: [
{ {
find(user): any { find(user): any {
return Roles.find({ _id: user.profile.role }); return rolesCollection.find({ _id: user.profile.role });
}, },
}, },
], ],
......
import { CONFIG } from '../config';
import { settingsCollection } from '../collections/setting.collections';
export class EmailService {
static sendVerificationEmail(user: any, userId: string): void {
Accounts.emailTemplates.siteName = CONFIG.SiteName;
// Will uncomment it later with domain email
// Accounts.emailTemplates.from = CONFIG.DomainEmail;
Accounts.emailTemplates.verifyEmail = {
subject() {
const content: any = settingsCollection.findOne({ REGISTER_HEADING: { $exists: true } });
return content ? content.REGISTER_HEADING.replace('$platform', CONFIG.SiteName) : 'Welcome';
},
html(usr, url) {
const token = url.substr(url.lastIndexOf('/') + 1);
const link = `${process.env.appUrl}#/auth/verify-email/${token}`;
const content: any = settingsCollection.findOne({ REGISTER_EMAIL_BODY: { $exists: true } });
return content.REGISTER_EMAIL_BODY.replace(/\$name/g, user.name)
.replace(/\$role/g, user.role)
.replace(/\$link/g, link);
},
};
Accounts.sendVerificationEmail(userId, user.email);
}
static sendForgotPasswordEmail(user: Meteor.User, email: string): void {
Accounts.emailTemplates.siteName = CONFIG.SiteName;
// Will uncomment it later with domain email
// Accounts.emailTemplates.from = CONFIG.DomainEmail;
Accounts.emailTemplates.resetPassword = {
subject() {
const content: any = settingsCollection.findOne({ RESET_HEADING: { $exists: true } });
return content ? content.RESET_HEADING.replace('$platform', CONFIG.SiteName) : 'Welcome';
},
html(usr, url) {
const token = url.substr(url.lastIndexOf('/') + 1);
const link = `${process.env.appUrl}#/auth/reset-password/${token}`;
const content: any = settingsCollection.findOne({ RESET_EMAIL_BODY: { $exists: true } });
return content.RESET_EMAIL_BODY.replace(/\$name/g, user.profile.name)
.replace(/\$link/g, link);
},
};
Accounts.sendResetPasswordEmail(user._id, email);
}
}
...@@ -2,13 +2,13 @@ ...@@ -2,13 +2,13 @@
import AWS = require('aws-sdk'); import AWS = require('aws-sdk');
import { Meteor } from 'meteor/meteor'; import { Meteor } from 'meteor/meteor';
import { Observable, Subject } from 'rxjs'; import { Observable, Subject } from 'rxjs';
import { Roles } from '../collections/role'; import { rolesCollection } from '../collections/role.collection';
export class UtilsService { export class UtilsService {
static getLoggedInUserPermissions(): string[] { static getLoggedInUserPermissions(): string[] {
const usr: Meteor.User = Meteor.user(); const usr: Meteor.User = Meteor.user();
const role = Roles.findOne(usr.profile.role); const role = rolesCollection.findOne(usr.profile.role);
return role.permissions; return role.permissions;
} }
...@@ -16,7 +16,7 @@ export class UtilsService { ...@@ -16,7 +16,7 @@ export class UtilsService {
static hasPermission(permissions: string | string[]): boolean { static hasPermission(permissions: string | string[]): boolean {
const usr: Meteor.User = Meteor.user(); const usr: Meteor.User = Meteor.user();
if (usr) { if (usr) {
const role = Roles.findOne(usr.profile.role); const role = rolesCollection.findOne(usr.profile.role);
if (typeof permissions === 'string') { if (typeof permissions === 'string') {
return role.permissions.indexOf(permissions) !== -1; return role.permissions.indexOf(permissions) !== -1;
...@@ -29,7 +29,7 @@ export class UtilsService { ...@@ -29,7 +29,7 @@ export class UtilsService {
} }
static hasPermissionOfUser(user: Meteor.User, permission: string): boolean { static hasPermissionOfUser(user: Meteor.User, permission: string): boolean {
const role = Roles.findOne(user.profile.role); const role = rolesCollection.findOne(user.profile.role);
return role.permissions.indexOf(permission) !== -1; return role.permissions.indexOf(permission) !== -1;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment