Skip to content

Instantly share code, notes, and snippets.

@taxilian
Last active May 11, 2020 18:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save taxilian/58c058638b11ad4a11399acb899a866d to your computer and use it in GitHub Desktop.
Save taxilian/58c058638b11ad4a11399acb899a866d to your computer and use it in GitHub Desktop.
Email address validation typescriptified model used by HamStudy.org, example of mongoose-decorators-ts use
// import _ from 'lodash';
// import moment from 'moment';
// import express from 'express';
// import uuid from 'uuid';
import mongoose from 'mongoose';
import {schemaDef, dateField, pre, ref, field, arrayField, required, ModelFromSchemaDef, IMongooseDocument, populateVirtual} from 'mongoose-decorators-ts';
import emailVerify from 'email-verifier';
import P from '../lib/PromiseTS';
import _ from 'lodash';
// Note that while requests aren't super expensive they are also not free
const whoisXmlApiKey = "at_7ytFXfrNboMG0G99Agvi7hGWfByte";
const Verifier = new emailVerify(whoisXmlApiKey, {
checkCatchAll: true,
checkDisposable: true,
checkFree: true,
validateDNS: true,
validateSMTP: true,
});
const WhoisXMLApiVerify = P.promisify(Verifier.verify, {context: Verifier});
@schemaDef({
indexes: [
[{email: 1, confirmKey: 1}],
[{createdAt: 1}, {expireAfterSeconds: 60 * 60 * 24 * 14}], // expires after 14 days
],
plugins: [
],
schema_options: { usePushEach: true, timestamps: true }
})
class EmailAddrValidation {
/** This is the email address of the record */
@field({ lowercase: true, trim: true })
_id: string;
/** Automatically added by `timestamps: true` in schema_options */
createdAt: Date;
/** Automatically added by `timestamps: true` in schema_options */
updatedAt: Date;
////// These are fields cached from the WhoisXmlApi validation request
/** 'true' if there were syntax errors in the address */
@required() formatCheck: "true" | "false";
/** 'true' if the email address exists and can receive email over SMTP */
@required() smtpCheck: "true" | "false";
/** 'true' if the domain's DNS is configured correctly for email */
@required() dnsCheck: "true" | "false";
/** 'false' if the domain is not free, 'true' if it is */
@required() freeCheck: "true" | "false";
/** 'true' if the email address is disposable */
@required() disposableCheck: "true" | "false";
/** 'true' if the mail server has a catch-all address */
@required() catchAllCheck: "true" | "false";
/** If present, a list of all mail servers from MX records */
@required() mxRecords?: string[];
static async validateEmailAddr(this: typeof EmailAddrValidationModel, email: string) {
let validate = await this.findById(email.toLowerCase().trim()).exec();
if (!validate) {
validate = new this({_id: email});
try {
let data = await WhoisXMLApiVerify(email);
for (let f of <const>[
"formatCheck",
"smtpCheck",
"dnsCheck",
"freeCheck",
"disposableCheck",
"catchAllCheck",
"mxRecords",
]) {
validate.set(f, data[f]);
}
await validate.save();
} catch (err) {
return null;
}
}
return validate;
}
}
export const EmailAddrValidationModel = ModelFromSchemaDef<typeof EmailAddrValidation, EmailAddrValidation>(EmailAddrValidation);
export type EmailAddrValidationModel = IMongooseDocument<EmailAddrValidation>;
export default EmailAddrValidationModel;
// excerpt, not full file
@schemaDef({
indexes: [
[{ session_def: 1, sequence_num: 1, owner: 1 }, { unique: true }],
[{ start_date: 1, end_date: 1 }],
[{ owner: 1, date: 1, session_def: 1, sequence_num: 1 }],
[{ password: 1, date: 1 }, { sparse: true }],
[{ date: 1 }],
],
plugins: [
{ plugin: createdModifiedPlugin, options: { index: true } },
],
schema_options: { usePushEach: true, id: false }
})
class SessionInstance {
_id: mongoose.Types.ObjectId;
// tslint:disable:variable-name
@ref('SessionDef', { required: true }) session_def: mongoose.Types.ObjectId;
@required sequence_num: number;
// Denormalized data duplicated from the sessiondef:
@ref('User', { required: true }) owner: mongoose.Types.ObjectId;
@dateField({ required: true }) date: Date;
@required({ enum: VECEnum }) vec: cData.VECList;
// --
/** Overrides the owner's callsign if set */
@field()
callsign: string;
/** Extra information needed for this VEC */
@field()
vec_metadata: any;
/** Extra information requested by this VE */
@field()
ve_metadata: any;
/** This field replaced the old ones and the old ones should not be used anymore */
@arrayField(VELink) sessionVes: mongoose.Types.DocumentArray<VELink>;
/** @deprecated */
@refArray('VE', mongoose.Types.ObjectId) veList: mongoose.Types.Array<mongoose.Types.ObjectId>;
/** @deprecated */
@refArray('VE', mongoose.Types.ObjectId) signing_ves: mongoose.Types.Array<mongoose.Types.ObjectId>;
/** @deprecated */
@field password: string;
@required({ default: 'pend', enum: Object.values(SessionStates) })
state: SessionStates;
//////////// Populate Fields \\\\\\\\\\\\
@populateVirtual({ref: 'SessionDef', localField: 'session_def', foreignField: '_id', justOne: true})
readonly sessionDef?: SessionDefModel;
@populateVirtual({
ref: 'Applicant', localField: '_id', foreignField: 'sInstance',
count: true})
readonly applicantCount?: number;
@populateVirtual({ref: 'Applicant', localField: '_id', foreignField: 'sInstance', justOne: false})
applicantObjects: ApplicantModel[];
@populateVirtual({
ref: 'VE',
localField: 'veList', foreignField: '_id',
justOne: false, options: <any>{select: "call vec display_name"}
})
readonly veListDocs?: Pick<VEModel, 'call'|'vec'|'display_name'>[];
@populateVirtual({
ref: 'VE',
localField: 'signing_ves', foreignField: '_id',
justOne: false, options: <any>{select: "call vec display_name"}
})
readonly signingVeDocs?: Pick<VEModel, 'call'|'vec'|'display_name'>[];
@populateVirtual({
ref: 'User', localField: 'owner', foreignField: '_id',
justOne: true, options: <any>{select: 'call email first last'}})
readonly ownerDoc?: UserModel;
get vec_name() {
return cData.VECList[<'w5yi'>this.vec];
}
// tslint:enable:variable-name
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment