feat: mail

This commit is contained in:
2024-09-25 17:02:02 +02:00
committed by Thea Schöbl
parent f81690e19f
commit bc4c45ea21
13 changed files with 390 additions and 228 deletions

View File

@@ -7,58 +7,65 @@ 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(async (request, response, next) => {
try {
const [user, pass] = Buffer.from(request.headers['authorization']!.replace(/^Basic /, ''), 'base64')
.toString('utf8')
.split(':');
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();
const client = new ImapFlow({
host: 'imap.server.uni-frankfurt.de',
port: 993,
secure: true,
emitLogs: false,
auth: {user, pass},
});
response.locals.client = client;
await client.connect();
response.on('finish', async () => {
await client.logout();
});
next();
} catch {
response.status(401).send();
}
});
app.get('/', async (request, response) => {
const client = response.locals.client as ImapFlow;
await client.connect();
const lock = await client.getMailboxLock('INBOX');
app.get('/', async (_request, response) => {
const result = await response.locals.client.listTree();
response.json(result);
});
app.get('/:mailbox', async (request, response) => {
try {
await response.locals.client.mailboxOpen(request.params.mailbox);
const since = Number(request.query.since) || undefined;
const data = response.locals.client.fetch(
'1:*',
{},
{
// caution, BigInt can throw
changedSince: typeof since === 'string' ? BigInt(since) : undefined,
},
);
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,
});
for await (const message of data) {
messages.push(message.seq);
}
response.json(messages);
} finally {
lock.release();
} catch (error) {
console.error(error);
response.status(404).send();
}
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');
app.get('/:mailbox/:id', async (request, response) => {
try {
const message = await client.fetchOne(request.params.id, {
await response.locals.client.mailboxOpen(request.params.mailbox);
const message = await response.locals.client.fetchOne(request.params.id, {
envelope: true,
labels: true,
flags: true,
@@ -71,49 +78,39 @@ app.get('/:id', async (request, response) => {
envelope: message.envelope,
seq: message.seq,
});
} finally {
lock.release();
} catch (error) {
console.error(error);
response.status(404).send();
}
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');
app.get('/:mailbox/:id/:part', async (request, response) => {
try {
const message = await client.download(request.params.id, request.params.attachment);
message.content.on('data', chunk => {
response.write(chunk);
});
message.content.on('end', () => {
await response.locals.client.mailboxOpen(request.params.mailbox, {readOnly: true});
if (request.query.raw) {
const message = await response.locals.client.fetchOne(request.params.id, {
bodyParts: [`${request.params.part}.mime`, request.params.part],
});
response.write(message.bodyParts.get(`${request.params.part}.mime`));
response.write(message.bodyParts.get(request.params.part));
response.end();
});
} finally {
lock.release();
} else {
const message = await response.locals.client.download(request.params.id, request.params.part);
message.content.on('data', chunk => {
response.write(chunk);
});
message.content.on('end', () => {
response.end();
});
}
} catch (error) {
console.error(error);
response.status(404).send();
}
await client.logout();
});
app.get('/:id/raw/:part', 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, {
bodyParts: [`${request.params.part}.mime`, request.params.part],
});
response.write(message.bodyParts.get(`${request.params.part}.mime`));
response.write(message.bodyParts.get(request.params.part));
response.end();
} finally {
lock.release();
}
await client.logout();
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
console.log(`Server listening on port ${port}`);
});

9
backend/mail-plugin/src/types.d.ts vendored Normal file
View File

@@ -0,0 +1,9 @@
import {ImapFlow} from 'imapflow';
declare global {
namespace Express {
interface Locals {
client: ImapFlow;
}
}
}