feat: add list emails api (#167)
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
---
|
||||
openapi: get /v1/contactBooks/{contactBookId}/contacts/
|
||||
openapi: get /v1/contactBooks/{contactBookId}/contacts
|
||||
---
|
||||
|
3
apps/docs/api-reference/emails/list-emails.mdx
Normal file
3
apps/docs/api-reference/emails/list-emails.mdx
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
openapi: get /v1/emails
|
||||
---
|
@@ -484,6 +484,223 @@
|
||||
}
|
||||
},
|
||||
"/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": {
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
@@ -495,21 +712,18 @@
|
||||
"to": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "email"
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "email"
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"from": {
|
||||
"type": "string",
|
||||
"format": "email"
|
||||
"type": "string"
|
||||
},
|
||||
"subject": {
|
||||
"type": "string",
|
||||
@@ -652,21 +866,18 @@
|
||||
"to": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "email"
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "email"
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"from": {
|
||||
"type": "string",
|
||||
"format": "email"
|
||||
"type": "string"
|
||||
},
|
||||
"subject": {
|
||||
"type": "string",
|
||||
|
@@ -77,6 +77,7 @@
|
||||
"group": "Emails",
|
||||
"pages": [
|
||||
"api-reference/emails/get-email",
|
||||
"api-reference/emails/list-emails",
|
||||
"api-reference/emails/send-email",
|
||||
"api-reference/emails/batch-email",
|
||||
"api-reference/emails/update-schedule",
|
||||
|
@@ -1,4 +1,4 @@
|
||||
export const DEFAULT_QUERY_LIMIT = 30;
|
||||
export const DEFAULT_QUERY_LIMIT = 50;
|
||||
|
||||
/* Reputation constants */
|
||||
export const HARD_BOUNCE_WARNING_RATE = 5;
|
||||
|
170
apps/web/src/server/public-api/api/emails/list-emails.ts
Normal file
170
apps/web/src/server/public-api/api/emails/list-emails.ts
Normal 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;
|
@@ -2,6 +2,7 @@ import { getApp } from "./hono";
|
||||
import getDomains from "./api/domains/get-domains";
|
||||
import sendEmail from "./api/emails/send-email";
|
||||
import getEmail from "./api/emails/get-email";
|
||||
import listEmails from "./api/emails/list-emails";
|
||||
import addContact from "./api/contacts/add-contact";
|
||||
import updateContactInfo from "./api/contacts/update-contact";
|
||||
import getContact from "./api/contacts/get-contact";
|
||||
@@ -23,6 +24,7 @@ verifyDomain(app);
|
||||
|
||||
/**Email related APIs */
|
||||
getEmail(app);
|
||||
listEmails(app);
|
||||
sendEmail(app);
|
||||
sendBatch(app);
|
||||
updateEmailScheduledAt(app);
|
||||
|
Reference in New Issue
Block a user