feat: email client prototype

This commit is contained in:
2024-06-26 20:40:00 +02:00
committed by Thea Schöbl
parent e6c17c860b
commit 7de16808fc
41 changed files with 1396 additions and 213 deletions

View File

@@ -0,0 +1,2 @@
#!/usr/bin/env node
import './lib/cli.js';

View File

@@ -0,0 +1,68 @@
{
"name": "@openstapps/mail-plugin",
"description": "Mail Plugin",
"version": "3.2.0",
"private": true,
"type": "module",
"license": "GPL-3.0-only",
"author": "Thea Schöbl",
"bin": "app.js",
"files": [
"app.js",
"lib",
"README.md",
"CHANGELOG.md",
"Dockerfile"
],
"scripts": {
"build": "tsup-node --dts",
"dev": "tsup-node --watch --onSuccess \"pnpm run start\"",
"start": "node app.js",
"deploy": "pnpm --prod --filter=@openstapps/minimal-plugin deploy ../../.deploy/minimal-plugin",
"format": "prettier . -c --ignore-path ../../.gitignore",
"format:fix": "prettier --write . --ignore-path ../../.gitignore",
"lint": "eslint --ext .ts src/",
"lint:fix": "eslint --fix --ext .ts src/"
},
"dependencies": {
"@openstapps/core": "workspace:*",
"@openstapps/core-tools": "workspace:*",
"@openstapps/logger": "workspace:*",
"commander": "10.0.0",
"dotenv": "16.4.5",
"express": "4.18.2",
"imapflow": "1.0.162",
"mailparser": "3.7.1",
"node-forge": "1.3.1",
"nodemailer": "6.9.14",
"ts-node": "10.9.2"
},
"devDependencies": {
"@openstapps/eslint-config": "workspace:*",
"@openstapps/prettier-config": "workspace:*",
"@openstapps/tsconfig": "workspace:*",
"@types/express": "4.17.17",
"@types/imapflow": "1.0.18",
"@types/mailparser": "3.4.4",
"@types/node": "18.15.3",
"@types/node-forge": "1.3.11",
"@types/nodemailer": "6.4.15",
"tsup": "6.7.0",
"typescript": "5.4.2"
},
"tsup": {
"entry": [
"src/cli.ts"
],
"sourcemap": true,
"clean": true,
"format": "esm",
"outDir": "lib"
},
"prettier": "@openstapps/prettier-config",
"eslintConfig": {
"extends": [
"@openstapps"
]
}
}

View File

@@ -0,0 +1,104 @@
import {config} from 'dotenv';
import {ImapFlow} from 'imapflow';
import express from 'express';
config({path: '.env.local'});
const app = express();
const port = process.env.PORT || 4000;
if (!process.env.IMAP_USER || !process.env.IMAP_PASSWORD) {
throw new Error('Provide IMAP user');
}
app.use((_request, response, next) => {
const client = new ImapFlow({
host: 'imap.server.uni-frankfurt.de',
port: 993,
secure: true,
emitLogs: false,
auth: {
user: process.env.IMAP_USER!,
pass: process.env.IMAP_PASSWORD!,
},
});
response.locals.client = client;
next();
});
app.get('/', async (request, response) => {
const client = response.locals.client as ImapFlow;
await client.connect();
const lock = await client.getMailboxLock('INBOX');
try {
const messages = [];
for await (const message of client.fetch('1:*', {
envelope: true,
labels: true,
bodyStructure: true,
flags: true,
})) {
messages.push({
bodyStructure: message.bodyStructure,
labels: [...(message.labels ?? [])],
flags: [...(message.flags ?? [])],
envelope: message.envelope,
seq: message.seq,
});
}
response.json(messages);
} finally {
lock.release();
}
await client.logout();
});
app.get('/:id', async (request, response) => {
const client = response.locals.client as ImapFlow;
await client.connect();
const lock = await client.getMailboxLock('INBOX');
try {
const message = await client.fetchOne(request.params.id, {
envelope: true,
labels: true,
flags: true,
bodyStructure: true,
});
response.json({
bodyStructure: message.bodyStructure,
labels: [...(message.labels ?? [])],
flags: [...(message.flags ?? [])],
envelope: message.envelope,
seq: message.seq,
});
} finally {
lock.release();
}
await client.logout();
});
app.get('/:id/attachment/:attachment', async (request, response) => {
const client = response.locals.client as ImapFlow;
await client.connect();
const lock = await client.getMailboxLock('INBOX');
try {
const message = await client.download(request.params.id, request.params.attachment);
const body = await new Promise<string>(resolve => {
let body = '';
message.content.on('data', chunk => {
body += chunk.toString();
});
message.content.on('end', () => {
resolve(body);
});
});
response.send(body);
} finally {
lock.release();
}
await client.logout();
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});

View File

@@ -0,0 +1,4 @@
{
"extends": "@openstapps/tsconfig",
"exclude": ["lib", "app.js"]
}