Skip to content

Commit a38def7

Browse files
committed
feat(new tool): Email Parser
Fix CorentinTh#529
1 parent 318fb6e commit a38def7

File tree

6 files changed

+145
-13
lines changed

6 files changed

+145
-13
lines changed

components.d.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ declare module '@vue/runtime-core' {
7373
DynamicValues: typeof import('./src/tools/benchmark-builder/dynamic-values.vue')['default']
7474
Editor: typeof import('./src/tools/html-wysiwyg-editor/editor/editor.vue')['default']
7575
EmailNormalizer: typeof import('./src/tools/email-normalizer/email-normalizer.vue')['default']
76+
EmailParser: typeof import('./src/tools/email-parser/email-parser.vue')['default']
7677
EmojiCard: typeof import('./src/tools/emoji-picker/emoji-card.vue')['default']
7778
EmojiGrid: typeof import('./src/tools/emoji-picker/emoji-grid.vue')['default']
7879
EmojiPicker: typeof import('./src/tools/emoji-picker/emoji-picker.vue')['default']
@@ -129,22 +130,19 @@ declare module '@vue/runtime-core' {
129130
MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
130131
MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
131132
NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
133+
NButton: typeof import('naive-ui')['NButton']
132134
NCode: typeof import('naive-ui')['NCode']
133135
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
134136
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
135137
NEllipsis: typeof import('naive-ui')['NEllipsis']
136-
NForm: typeof import('naive-ui')['NForm']
137-
NFormItem: typeof import('naive-ui')['NFormItem']
138138
NH1: typeof import('naive-ui')['NH1']
139139
NH3: typeof import('naive-ui')['NH3']
140140
NIcon: typeof import('naive-ui')['NIcon']
141-
NInputNumber: typeof import('naive-ui')['NInputNumber']
142141
NLayout: typeof import('naive-ui')['NLayout']
143142
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
144143
NMenu: typeof import('naive-ui')['NMenu']
145144
NScrollbar: typeof import('naive-ui')['NScrollbar']
146-
NSlider: typeof import('naive-ui')['NSlider']
147-
NSwitch: typeof import('naive-ui')['NSwitch']
145+
NTable: typeof import('naive-ui')['NTable']
148146
NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
149147
OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']
150148
PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default']

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
"pdf-signature-reader": "^1.4.2",
8282
"pinia": "^2.0.34",
8383
"plausible-tracker": "^0.3.8",
84+
"postal-mime": "^2.2.7",
8485
"qrcode": "^1.5.1",
8586
"sql-formatter": "^13.0.0",
8687
"ua-parser-js": "^1.0.35",

pnpm-lock.yaml

Lines changed: 14 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<script setup lang="ts">
2+
import PostalMime from 'postal-mime';
3+
4+
const emailContent = ref('');
5+
6+
const parsedEmail = computedAsync(async () => {
7+
const emailContentValue = emailContent.value;
8+
try {
9+
return await PostalMime.parse(emailContentValue);
10+
}
11+
catch (e: any) {
12+
return e.toString();
13+
}
14+
});
15+
16+
function downloadFile(data: Uint8Array, fileName: string, fileType: string) {
17+
const blob = new Blob([data], { type: fileType || 'application/octet-stream' });
18+
const downloadUrl = URL.createObjectURL(blob);
19+
const a = document.createElement('a');
20+
a.href = downloadUrl;
21+
a.download = fileName;
22+
document.body.appendChild(a);
23+
a.click();
24+
URL.revokeObjectURL(downloadUrl);
25+
}
26+
</script>
27+
28+
<template>
29+
<div style="max-width: 600px;">
30+
<c-card title="Input" mb-2>
31+
<c-input-text
32+
v-model:value="emailContent"
33+
label="Email Content"
34+
multiline
35+
placeholder="Put your eml/email content here..."
36+
rows="15"
37+
mb-2
38+
/>
39+
</c-card>
40+
41+
<c-card v-if="parsedEmail && emailContent" title="Output">
42+
<input-copyable v-if="parsedEmail.date" label="Date" :value="parsedEmail.date" />
43+
<input-copyable v-if="parsedEmail.from?.name" label="From (name)" :value="parsedEmail.from?.name" />
44+
<input-copyable v-if="parsedEmail.from" label="From (address)" :value="parsedEmail.from?.address || parsedEmail.from" />
45+
<input-copyable v-if="parsedEmail.to" label="To" :value="JSON.stringify(parsedEmail.to)" />
46+
<input-copyable v-if="parsedEmail.cc" label="Cc" :value="JSON.stringify(parsedEmail.cc)" />
47+
<input-copyable v-if="parsedEmail.bcc?.name" label="Bcc" :value="JSON.stringify(parsedEmail.bcc)" />
48+
<input-copyable v-if="parsedEmail.replyTo" label="Reply-To" :value="JSON.stringify(parsedEmail.replyTo)" />
49+
<input-copyable v-if="parsedEmail.subject" label="Subject" :value="parsedEmail.subject" />
50+
<c-card v-if="parsedEmail.text" title="Plain Content" mb-2>
51+
<details>
52+
<summary>See content</summary>
53+
<textarea-copyable :value="parsedEmail.text" />
54+
</details>
55+
</c-card>
56+
<c-card v-if="parsedEmail.html" title="Html Content" mb-2>
57+
<details>
58+
<summary>See content</summary>
59+
<textarea-copyable :value="parsedEmail.html" />
60+
</details>
61+
</c-card>
62+
<c-card v-if="parsedEmail?.attachments?.length" title="Attachments" mb-2>
63+
<n-table>
64+
<thead>
65+
<tr>
66+
<th>Attachment</th><th />
67+
</tr>
68+
</thead>
69+
<tbody>
70+
<tr
71+
v-for="(h, index) in parsedEmail.attachments || []"
72+
:key="index"
73+
>
74+
<td>
75+
{{ `${h.filename || h.contentId || 'noname'} (${h.mimeType}) / ${h.disposition}` }}
76+
</td>
77+
<td>
78+
<c-button @click="downloadFile(h.content, h.filename || h.contentId || 'noname', h.mimeType)">
79+
Download
80+
</c-button>
81+
</td>
82+
</tr>
83+
</tbody>
84+
</n-table>
85+
</c-card>
86+
87+
<input-copyable v-if="parsedEmail.messageId" label="Message Id" :value="parsedEmail.messageId" />
88+
<input-copyable v-if="parsedEmail.inReplyTo" label="In Reply To" :value="parsedEmail.inReplyTo" />
89+
<input-copyable v-if="parsedEmail.references" label="References" :value="parsedEmail.references" />
90+
<input-copyable v-if="parsedEmail.deliveredTo" label="Delivered To" :value="parsedEmail.deliveredTo" />
91+
<input-copyable v-if="parsedEmail.returnPath" label="Return Path" :value="parsedEmail.returnPath" />
92+
<input-copyable v-if="parsedEmail.sender?.name" label="Sender (name)" :value="parsedEmail.sender?.name" />
93+
<input-copyable v-if="parsedEmail.sender" label="Sender (address)" :value="parsedEmail.sender?.address || parsedEmail.sender" />
94+
95+
<c-card title="All Headers" mt-2>
96+
<input-copyable
97+
v-for="(h, index) in parsedEmail.headers || []"
98+
:key="index"
99+
:label="h.key"
100+
:value="h.value"
101+
/>
102+
</c-card>
103+
</c-card>
104+
</div>
105+
</template>

src/tools/email-parser/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Mail } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'Email Parser',
6+
path: '/email-parser',
7+
description: 'Parse and extract information from raw Email content',
8+
keywords: ['email', 'parser', 'header', 'rfc2822', 'rfc5322', 'rfc822'],
9+
component: () => import('./email-parser.vue'),
10+
icon: Mail,
11+
createdAt: new Date('2024-08-15'),
12+
});

src/tools/index.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { tool as base64FileConverter } from './base64-file-converter';
22
import { tool as base64StringConverter } from './base64-string-converter';
33
import { tool as basicAuthGenerator } from './basic-auth-generator';
44
import { tool as emailNormalizer } from './email-normalizer';
5+
import { tool as emailParser } from './email-parser';
56

67
import { tool as asciiTextDrawer } from './ascii-text-drawer';
78

@@ -158,7 +159,15 @@ export const toolsByCategory: ToolCategory[] = [
158159
},
159160
{
160161
name: 'Network',
161-
components: [ipv4SubnetCalculator, ipv4AddressConverter, ipv4RangeExpander, macAddressLookup, macAddressGenerator, ipv6UlaGenerator],
162+
components: [
163+
ipv4SubnetCalculator,
164+
ipv4AddressConverter,
165+
ipv4RangeExpander,
166+
macAddressLookup,
167+
macAddressGenerator,
168+
ipv6UlaGenerator,
169+
emailParser,
170+
],
162171
},
163172
{
164173
name: 'Math',

0 commit comments

Comments
 (0)