feat: add list emails api (#167)

This commit is contained in:
KM Koushik
2025-06-01 10:07:57 +10:00
committed by GitHub
parent ae215abf1a
commit 4957ea822f
7 changed files with 401 additions and 14 deletions

View File

@@ -1,3 +1,3 @@
--- ---
openapi: get /v1/contactBooks/{contactBookId}/contacts/ openapi: get /v1/contactBooks/{contactBookId}/contacts
--- ---

View File

@@ -0,0 +1,3 @@
---
openapi: get /v1/emails
---

View File

@@ -484,6 +484,223 @@
} }
}, },
"/v1/emails": { "/v1/emails": {
"get": {
"parameters": [
{
"schema": {
"type": "string",
"default": "1",
"example": "1"
},
"required": false,
"name": "page",
"in": "query"
},
{
"schema": {
"type": "string",
"default": "50",
"example": "50"
},
"required": false,
"name": "limit",
"in": "query"
},
{
"schema": {
"type": "string",
"format": "date-time",
"example": "2024-01-01T00:00:00Z"
},
"required": false,
"name": "startDate",
"in": "query"
},
{
"schema": {
"type": "string",
"format": "date-time",
"example": "2024-01-31T23:59:59Z"
},
"required": false,
"name": "endDate",
"in": "query"
},
{
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"example": "123"
},
"required": false,
"name": "domainId",
"in": "query"
}
],
"responses": {
"200": {
"description": "Retrieve a list of emails",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"to": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"replyTo": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
},
{
"nullable": true
}
]
},
"cc": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
},
{
"nullable": true
}
]
},
"bcc": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
},
{
"nullable": true
}
]
},
"from": {
"type": "string"
},
"subject": {
"type": "string"
},
"html": {
"type": "string",
"nullable": true
},
"text": {
"type": "string",
"nullable": true
},
"createdAt": {
"type": "string"
},
"updatedAt": {
"type": "string"
},
"latestStatus": {
"type": "string",
"nullable": true,
"enum": [
"SCHEDULED",
"QUEUED",
"SENT",
"DELIVERY_DELAYED",
"BOUNCED",
"REJECTED",
"RENDERING_FAILURE",
"DELIVERED",
"OPENED",
"CLICKED",
"COMPLAINED",
"FAILED",
"CANCELLED"
]
},
"scheduledAt": {
"type": "string",
"nullable": true,
"format": "date-time"
},
"domainId": {
"type": "number",
"nullable": true
}
},
"required": [
"id",
"to",
"from",
"subject",
"html",
"text",
"createdAt",
"updatedAt",
"latestStatus",
"scheduledAt",
"domainId"
]
}
},
"count": {
"type": "number"
}
},
"required": [
"data",
"count"
]
}
}
}
}
}
},
"post": { "post": {
"requestBody": { "requestBody": {
"required": true, "required": true,
@@ -495,21 +712,18 @@
"to": { "to": {
"anyOf": [ "anyOf": [
{ {
"type": "string", "type": "string"
"format": "email"
}, },
{ {
"type": "array", "type": "array",
"items": { "items": {
"type": "string", "type": "string"
"format": "email"
} }
} }
] ]
}, },
"from": { "from": {
"type": "string", "type": "string"
"format": "email"
}, },
"subject": { "subject": {
"type": "string", "type": "string",
@@ -652,21 +866,18 @@
"to": { "to": {
"anyOf": [ "anyOf": [
{ {
"type": "string", "type": "string"
"format": "email"
}, },
{ {
"type": "array", "type": "array",
"items": { "items": {
"type": "string", "type": "string"
"format": "email"
} }
} }
] ]
}, },
"from": { "from": {
"type": "string", "type": "string"
"format": "email"
}, },
"subject": { "subject": {
"type": "string", "type": "string",

View File

@@ -77,6 +77,7 @@
"group": "Emails", "group": "Emails",
"pages": [ "pages": [
"api-reference/emails/get-email", "api-reference/emails/get-email",
"api-reference/emails/list-emails",
"api-reference/emails/send-email", "api-reference/emails/send-email",
"api-reference/emails/batch-email", "api-reference/emails/batch-email",
"api-reference/emails/update-schedule", "api-reference/emails/update-schedule",

View File

@@ -1,4 +1,4 @@
export const DEFAULT_QUERY_LIMIT = 30; export const DEFAULT_QUERY_LIMIT = 50;
/* Reputation constants */ /* Reputation constants */
export const HARD_BOUNCE_WARNING_RATE = 5; export const HARD_BOUNCE_WARNING_RATE = 5;

View File

@@ -0,0 +1,170 @@
import { createRoute, z } from "@hono/zod-openapi";
import { PublicAPIApp } from "~/server/public-api/hono";
import { db } from "~/server/db";
import { EmailStatus, Prisma } from "@prisma/client";
import { DEFAULT_QUERY_LIMIT } from "~/lib/constants";
const EmailSchema = z.object({
id: z.string(),
to: z.string().or(z.array(z.string())),
replyTo: z.string().or(z.array(z.string())).optional().nullable(),
cc: z.string().or(z.array(z.string())).optional().nullable(),
bcc: z.string().or(z.array(z.string())).optional().nullable(),
from: z.string(),
subject: z.string(),
html: z.string().nullable(),
text: z.string().nullable(),
createdAt: z.string(),
updatedAt: z.string(),
latestStatus: z.nativeEnum(EmailStatus).nullable(),
scheduledAt: z.string().datetime().nullable(),
domainId: z.number().nullable(),
});
const route = createRoute({
method: "get",
path: "/v1/emails",
request: {
query: z.object({
page: z
.string()
.optional()
.default("1")
.transform(Number)
.openapi({
param: {
name: "page",
in: "query",
},
example: "1",
}),
limit: z
.string()
.optional()
.default(String(DEFAULT_QUERY_LIMIT))
.pipe(z.coerce.number().min(1).max(DEFAULT_QUERY_LIMIT))
.openapi({
param: {
name: "limit",
in: "query",
},
example: String(DEFAULT_QUERY_LIMIT),
}),
startDate: z
.string()
.datetime()
.optional()
.openapi({
param: {
name: "startDate",
in: "query",
},
example: "2024-01-01T00:00:00Z",
}),
endDate: z
.string()
.datetime()
.optional()
.openapi({
param: {
name: "endDate",
in: "query",
},
example: "2024-01-31T23:59:59Z",
}),
domainId: z
.union([z.string(), z.array(z.string())])
.optional()
.transform((val) => {
if (!val) return undefined;
return (Array.isArray(val) ? val : [val]).map(Number);
})
.openapi({
param: {
name: "domainId",
in: "query",
},
example: "123", // or ["123", "456"]
}),
}),
},
responses: {
200: {
content: {
"application/json": {
schema: z.object({
data: z.array(EmailSchema),
count: z.number(),
}),
},
},
description: "Retrieve a list of emails",
},
},
});
function listEmails(app: PublicAPIApp) {
app.openapi(route, async (c) => {
const team = c.var.team;
const { page, limit, startDate, endDate, domainId } = c.req.valid("query");
const whereClause: Prisma.EmailWhereInput = {
teamId: team.id,
};
if (startDate) {
whereClause.createdAt = {
gte: new Date(startDate),
};
}
if (endDate) {
whereClause.createdAt = {
lte: new Date(endDate),
};
}
if (domainId && domainId.length > 0) {
whereClause.domainId = { in: domainId };
}
const [emails, count] = await db.$transaction([
db.email.findMany({
where: whereClause,
select: {
id: true,
to: true,
replyTo: true,
cc: true,
bcc: true,
from: true,
subject: true,
html: true,
text: true,
createdAt: true,
updatedAt: true,
latestStatus: true,
scheduledAt: true,
domainId: true,
},
orderBy: {
createdAt: "desc",
},
skip: (page - 1) * limit,
take: limit,
}),
db.email.count({ where: whereClause }),
]);
return c.json({
data: emails.map((email) => ({
...email,
createdAt: email.createdAt.toISOString(),
updatedAt: email.updatedAt.toISOString(),
scheduledAt: email.scheduledAt ? email.scheduledAt.toISOString() : null,
})),
count,
});
});
}
export default listEmails;

View File

@@ -2,6 +2,7 @@ import { getApp } from "./hono";
import getDomains from "./api/domains/get-domains"; import getDomains from "./api/domains/get-domains";
import sendEmail from "./api/emails/send-email"; import sendEmail from "./api/emails/send-email";
import getEmail from "./api/emails/get-email"; import getEmail from "./api/emails/get-email";
import listEmails from "./api/emails/list-emails";
import addContact from "./api/contacts/add-contact"; import addContact from "./api/contacts/add-contact";
import updateContactInfo from "./api/contacts/update-contact"; import updateContactInfo from "./api/contacts/update-contact";
import getContact from "./api/contacts/get-contact"; import getContact from "./api/contacts/get-contact";
@@ -23,6 +24,7 @@ verifyDomain(app);
/**Email related APIs */ /**Email related APIs */
getEmail(app); getEmail(app);
listEmails(app);
sendEmail(app); sendEmail(app);
sendBatch(app); sendBatch(app);
updateEmailScheduledAt(app); updateEmailScheduledAt(app);