From 054b9c28643d86ffe200334aa3b9696a930ee3ca Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Wed, 2 Oct 2024 17:51:43 -0400 Subject: [PATCH] dev: add service for cleaning email addresses --- src/backend/src/services/CleanEmailService.js | 126 ++++++++++++++++++ .../database/SqliteDatabaseAccessService.js | 3 + .../sqlite_setup/0028_clean-email.sql | 2 + 3 files changed, 131 insertions(+) create mode 100644 src/backend/src/services/CleanEmailService.js create mode 100644 src/backend/src/services/database/sqlite_setup/0028_clean-email.sql diff --git a/src/backend/src/services/CleanEmailService.js b/src/backend/src/services/CleanEmailService.js new file mode 100644 index 00000000..ab879c60 --- /dev/null +++ b/src/backend/src/services/CleanEmailService.js @@ -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 }; diff --git a/src/backend/src/services/database/SqliteDatabaseAccessService.js b/src/backend/src/services/database/SqliteDatabaseAccessService.js index 2c6470b4..2c6c4ded 100644 --- a/src/backend/src/services/database/SqliteDatabaseAccessService.js +++ b/src/backend/src/services/database/SqliteDatabaseAccessService.js @@ -126,6 +126,9 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService { [24, [ '0027_emulator-app.dbmig.js', ]], + [25, [ + '0028_clean-email.sql', + ]], ]; // Database upgrade logic diff --git a/src/backend/src/services/database/sqlite_setup/0028_clean-email.sql b/src/backend/src/services/database/sqlite_setup/0028_clean-email.sql new file mode 100644 index 00000000..4a08d75f --- /dev/null +++ b/src/backend/src/services/database/sqlite_setup/0028_clean-email.sql @@ -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`);