feat: support create short link with api

This commit is contained in:
oiov
2025-04-05 20:34:31 +08:00
parent 29c8b91817
commit ed7cef5bf8
13 changed files with 2732 additions and 14 deletions
@@ -0,0 +1,35 @@
import Link from "next/link";
import { Badge } from "@/components/ui/badge";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
export default function ApiReference() {
return (
<Card>
<CardHeader>
<CardTitle>API Reference</CardTitle>
</CardHeader>
<CardContent>
<Badge>POST /api/v1/short</Badge>
<div className="mt-2">
We provide a simple API for creating short URLs. See usage
instructions at{" "}
<Link
href={"/docs/short-urls#api-reference"}
target="_blank"
className="font-semibold after:content-['_↗'] hover:text-blue-500 hover:underline"
>
api reference
</Link>
.
</div>
</CardContent>
</Card>
);
}
+3 -1
View File
@@ -4,6 +4,7 @@ import { getCurrentUser } from "@/lib/session";
import { constructMetadata } from "@/lib/utils";
import { DashboardHeader } from "@/components/dashboard/header";
import ApiReference from "./api-reference";
import LiveLog from "./live-logs";
import UserUrlsList from "./url-list";
@@ -23,7 +24,7 @@ export default async function DashboardPage() {
heading="Manage&nbsp;Short&nbsp;URLs"
text="List and manage short urls."
link="/docs/short-urls"
linkText="Short urls."
linkText="short urls."
/>
<UserUrlsList
user={{
@@ -35,6 +36,7 @@ export default async function DashboardPage() {
action="/api/url"
/>
<LiveLog admin={false} />
<ApiReference />
</>
);
}
-2
View File
@@ -10,8 +10,6 @@ export async function POST(req: Request) {
const user = checkUserStatus(await getCurrentUser());
if (user instanceof Response) return user;
const { NEXT_PUBLIC_FREE_URL_QUOTA } = env;
// check quota
const user_urls_count = await getUserShortUrlCount(user.id);
if (user_urls_count >= Team_Plan_Quota[user.team].SL_NewLinks) {
+63
View File
@@ -0,0 +1,63 @@
import { checkApiKey } from "@/lib/dto/api-key";
import { createUserShortUrl, getUserShortUrlCount } from "@/lib/dto/short-urls";
import { Team_Plan_Quota } from "@/lib/team";
import { createUrlSchema } from "@/lib/validations/url";
export async function POST(req: Request) {
try {
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_urls_count = await getUserShortUrlCount(user.id);
if (user_urls_count >= Team_Plan_Quota[user.team || "free"].SL_NewLinks) {
return Response.json("Your short urls have reached the free limit.", {
status: 403,
});
}
const data = await req.json();
const { target, url, prefix, visible, active, expiration } =
createUrlSchema.parse(data);
if (!target || !url) {
return Response.json("Target url and slug are required", {
status: 400,
});
}
const res = await createUserShortUrl({
userId: user.id,
userName: user.name || "Anonymous",
target,
url,
prefix,
visible,
active,
expiration,
});
if (res.status !== "success") {
return Response.json(res.status, {
status: 502,
});
}
return Response.json(res.data);
} catch (error) {
return Response.json(error?.statusText || error, {
status: error.status || 500,
});
}
}
+5 -5
View File
@@ -16,11 +16,6 @@ export const docsConfig: DocsConfig = {
href: "/docs/quick-start",
icon: "page",
},
{
title: "DNS Records",
href: "/docs/dns-records",
icon: "page",
},
{
title: "Short URLs",
href: "/docs/short-urls",
@@ -31,6 +26,11 @@ export const docsConfig: DocsConfig = {
href: "/docs/emails",
icon: "page",
},
{
title: "DNS Records",
href: "/docs/dns-records",
icon: "page",
},
],
},
{
@@ -88,15 +88,21 @@ Remember to add your environment variables in `wrangler.jsonc` before deploy.
### Config your domain email rule
Via:
```bash
https://dash.cloudflare.com/[account_id]/[zone_name]/email/routing/routes
```
Via the [https://dash.cloudflare.com/account_id/zone_name/email/routing/routes](https://dash.cloudflare.com/[account_id]/[zone_name]/email/routing/routes]),
edit `Catch-all address`, select:
- `Action` -> `Send to a worker`
- `Destination` -> `wrdo-email-worker`(The worker name you deploy).
Then save and active it.
<Callout type="warning" twClass="mb-3">
Once you add a new domain, you need to perform the same action, the email worker can be the same.
</Callout>
+57
View File
@@ -28,6 +28,63 @@ WR.DO provides a simple access statistics feature that can be used to track the
Once the generated short chain becomes invalid, it will not be deleted. When accessing the short chain again, it will be redirected to this page. Users can reset the short chain validity period to activate.
## API Reference
The `POST /api/v1/short` endpoint allows you to create a new short link for a given long URL.
```bash
curl -X POST \
-H "Content-Type: application/json" \
-H "wrdo-api-key: YOUR_API_KEY" \
-d '{
"target": "https://www.oiov.dev",
"url": "abc123",
"expiration": "-1",
"prefix": "wr.do",
"visible": 1,
"active": 1
}' \
https://wr.do/api/v1/short
```
### Request Body (Params)
```json
{
"target": "https://www.oiov.dev", // required
"url": "abc123", // required, slug
"expiration": "-1", // optional, seconds, default: "-1", "-1" means no expiration; "60" means 60 seconds
"prefix": "wr.do", // optional, default: wr.do
"visible": 1, // optional, default: 1, 1: visible, 0: invisible
"active": 1 // optional, default: 1, 1: active, 0: inactive
}
```
### 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
```json
{
"id": "c_abcd123",
"userId": "string",
"userName": "string",
"target": "https://www.example.com",
"url": "abc123",
"prefix": "wr.do",
"visible": 1,
"active": 1,
"expiration": "-1",
"createdAt": 2025-01-01T00:00:00.000Z,
"updatedAt": 2025-01-01T00:00:00.000Z
}
```
## Problems
### Expired Links
+3 -3
View File
@@ -11,15 +11,15 @@ export const getApiKeyByUserId = async (userId: string) => {
export const checkApiKey = async (apiKey: string) => {
return prisma.user.findFirst({
where: { apiKey },
select: { id: true },
where: { apiKey, active: 1 },
select: { id: true, team: true, name: true },
});
};
export const generateApiKey = async (userId: string) => {
const apiKey = crypto.randomUUID();
return prisma.user.update({
where: { id: userId },
where: { id: userId, active: 1 },
data: { apiKey },
select: { apiKey: true },
});
+101 -1
View File
File diff suppressed because one or more lines are too long
+1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long