Skip to content

Commit 7247342

Browse files
authored
fix: ensure config work as stated on documentation
The previous setup handles config a bit different from the documentation: * `scanEnabled` defaulted to false instead of true * misrecognize `fallbackEmails` set with an empty array as fallback enabled * alias with `to` being an empty array or not set was considered valid They are all fixed in this commit (#12) with regression tests covered.
1 parent fa53706 commit 7247342

File tree

5 files changed

+1097
-903
lines changed

5 files changed

+1097
-903
lines changed

.github/workflows/analyze.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,5 @@ jobs:
3636
- name: Check formatting
3737
run: npm run format-checking
3838

39-
- name: Check snapshot
39+
- name: Run test
4040
run: npm test

lib/maildog-stack.ts

Lines changed: 64 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,23 @@ import * as snsSubscriptions from '@aws-cdk/aws-sns-subscriptions';
1010
import * as sqs from '@aws-cdk/aws-sqs';
1111
import * as path from 'path';
1212
import { DispatcherConfig } from './maildog-stack.dispatcher';
13-
interface MailDogForwardingRule {
14-
description?: string;
13+
14+
interface MailDogAliasRule {
15+
description: string;
1516
to: string[];
1617
}
1718

1819
interface MailDogDomainRule {
19-
enabled?: boolean;
20-
fromEmail?: string;
21-
scanEnabled?: boolean;
22-
tlsEnforced?: boolean;
23-
fallbackEmails?: string[];
24-
alias?: Record<string, MailDogForwardingRule>;
20+
enabled: boolean;
21+
fromEmail: string;
22+
scanEnabled: boolean;
23+
tlsEnforced: boolean;
24+
fallbackEmails: string[];
25+
alias: Record<string, Partial<MailDogAliasRule>>;
2526
}
2627

2728
interface MailDogConfig {
28-
domains: Record<string, MailDogDomainRule>;
29+
domains: Record<string, Partial<MailDogDomainRule>>;
2930
}
3031

3132
interface MailDogStackProps extends cdk.StackProps {
@@ -37,7 +38,19 @@ export class MailDogStack extends cdk.Stack {
3738
super(scope, id, props);
3839

3940
// The code that defines your stack goes here
40-
const { domains } = props.config;
41+
const domainRuleEntries = Object.entries(props.config.domains).map<
42+
[string, MailDogDomainRule]
43+
>(([domain, rule]) => [
44+
domain,
45+
{
46+
enabled: rule.enabled ?? true,
47+
fromEmail: rule.fromEmail ?? 'noreply',
48+
scanEnabled: rule.scanEnabled ?? true,
49+
tlsEnforced: rule.tlsEnforced ?? false,
50+
fallbackEmails: rule.fallbackEmails ?? [],
51+
alias: rule.alias ?? {},
52+
},
53+
]);
4154
const bucket = new s3.Bucket(this, 'Bucket', {
4255
lifecycleRules: [
4356
{
@@ -75,24 +88,28 @@ export class MailDogStack extends cdk.Stack {
7588
sourceMap: false,
7689
define: {
7790
'process.env.CONFIG_PER_KEY_PREFIX': JSON.stringify(
78-
Object.entries(domains).reduce((result, [domain, rule]) => {
79-
result[`${domain}/`] = {
80-
fromEmail: `${rule.fromEmail ?? 'noreply'}@${domain}`,
81-
forwardMapping: Object.entries(rule.alias ?? {})
82-
.concat(
83-
rule.fallbackEmails
84-
? [['', { to: rule.fallbackEmails }]]
85-
: [],
86-
)
87-
.reduce((mapping, [prefix, entry]) => {
88-
mapping[`${prefix}@${domain}`] = entry.to;
89-
90-
return mapping;
91-
}, {} as Record<string, string[]>),
92-
};
93-
94-
return result;
95-
}, {} as Record<string, DispatcherConfig>),
91+
Object.fromEntries(
92+
domainRuleEntries.map<[string, DispatcherConfig]>(
93+
([domain, rule]) => [
94+
`${domain}/`,
95+
{
96+
fromEmail: `${rule.fromEmail}@${domain}`,
97+
forwardMapping: Object.fromEntries(
98+
Object.entries(rule.alias)
99+
.concat(
100+
rule.fallbackEmails.length > 0
101+
? [['', { to: rule.fallbackEmails }]]
102+
: [],
103+
)
104+
.map(([alias, entry]) => [
105+
`${alias}@${domain}`,
106+
entry.to ?? [],
107+
]),
108+
),
109+
},
110+
],
111+
),
112+
),
96113
),
97114
},
98115
},
@@ -146,13 +163,26 @@ export class MailDogStack extends cdk.Stack {
146163
const ruleset = new ses.ReceiptRuleSet(this, 'ReceiptRuleSet', {
147164
receiptRuleSetName: `${props.stackName ?? 'MailDog'}-ReceiptRuleSet`,
148165
dropSpam: false, // maybe a bug, it is not added as first rule
149-
rules: Object.entries(domains).flatMap(([domain, rule]) => {
166+
rules: domainRuleEntries.flatMap(([domain, rule]) => {
150167
const maxRecipientsPerRule = 100;
151-
const recipients = rule.fallbackEmails
152-
? [domain]
153-
: Object.keys(rule.alias ?? {}).map(
154-
(prefix) => `${prefix}@${domain}`,
155-
);
168+
const recipients =
169+
rule.fallbackEmails.length > 0
170+
? [domain]
171+
: Object.entries(rule.alias)
172+
.filter(([_, entry]) => {
173+
if (
174+
typeof entry.to === 'undefined' ||
175+
entry.to.length === 0
176+
) {
177+
console.warn(
178+
'[maildog] Alias with no forwarding email addresses found; It will be disabled if no fallback emails are set',
179+
);
180+
return false;
181+
}
182+
183+
return true;
184+
})
185+
.map(([alias]) => `${alias}@${domain}`);
156186
const rules = recipients
157187
.reduce((chunks, _, i, list) => {
158188
if (i % maxRecipientsPerRule === 0) {

0 commit comments

Comments
 (0)