Skip to content

Commit

Permalink
feat(general): Add multiple mailboxes. (#1445)
Browse files Browse the repository at this point in the history
  • Loading branch information
rnwood committed Apr 27, 2024
1 parent ba461a4 commit 53a0244
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 90 deletions.
79 changes: 0 additions & 79 deletions Rnwood.Smtp4dev/ClientApp/src/components/messageanalysis.vue

This file was deleted.

158 changes: 158 additions & 0 deletions Rnwood.Smtp4dev/ClientApp/src/components/messageclientanalysis.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<template>

<div class="vfillpanel" v-loading="loading">

<el-alert v-if="error" type="error">
{{error.message}}

<el-button v-on:click="loadMessage">Retry</el-button>
</el-alert>

<div v-if="message && !message.hasHtmlBody" class="fill nodetails centrecontents">
<div>Message has no HTML body</div>
</div>

<el-table class="fill table" stripe :data="warnings" v-if="message?.hasHtmlBody" empty-text="There are no warnings">
<el-table-column prop="feature" label="Feature" width="180">
<template #default="scope">
<span style="font-family: Courier New, Courier, monospace">{{scope.row.feature}}</span>
</template>
</el-table-column>
<el-table-column prop="type" label="Type" width="180">
<template #default="scope">
<a v-if="scope.row.url" target="_blank" :href="scope.row.url">{{scope.row.type}}</a>
<span v-if="!scope.row.url">{{scope.row.type}}</span>
</template>
</el-table-column>
<el-table-column prop="browser" label="Browsers">
<template #default="scope">
<div style="display: flex; flex-wrap: wrap; gap: 5px;">
<el-tag v-for="browser in scope.row.browsers" :type="scope.row.isError ? 'error' : 'warning'" :key="browser">{{browser}}</el-tag>
</div>
</template>
</el-table-column>
</el-table>
</div>


</template>
<script lang="ts">
import { Component, Vue, Prop, Watch, toNative, Emit } from 'vue-facing-decorator'
import MessagesController from "../ApiClient/MessagesController";
import Message from "../ApiClient/Message";
import { doIUseEmail } from '@jsx-email/doiuse-email';
@Component
class MessageClientAnalysis extends Vue {
@Prop({ default: null })
message: Message | null | undefined;
error: Error | null = null;
loading = false;
warnings: { message: string, feature: string, type: string, browsers: string[], url: string, isError: boolean }[] =[];
@Watch("message")
async onMessageChanged(value: Message | null, oldValue: Message | null) {
await this.loadMessage();
}
@Watch("warnings")
onWarningsChanged() {
this.fireWarningCountChanged()
}
@Emit("warning-count-changed")
fireWarningCountChanged() {
return this.warnings?.length ?? 0;
}
private parseWarning(warning: string, isError: boolean) {
const details = { message: warning, type: "", feature: "", browser: "", url: "", isError: false };
const detailsMatch = warning.match(/^`(.+)` is (.+) by `(.+)`$/);
if (detailsMatch) {
details.feature = detailsMatch[1] ?? null;
details.type = detailsMatch[2] ?? null;
details.browser = detailsMatch[3] ?? null;
details.isError = isError;
if (details.feature.endsWith(" element")) {
details.url = `https://www.caniemail.com/features/html-${details.feature.replace("<", "").replace("> element", "")}/`;
} else {
details.url = `https://www.caniemail.com/features/css-${details.feature.replace(":", "-")}/`;
}
}
return details;
}
async loadMessage() {
this.warnings = [];
this.error = null;
this.loading = true;
try {
if (this.message != null && this.message.hasHtmlBody) {
const html = await new MessagesController().getMessageHtml(this.message.id);
const doIUseResults = doIUseEmail(html, { emailClients: ["*"] });
const allWarnings = [];
for (const warning of doIUseResults.warnings) {
const details = this.parseWarning(warning, false);
allWarnings.push(details);
}
if (doIUseResults.success == false) {
for (const warning of doIUseResults.errors) {
const details = this.parseWarning(warning,true);
allWarnings.push(details);
}
}
const allGrouped = Object.groupBy(allWarnings, i => i.feature + " " + i.type);
for (const groupKey in allGrouped) {
const groupItems = allGrouped[groupKey]!;
this.warnings.push({
type: groupItems[0].type,
feature: groupItems[0].feature,
message: groupItems[0].message,
url: groupItems[0].url,
browsers: groupItems.map(i => i.browser),
isError: groupItems[0].isError
})
}
}
} catch (e: any) {
this.error = e;
} finally {
this.loading = false;
}
}
async created() {
this.loadMessage();
}
async destroyed() {
}
}
export default toNative(MessageClientAnalysis)
</script>
26 changes: 19 additions & 7 deletions Rnwood.Smtp4dev/ClientApp/src/components/messageview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,24 @@
</el-tabs>
</el-tab-pane>

<!--<el-tab-pane label="Analysis" id="analysis" class="fill vfillpanel">
<el-tab-pane label="Analysis" id="analysis">
<template #label>
<el-icon><FirstAidKit /></el-icon>&nbsp;Analysis
<el-badge type="warning" :value="analysisWarningCount ? analysisWarningCount : ''" :offset="[8, 3]">
<el-icon><FirstAidKit /></el-icon>&nbsp;Analysis
</el-badge>
</template>
<messageanalysis class="fill" :message="message" type="source"></messageanalysis>
</el-tab-pane>-->

<el-tabs value="clients">
<el-tab-pane label="HTML Compatibility" id="clients" class="hfillpanel">
<template #label>
<el-badge type="warning" :value="analysisWarningCount ? analysisWarningCount : ''" :offset="[8, 3]">
HTML Compatibility
</el-badge>
</template>
<messageclientanalysis class="fill" :message="message" @warning-count-changed="n => this.analysisWarningCount=n"></messageclientanalysis>
</el-tab-pane>
</el-tabs>
</el-tab-pane>

<el-tab-pane label="Source" id="source" class="fill vfillpanel">
<template #label>
Expand Down Expand Up @@ -178,8 +190,7 @@
import MessageviewAttachments from "@/components/messageviewattachments.vue";
import MessagePartsSource from "@/components/messagepartsource.vue";
import MessageSource from "@/components/messagesource.vue";
import MessageAnalysis from "@/components/messageanalysis.vue";
import { TreeInstance } from "element-plus/es/components/tree";
import MessageClientAnalysis from "@/components/messageclientanalysis.vue";
import { MessageBoxInputData } from 'element-plus/es/components/message-box';
import { ElMessageBox, ElNotification } from 'element-plus';
import ServerController from '../ApiClient/ServerController';
Expand All @@ -193,7 +204,7 @@
messageviewattachments: MessageviewAttachments,
messagepartsource: MessagePartsSource,
messagesource: MessageSource,
messageanalysis: MessageAnalysis
messageclientanalysis: MessageClientAnalysis
}
})
class MessageView extends Vue {
Expand All @@ -204,6 +215,7 @@
message: Message | null = null;
selectedPart: MessageEntitySummary | null = null;
warnings: MessageWarning[] = [];
analysisWarningCount: number = 0;
error: Error | null = null;
loading = false;
Expand Down
2 changes: 1 addition & 1 deletion Rnwood.Smtp4dev/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"profiles": {
"Rnwood.Smtp4dev": {
"commandName": "Project",
"commandLineArgs": "--db=",
"commandLineArgs": "--db=database2.db",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
Expand Down
2 changes: 1 addition & 1 deletion Rnwood.Smtp4dev/Rnwood.Smtp4dev.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@
<ClientApp Remove="ClientApp\src\ApiClient\Server.ts" />
<ClientApp Remove="ClientApp\src\ApiClient\ServerController.ts" />
<ClientApp Remove="ClientApp\src\ApiClient\User.ts" />
<ClientApp Remove="ClientApp\src\components\messageanalysis.vue" />
<ClientApp Remove="ClientApp\src\components\messageclientanalysis.vue" />
<ClientApp Remove="ClientApp\src\components\messagesource.vue" />
<ClientApp Remove="ClientApp\src\components\settingsdialog.vue" />
</ItemGroup>
Expand Down
4 changes: 3 additions & 1 deletion Rnwood.Smtp4dev/Server/Smtp4devServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ void ProcessMessage(Message message, ISession session, MailboxOptions targetMail
public RelayResult TryRelayMessage(Message message, MailboxAddress[] overrideRecipients)
{
var result = new RelayResult(message);

if (!relayOptions.CurrentValue.IsEnabled)
{
return result;
Expand Down Expand Up @@ -499,12 +500,13 @@ public RelayResult TryRelayMessage(Message message, MailboxAddress[] overrideRec
{
log.Information("Relaying message to {recipient}", recipient);

using var relaySmtpClient = this.relaySmtpClientFactory(this.relayOptions.CurrentValue);
using SmtpClient relaySmtpClient = relaySmtpClientFactory(relayOptions.CurrentValue);

if (relaySmtpClient == null)
{
throw new ApplicationException("Relay server options are incomplete.");
}

var apiMsg = new ApiModel.Message(message);
MimeMessage newEmail = apiMsg.MimeMessage;
MailboxAddress sender = MailboxAddress.Parse(
Expand Down
5 changes: 4 additions & 1 deletion Rnwood.Smtp4dev/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ namespace Rnwood.Smtp4dev
public class Startup
{
private const string InMemoryDbConnString = "Data Source=file:cachedb?mode=memory&cache=shared";
private SqliteConnection keepAliveConnection;

public Startup(IConfiguration configuration)
{
Expand Down Expand Up @@ -63,7 +64,9 @@ public void ConfigureServices(IServiceCollection services)
if (string.IsNullOrEmpty(serverOptions.Database))
{
Log.Logger.Information("Using in memory database.");
var keepAliveConnection = new SqliteConnection(InMemoryDbConnString);
//Must be held open to keep the memory DB alive
keepAliveConnection = new SqliteConnection(InMemoryDbConnString);
keepAliveConnection.Open();
opt.UseSqlite(InMemoryDbConnString);
}
Expand Down

0 comments on commit 53a0244

Please sign in to comment.