Bläddra i källkod

dev: add service for cleaning email addresses

KernelDeimos 8 månader sedan
förälder
incheckning
054b9c2864

+ 126 - 0
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 };

+ 3 - 0
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

+ 2 - 0
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`);