dev: add service for cleaning email addresses

This commit is contained in:
KernelDeimos 2024-10-02 17:51:43 -04:00
parent 9db55fc5f7
commit 054b9c2864
3 changed files with 131 additions and 0 deletions

View File

@ -0,0 +1,126 @@
const BaseService = require("./BaseService");
class CleanEmailService extends BaseService {
static NAMED_RULES = {
// For some providers, dots don't matter
dots_dont_matter: {
name: 'dots_dont_matter',
description: 'Dots don\'t matter',
rule: ({ eml }) => {
eml.local = eml.local.replace(/\./g, '');
},
},
remove_subaddressing: {
name: 'remove_subaddressing',
description: 'Remove subaddressing',
rule: ({ eml }) => {
eml.local = eml.local.split('+')[0];
},
},
};
static PROVIDERS = {
gmail: {
name: 'gmail',
description: 'Gmail',
rules: ['dots_dont_matter'],
},
icloud: {
name: 'icloud',
description: 'iCloud',
rules: ['dots_dont_matter'],
},
yahoo: {
name: 'yahoo',
description: 'Yahoo',
// Yahoo doesn't allow subaddressing, which would be a non-issue,
// except Yahoo allows '+' symbols in the primary email address.
rmrules: ['remove_subaddressing'],
},
};
// Service providers may have multiple subdomains a user can choose
static DOMAIN_TO_PROVIDER = {
'gmail.com': 'gmail',
'yahoo.com': 'yahoo',
'yahoo.co.uk': 'yahoo',
'yahoo.ca': 'yahoo',
'yahoo.com.au': 'yahoo',
'icloud.com': 'icloud',
'me.com': 'icloud',
'mac.com': 'icloud',
};
// Service providers may allow the same primary email address to be
// used with different domains
static DOMAIN_NONDISTINCT = {
'googlemail.com': 'gmail.com',
}
_construct () {
this.named_rules = this.constructor.NAMED_RULES;
this.providers = this.constructor.PROVIDERS;
this.domain_to_provider = this.constructor.DOMAIN_TO_PROVIDER;
this.domain_nondistinct = this.constructor.DOMAIN_NONDISTINCT;
}
clean_email (email) {
const eml = (() => {
const [local, domain] = email.split('@');
return { local, domain };
})();
if ( this.domain_nondistinct[eml.domain] ) {
eml.domain = this.domain_nondistinct[eml.domain];
}
const rules = [
'remove_subaddressing',
];
const provider = this.domain_to_provider[eml.domain] || eml.domain;
const provider_info = this.providers[provider];
if ( provider_info ) {
provider_info.rules = provider_info.rules || [];
provider_info.rmrules = provider_info.rmrules || [];
for ( const rule_name of provider_info.rules ) {
rules.push(rule_name);
}
for ( const rule_name of provider_info.rmrules ) {
const idx = rules.indexOf(rule_name);
if ( idx !== -1 ) {
rules.splice(idx, 1);
}
}
}
for ( const rule_name of rules ) {
const rule = this.named_rules[rule_name];
rule.rule({ eml });
}
return eml.local + '@' + eml.domain;
}
_test ({ assert }) {
const cases = [
{
email: 'bob.ross+happy-clouds@googlemail.com',
expected: 'bobross@gmail.com',
},
{
email: 'under.rated+email-service@yahoo.com',
expected: 'under.rated+email-service@yahoo.com',
},
{
email: 'the-absolute+best@protonmail.com',
expected: 'the-absolute@protonmail.com',
},
];
for ( const { email, expected } of cases ) {
const cleaned = this.clean_email(email);
assert.equal(cleaned, expected, `clean_email(${email}) === ${expected}`);
}
}
}
module.exports = { CleanEmailService };

View File

@ -126,6 +126,9 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService {
[24, [
'0027_emulator-app.dbmig.js',
]],
[25, [
'0028_clean-email.sql',
]],
];
// Database upgrade logic

View File

@ -0,0 +1,2 @@
ALTER TABLE `user` ADD COLUMN `clean_email` varchar(256) DEFAULT NULL;
CREATE INDEX idx_user_clean_email ON `user` (`clean_email`);