From cc0f31f649963e638c1a8ef8d3c82358b784a158 Mon Sep 17 00:00:00 2001 From: titanism <101466223+titanism@users.noreply.github.com> Date: Fri, 16 Aug 2024 08:06:45 -0500 Subject: [PATCH] fix: added parse DNS CSV script work in progress --- package.json | 3 +- scripts/parse-dns-csv.js | 96 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 scripts/parse-dns-csv.js diff --git a/package.json b/package.json index 119942cbc5..ddd9109f0f 100644 --- a/package.json +++ b/package.json @@ -122,6 +122,7 @@ "email-templates": "11.1.1", "error-stack-parser": "2.1.4", "etag": "1.8.1", + "fast-csv": "5.0.1", "fast-dir-size": "1.3.0", "fast-safe-stringify": "2.1.1", "fast-xml-parser": "4.4.1", @@ -168,7 +169,7 @@ "lazyframe": "2.2.7", "lazyload": "2.0.0-rc.2", "libmime": "5.3.5", - "localhost-url-regex": "^1.0.11", + "localhost-url-regex": "1.0.11", "lodash": "4.17.21", "mailauth": "4.6.8", "mailparser": "3.7.1", diff --git a/scripts/parse-dns-csv.js b/scripts/parse-dns-csv.js new file mode 100644 index 0000000000..20168e42fe --- /dev/null +++ b/scripts/parse-dns-csv.js @@ -0,0 +1,96 @@ +/** + * Copyright (c) Forward Email LLC + * SPDX-License-Identifier: BUSL-1.1 + */ + +// eslint-disable-next-line import/no-unassigned-import +require('#config/env'); + +const process = require('node:process'); +const fs = require('node:fs'); + +// eslint-disable-next-line import/no-unassigned-import +require('#config/env'); +// eslint-disable-next-line import/no-unassigned-import +require('#config/mongoose'); + +const Graceful = require('@ladjs/graceful'); +const isSANB = require('is-string-and-not-blank'); +const mongoose = require('mongoose'); +const pEvent = require('p-event'); +const { parse } = require('fast-csv'); + +const logger = require('#helpers/logger'); +const setupMongoose = require('#helpers/setup-mongoose'); + +// FILE_PATH=the file path to the CSV file +if (!isSANB(process.env.FILE_PATH)) throw new Error('FILE_PATH missing'); + +const graceful = new Graceful({ + mongooses: [mongoose], + logger +}); + +graceful.listen(); + +(async () => { + await setupMongoose(logger); + + const stat = await fs.promises.stat(process.env.FILE_PATH); + if (!stat.isFile()) throw new Error(`${process.env.FILE_PATH} is not a file`); + + if (!process.env.WRITE_PATH || !process.env.WRITE_PATH.endsWith('.txt')) + throw new Error('Write path missing (must end in ".txt")'); + + try { + const stat2 = await fs.promises.stat(process.env.WRITE_PATH); + if (stat2.isFile()) + throw new Error('Write path exists, please delete it first'); + } catch (err) { + if (err.code !== 'ENOENT') throw err; + } + + const stream = fs.createReadStream(process.env.FILE_PATH); + const writeStream = fs.createWriteStream(process.env.WRITE_PATH); + + await pEvent(writeStream, 'open'); + + const REGEX = /forwardemail\.net/i; + + // TODO: instead of doing this we can simply use ripgrep + // `rg 'forwardemail' -i ~/Desktop/mxrecords.csv > ~/Desktop/mx.csv` + stream + .pipe(parse({ delimiter: ';' })) + .on('error', console.error) + .on('data', (row) => { + const [domain, exchanges] = row; + // write to a new file stream the domain name + if (REGEX.test(exchanges)) { + console.log('domain matched', domain); + writeStream.write(`${domain}\n`); + } + }); + + await pEvent(stream, 'end'); + + // + // TODO: open the file that was created and read each line + // perform DNS lookup on the domain name and create a mapping + // (if it is `forward-email=` then perform lookup on email addresses) + // (and create a mapping of email address -> [ array, of, domain, names, ... ] + // + // TODO: the "Date" header should be set in the future and spread out over a range + // (e.g. 1000 emails a day every day until the entire list is sent towards) + // (and we should keep track of the emails sent in a new file `emails.csv` with Email ID - that way we don't send duplicates) + // + // TODO: create an account for them automatically and email them their username and password + // (import all of their domains too as domains added to the free plan -> which will put them in a drip campaign) + // (their account will get auto-deleted in 30 days if they don't verify their email address) + // + // TODO: one-click link to create and one-click link to delete + // + // TODO: magic link to login on our login form + // + + process.exit(0); +})();