Skip to content

Commit 3dc475b

Browse files
authored
Merge pull request #1 from roverdiani/master
feat: add SES integration with email management features
2 parents 1eb858c + a4ee974 commit 3dc475b

16 files changed

Lines changed: 841 additions & 37 deletions

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ services:
88
- "127.0.0.1:4566:4566" # LocalStack Edge Proxy
99
environment:
1010
- DEBUG=${DEBUG:-0}
11-
- SERVICES=sns,sqs,s3,lambda,dynamodb,kinesis,kms
11+
- SERVICES=sns,sqs,s3,ses,lambda,dynamodb,kinesis,kms
1212
- DISABLE_CORS_CHECKS=1
1313
- DISABLE_CORS_HANDLERS=1
1414
- SKIP_CORS_PREFLIGHT=1

package-lock.json

Lines changed: 373 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@
1111
},
1212
"dependencies": {
1313
"@aws-sdk/client-dynamodb": "^3.400.0",
14+
"@aws-sdk/client-kinesis": "^3.400.0",
15+
"@aws-sdk/client-kms": "^3.400.0",
16+
"@aws-sdk/client-lambda": "^3.400.0",
1417
"@aws-sdk/client-s3": "^3.400.0",
15-
"@aws-sdk/client-sqs": "^3.400.0",
18+
"@aws-sdk/client-ses": "^3.839.0",
1619
"@aws-sdk/client-sns": "^3.400.0",
17-
"@aws-sdk/client-lambda": "^3.400.0",
18-
"@aws-sdk/client-kms": "^3.400.0",
19-
"@aws-sdk/client-kinesis": "^3.400.0",
20+
"@aws-sdk/client-sqs": "^3.400.0",
2021
"@mdi/font": "^7.4.0",
2122
"axios": "^1.6.0",
2223
"jszip": "^3.10.1",

src/App.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ const menuItems = [
170170
{ title: 'KMS Keys', icon: 'mdi-key', to: '/kms' },
171171
{ title: 'Lambda Functions', icon: 'mdi-function', to: '/lambda' },
172172
{ title: 'S3 Buckets', icon: 'mdi-folder-multiple', to: '/s3' },
173+
{ title: 'SES Emails', icon: 'mdi-email-multiple', to: '/ses' },
173174
{ title: 'SNS Topics', icon: 'mdi-forum', to: '/sns' },
174175
{ title: 'SQS Queues', icon: 'mdi-format-list-bulleted', to: '/sqs' },
175176
]

src/router/routes.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import KinesisView from '@/views/KinesisView.vue'
77
import SNSView from '@/views/SNSView.vue'
88
import KMSView from '@/views/KMSView.vue'
99
import AboutView from '@/views/AboutView.vue'
10+
import SESView from '@/views/SESView.vue'
1011

1112
export default [
1213
{
@@ -19,6 +20,11 @@ export default [
1920
name: 'S3',
2021
component: S3View
2122
},
23+
{
24+
path: '/ses',
25+
name: 'SES',
26+
component: SESView
27+
},
2228
{
2329
path: '/sqs',
2430
name: 'SQS',

src/stores/app.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { defineStore } from 'pinia'
22
import { ref } from 'vue'
33
import { S3Client } from '@aws-sdk/client-s3'
4+
import { SESClient} from '@aws-sdk/client-ses'
45
import { SNSClient } from '@aws-sdk/client-sns'
56
import { SQSClient } from '@aws-sdk/client-sqs'
67
import { DynamoDBClient } from '@aws-sdk/client-dynamodb'
@@ -35,6 +36,7 @@ export const useAppStore = defineStore('app', () => {
3536

3637
// Initialize AWS services
3738
const s3 = ref(null)
39+
const ses = ref(null)
3840
const sns = ref(null)
3941
const sqs = ref(null)
4042
const dynamodb = ref(null)
@@ -53,6 +55,7 @@ export const useAppStore = defineStore('app', () => {
5355
}
5456

5557
s3.value = new S3Client(config)
58+
ses.value = new SESClient(config)
5659
sns.value = new SNSClient(config)
5760
sqs.value = new SQSClient(config)
5861
dynamodb.value = new DynamoDBClient(config)
@@ -106,6 +109,7 @@ export const useAppStore = defineStore('app', () => {
106109
connectionStatus,
107110
snackbar,
108111
s3,
112+
ses,
109113
sns,
110114
sqs,
111115
dynamodb,

src/utils/api.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import axios from 'axios'
2+
3+
const api = axios.create({
4+
baseURL: import.meta.env.VITE_LOCALSTACK_ENDPOINT || 'http://localhost:4566',
5+
timeout: 10000, // 10 seconds
6+
})
7+
8+
export const getSesData = async (params = {}) => {
9+
try {
10+
const response = await api.get('/_aws/ses', { params })
11+
return response.data
12+
} catch (error) {
13+
console.error('Error fetching SES data:', error);
14+
throw error
15+
}
16+
}
17+
18+
export const deleteSesMessage = async (params = {}) => {
19+
try {
20+
await api.delete('/_aws/ses', { params })
21+
} catch (error) {
22+
console.error('Error deleting SES message:', error);
23+
throw error
24+
}
25+
}

src/utils/formatDate.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const formatDate = (date) => {
2+
if (!date) return 'N/A'
3+
return new Date(date).toLocaleString('pt-BR')
4+
}

src/views/Dashboard.vue

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
:color="service.status === 'active' ? 'primary' : 'grey-lighten-2'"
1616
:variant="service.status === 'active' ? 'elevated' : 'outlined'"
1717
class="pa-4"
18-
height="120"
18+
height="150"
1919
@click="service.status === 'active' ? navigateToService(service.route): null"
2020
style="cursor: pointer"
2121
>
@@ -127,10 +127,12 @@ import { ListFunctionsCommand } from '@aws-sdk/client-lambda'
127127
import { ListBucketsCommand, ListObjectsV2Command, DeleteObjectsCommand, DeleteBucketCommand } from '@aws-sdk/client-s3'
128128
import { ListTopicsCommand } from '@aws-sdk/client-sns'
129129
import { ListQueuesCommand, DeleteQueueCommand } from '@aws-sdk/client-sqs'
130+
import { ListIdentitiesCommand } from '@aws-sdk/client-ses'
131+
import { deleteSesMessage, getSesData } from '@/utils/api.js'
130132
131133
const router = useRouter()
132134
const appStore = useAppStore()
133-
const { s3, sns, sqs, dynamodb, lambda, kinesis, kms } = storeToRefs(appStore)
135+
const { s3, ses, sns, sqs, dynamodb, lambda, kinesis, kms } = storeToRefs(appStore)
134136
135137
const services = ref([
136138
{
@@ -168,6 +170,13 @@ const services = ref([
168170
status: 'inactive',
169171
stats: null
170172
},
173+
{
174+
name: 'SES',
175+
icon: 'mdi-email',
176+
route: '/ses',
177+
status: 'inactive',
178+
stats: null
179+
},
171180
{
172181
name: 'SNS',
173182
icon: 'mdi-forum',
@@ -206,6 +215,13 @@ const quickActions = ref([
206215
action: 'clearDynamoDB',
207216
loading: false
208217
},
218+
{
219+
title: 'Limpar SES',
220+
icon: 'mdi-email-remove',
221+
color: 'error',
222+
action: 'clearSES',
223+
loading: false
224+
},
209225
{
210226
title: 'Atualizar Status',
211227
icon: 'mdi-refresh',
@@ -290,28 +306,42 @@ const loadServiceStats = async () => {
290306
services.value[4].status = 'inactive'
291307
}
292308
293-
// SNS Stats
309+
// SES Stats
294310
try {
295-
const snsTopics = await sns.value.send(new ListTopicsCommand({}))
311+
const sesIdentities = await ses.value.send(new ListIdentitiesCommand({ IdentityType: 'EmailAddress' }))
312+
const sesData = await getSesData();
296313
services.value[5].status = 'active'
297314
services.value[5].stats = {
315+
'E-mails': sesData.messages ? sesData.messages.length : 0,
316+
'Identidades': sesIdentities.Identities ? sesIdentities.Identities.length : 0
317+
}
318+
} catch (error) {
319+
console.error('SES error:', error)
320+
services.value[5].status = 'inactive'
321+
}
322+
323+
// SNS Stats
324+
try {
325+
const snsTopics = await sns.value.send(new ListTopicsCommand({}))
326+
services.value[6].status = 'active'
327+
services.value[6].stats = {
298328
'Tópicos': snsTopics.Topics ? snsTopics.Topics.length : 0
299329
}
300330
} catch (error) {
301331
console.error('SNS error:', error)
302-
services.value[5].status = 'inactive'
332+
services.value[6].status = 'inactive'
303333
}
304334
305335
// SQS Stats
306336
try {
307337
const sqsQueues = await sqs.value.send(new ListQueuesCommand({}))
308-
services.value[6].status = 'active'
309-
services.value[6].stats = {
338+
services.value[7].status = 'active'
339+
services.value[7].stats = {
310340
'Filas': sqsQueues.QueueUrls ? sqsQueues.QueueUrls.length : 0
311341
}
312342
} catch (error) {
313343
console.error('SQS error:', error)
314-
services.value[6].status = 'inactive'
344+
services.value[7].status = 'inactive'
315345
}
316346
317347
} catch (error) {
@@ -344,6 +374,11 @@ const executeQuickAction = (action) => {
344374
text = 'Esta ação irá deletar todas as filas SQS. Deseja continuar?'
345375
color = 'warning'
346376
break
377+
case 'clearSES':
378+
title = 'Limpar todos os emails SES'
379+
text = 'Esta ação irá deletar todos os emails do SES. Deseja continuar?'
380+
color = 'error'
381+
break
347382
case 'clearDynamoDB':
348383
title = 'Limpar todas as tabelas DynamoDB'
349384
text = 'Esta ação irá deletar todas as tabelas DynamoDB. Deseja continuar?'
@@ -373,6 +408,9 @@ const performClearAction = async (action) => {
373408
case 'clearSQS':
374409
await clearAllSQSQueues()
375410
break
411+
case 'clearSES':
412+
await clearAllSESData()
413+
break
376414
case 'clearDynamoDB':
377415
await clearAllDynamoDBTables()
378416
break
@@ -422,6 +460,10 @@ const clearAllSQSQueues = async () => {
422460
}
423461
}
424462
463+
const clearAllSESData = async () => {
464+
await deleteSesMessage()
465+
}
466+
425467
const clearAllDynamoDBTables = async () => {
426468
const tables = await dynamodb.value.send(new ListTablesCommand({}))
427469

src/views/DynamoDBView.vue

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ import {
304304
DeleteItemCommand,
305305
PutItemCommand
306306
} from '@aws-sdk/client-dynamodb'
307+
import { formatDate } from '../utils/formatDate.js'
307308
308309
const appStore = useAppStore()
309310
const { dynamodb } = storeToRefs(appStore)
@@ -622,10 +623,6 @@ const getStatusColor = (status) => {
622623
}
623624
}
624625
625-
const formatDate = (date) => {
626-
return new Date(date).toLocaleString('pt-BR')
627-
}
628-
629626
const formatBytes = (bytes) => {
630627
if (bytes === 0) return '0 Bytes'
631628
const k = 1024

0 commit comments

Comments
 (0)