Enables email address creation via API

This commit is contained in:
oiov
2025-04-10 15:37:05 +08:00
parent 27da37f60d
commit 292f5911c8
12 changed files with 293 additions and 6 deletions
+3 -3
View File
@@ -45,14 +45,14 @@ export async function POST(req: NextRequest) {
return NextResponse.json("Missing userId or emailAddress", { status: 400 });
}
const suffix = emailAddress.split("@")[0];
if (!suffix || suffix < 5) {
const prefix = emailAddress.split("@")[0];
if (!prefix || prefix.length < 5) {
return NextResponse.json("Email address length must be at least 5", {
status: 400,
});
}
if (reservedAddressSuffix.includes(suffix)) {
if (reservedAddressSuffix.includes(prefix)) {
return NextResponse.json("Invalid email address", { status: 400 });
}
+49
View File
@@ -0,0 +1,49 @@
import { NextRequest, NextResponse } from "next/server";
import { checkApiKey } from "@/lib/dto/api-key";
import { getEmailsByEmailAddress } from "@/lib/dto/email";
// 通过 emailAddress 查询所有相关 ForwardEmail
export async function GET(req: NextRequest) {
const custom_api_key = req.headers.get("wrdo-api-key");
if (!custom_api_key) {
return Response.json("Unauthorized", {
status: 401,
});
}
// Check if the API key is valid
const user = await checkApiKey(custom_api_key);
if (!user?.id) {
return Response.json(
"Invalid API key. You can get your API key from https://wr.do/dashboard/settings.",
{ status: 401 },
);
}
const { searchParams } = new URL(req.url);
const emailAddress = searchParams.get("emailAddress");
const page = parseInt(searchParams.get("page") || "1", 10);
const pageSize = parseInt(searchParams.get("size") || "10", 10);
if (!emailAddress) {
return NextResponse.json(
{ error: "Missing emailAddress parameter" },
{ status: 400 },
);
}
try {
const emails = await getEmailsByEmailAddress(emailAddress, page, pageSize);
return NextResponse.json(emails, { status: 200 });
} catch (error) {
console.error("Error fetching emails:", error);
if (error.message === "Email address not found") {
return NextResponse.json({ error: error.message }, { status: 404 });
}
return NextResponse.json(
{ error: "Internal Server Error" },
{ status: 500 },
);
}
}
+73
View File
@@ -0,0 +1,73 @@
import { NextRequest, NextResponse } from "next/server";
import { checkApiKey } from "@/lib/dto/api-key";
import { createUserEmail, getAllUserEmailsCount } from "@/lib/dto/email";
import { reservedAddressSuffix } from "@/lib/enums";
import { Team_Plan_Quota } from "@/lib/team";
import { siteConfig } from "../../../../config/site";
// 创建新 UserEmail
export async function POST(req: NextRequest) {
const custom_api_key = req.headers.get("wrdo-api-key");
if (!custom_api_key) {
return Response.json("Unauthorized", {
status: 401,
});
}
// Check if the API key is valid
const user = await checkApiKey(custom_api_key);
if (!user?.id) {
return Response.json(
"Invalid API key. You can get your API key from https://wr.do/dashboard/settings.",
{ status: 401 },
);
}
// check quota
const user_address_count = await getAllUserEmailsCount(user.id);
if (
user_address_count >= Team_Plan_Quota[user.team || "free"].EM_EmailAddresses
) {
return Response.json("Your email addresses have reached the free limit.", {
status: 403,
});
}
const { emailAddress } = await req.json();
if (!emailAddress) {
return NextResponse.json("Missing userId or emailAddress", { status: 400 });
}
const [prefix, suffix] = emailAddress.split("@");
if (!prefix || prefix.length < 5) {
return NextResponse.json("Email address length must be at least 5", {
status: 400,
});
}
if (!siteConfig.emailDomains.includes(suffix)) {
return NextResponse.json("Invalid email suffix address", { status: 400 });
}
if (reservedAddressSuffix.includes(prefix)) {
return NextResponse.json("Invalid email address", { status: 400 });
}
try {
const userEmail = await createUserEmail(user.id, emailAddress);
return NextResponse.json(userEmail, { status: 201 });
} catch (error) {
// console.log("Error creating user email:", error);
if (error.message === "Invalid userId") {
return NextResponse.json({ error: error.message }, { status: 400 });
}
if (error.code === "P2002") {
return NextResponse.json("Email address already exists", {
status: 409,
});
}
return NextResponse.json(error.message, { status: 500 });
}
}
+28
View File
@@ -0,0 +1,28 @@
import Link from "next/link";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
export default function ApiReference() {
return (
<Card>
<CardHeader>
<CardTitle>API Reference</CardTitle>
</CardHeader>
<CardContent>
<Badge>POST /api/v1/email</Badge>
<div className="mt-2">
We provide a simple API for creating emails. See usage instructions at{" "}
<Link
href={"/docs/emails#api-reference"}
target="_blank"
className="font-semibold after:content-['_↗'] hover:text-blue-500 hover:underline"
>
api reference
</Link>
.
</div>
</CardContent>
</Card>
);
}
+10
View File
@@ -364,6 +364,16 @@ export function EmptyInboxSection() {
What is the limit? It's free?
</Link>
</li>
<li>
<Link
className="text-blue-500 underline"
href="/docs/emails#api-reference"
target="_blank"
rel="noreferrer"
>
How to create emails with api?
</Link>
</li>
</ul>
<div className="mt-6 flex gap-2">
<span className="h-2 w-2 animate-pulse rounded-full bg-neutral-300 dark:bg-neutral-600" />
+3 -2
View File
@@ -19,6 +19,7 @@ import { UserEmailList } from "@/lib/dto/email";
import { reservedAddressSuffix } from "@/lib/enums";
import { cn, fetcher, timeAgo } from "@/lib/utils";
import { useMediaQuery } from "@/hooks/use-media-query";
import ApiReference from "@/app/emails/api-reference";
import CountUp from "../dashboard/count-up";
import { CopyButton } from "../shared/copy-button";
@@ -544,14 +545,14 @@ export default function EmailSidebar({
setDomainSuffix(value);
}}
name="suffix"
defaultValue={domainSuffix || siteConfig.shortDomains[0]}
defaultValue={domainSuffix || siteConfig.emailDomains[0]}
disabled={isEdit}
>
<SelectTrigger className="w-1/3 rounded-none border-x-0 shadow-inner">
<SelectValue placeholder="Select a domain" />
</SelectTrigger>
<SelectContent>
{siteConfig.shortDomains.map((v) => (
{siteConfig.emailDomains.map((v) => (
<SelectItem key={v} value={v}>
@{v}
</SelectItem>
+2
View File
@@ -6,6 +6,7 @@ const free_recored_quota = env.NEXT_PUBLIC_FREE_RECORD_QUOTA;
const free_url_quota = env.NEXT_PUBLIC_FREE_URL_QUOTA;
const open_signup = env.NEXT_PUBLIC_OPEN_SIGNUP;
const short_domains = env.NEXT_PUBLIC_SHORT_DOMAINS || "";
const email_domains = env.NEXT_PUBLIC_EMAIL_DOMAINS || "";
const email_r2_domain = env.NEXT_PUBLIC_EMAIL_R2_DOMAIN || "";
export const siteConfig: SiteConfig = {
@@ -25,6 +26,7 @@ export const siteConfig: SiteConfig = {
},
openSignup: open_signup === "1" ? true : false,
shortDomains: short_domains.split(","),
emailDomains: email_domains.split(","),
emailR2Domain: email_r2_domain,
};
+1
View File
@@ -57,6 +57,7 @@ Copy/paste the `.env.example` in the `.env` file:
| SCREENSHOTONE_BASE_URL | `https://api.example.com` | pending |
| GITHUB_TOKEN | `ghp_sscsfarwetqet` | https://github.com/settings/tokens |
| NEXT_PUBLIC_SHORT_DOMAINS | `wr.do,uv.do` | The list of short domains. Separated by `,` |
| NEXT_PUBLIC_EMAIL_DOMAINS | `wr.do,uv.do` | The list of email domains. Separated by `,` |
- How to get `GOOGLE_CLIENT_ID`、`GITHUB_ID`, see [Authentification](/docs/developer/authentification).
- How to get `RESEND_API_KEY`, see [Email](/docs/developer/email).
+120
View File
@@ -36,4 +36,124 @@ Each email address can receive unlimited emails
For send emails, the maximum number of emails is 10 per day.
## API Reference
The Email API allows you to create and manage email addresses and retrieve received emails in your inbox.
### Create Email Address
The `POST /api/v1/email` endpoint allows you to create a new email address.
```bash
curl -X POST \
-H "Content-Type: application/json" \
-H "wrdo-api-key: YOUR_API_KEY" \
-d '{
"emailAddress": "your-suffix@wr.do"
}' \
https://wr.do/api/v1/email
```
#### Request Body (Params)
```json
{
"emailAddress": "your-suffix@wr.do" // required, suffix must be at least 5 characters
}
```
#### Authorization Header
- `wrdo-api-key`: You can use your API key to authenticate your requests.
You can find your API key in your [account settings](/dashboard/settings).
Add the header `wrdo-api-key: YOUR_API_KEY` to your request.
#### Response
On success (Status 201):
```json
{
id: string;
userId: string;
emailAddress: string;
createdAt: Date;
updatedAt: Date;
deletedAt: Date | null;
}
```
#### Error Responses
- `401 Unauthorized`: Missing or invalid API key
- `400 Bad Request`: Missing email address or invalid suffix (less than 5 characters)
- `403 Forbidden`: Email address quota has been reached
- `409 Conflict`: Email address already exists
- `500 Internal Server Error`: Server error
### Get Email Inbox
The `GET /api/v1/email/inbox` endpoint allows you to retrieve all forwarded emails for a specific email address.
<Callout type="warning" twClass="mt-0">
You must create a email address before you can get the inbox.
</Callout>
```bash
curl -X GET \
-H "wrdo-api-key: YOUR_API_KEY" \
"https://wr.do/api/v1/email/inbox?emailAddress=your-suffix@wr.do&page=1&size=10"
```
#### Query Parameters
- `emailAddress`: The email address to get the inbox for (required)
- `page`: Page number for pagination (optional, default: 1)
- `size`: Number of emails per page (optional, default: 10)
#### Authorization Header
- `wrdo-api-key`: You can use your API key to authenticate your requests.
You can find your API key in your [account settings](/dashboard/settings).
Add the header `wrdo-api-key: YOUR_API_KEY` to your request.
#### Response
On success (Status 200):
```json
{
"list": [
{
id: string
from: string
fromName: string
to: string
subject: string | null
text: string | null
html: string | null
date: string | null
messageId: string | null
replyTo: string | null
cc: string | null
headers: string | null
attachments: string | null
readAt: Date | null
createdAt: Date
updatedAt: Date
},
],
"total": 25
}
```
#### Error Responses
- `401 Unauthorized`: Missing or invalid API key
- `400 Bad Request`: Missing emailAddress parameter
- `404 Not Found`: Email address not found or has been deleted
- `500 Internal Server Error`: Server error
### Delete Email Address
The `DELETE /api/v1/email` endpoint allows you to delete a specific email address.
working on it.
+2
View File
@@ -28,6 +28,7 @@ export const env = createEnv({
NEXT_PUBLIC_FREE_URL_QUOTA: z.string().min(1).default("100"),
NEXT_PUBLIC_OPEN_SIGNUP: z.string().min(1).default("1"),
NEXT_PUBLIC_SHORT_DOMAINS: z.string().min(1).default(""),
NEXT_PUBLIC_EMAIL_DOMAINS: z.string().min(1).default(""),
NEXT_PUBLIC_EMAIL_R2_DOMAIN: z.string().min(1),
},
runtimeEnv: {
@@ -44,6 +45,7 @@ export const env = createEnv({
NEXT_PUBLIC_FREE_URL_QUOTA: process.env.NEXT_PUBLIC_FREE_URL_QUOTA,
NEXT_PUBLIC_OPEN_SIGNUP: process.env.NEXT_PUBLIC_OPEN_SIGNUP,
NEXT_PUBLIC_SHORT_DOMAINS: process.env.NEXT_PUBLIC_SHORT_DOMAINS,
NEXT_PUBLIC_EMAIL_DOMAINS: process.env.NEXT_PUBLIC_EMAIL_DOMAINS,
NEXT_PUBLIC_EMAIL_R2_DOMAIN: process.env.NEXT_PUBLIC_EMAIL_R2_DOMAIN,
CLOUDFLARE_ZONE_ID: process.env.CLOUDFLARE_ZONE_ID,
CLOUDFLARE_ZONE_NAME: process.env.CLOUDFLARE_ZONE_NAME,
+1 -1
View File
File diff suppressed because one or more lines are too long
+1
View File
@@ -20,6 +20,7 @@ export type SiteConfig = {
};
openSignup: boolean;
shortDomains: string[];
emailDomains: string[];
emailR2Domain: string;
};