Enables email address creation via API
This commit is contained in:
@@ -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 });
|
||||
}
|
||||
|
||||
|
||||
@@ -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 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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.
|
||||
@@ -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
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -20,6 +20,7 @@ export type SiteConfig = {
|
||||
};
|
||||
openSignup: boolean;
|
||||
shortDomains: string[];
|
||||
emailDomains: string[];
|
||||
emailR2Domain: string;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user