diff --git a/app/(protected)/dashboard/urls/url-list.tsx b/app/(protected)/dashboard/urls/url-list.tsx
index 36fde00..23fe985 100644
--- a/app/(protected)/dashboard/urls/url-list.tsx
+++ b/app/(protected)/dashboard/urls/url-list.tsx
@@ -64,8 +64,8 @@ export interface UrlListProps {
function TableColumnSekleton() {
return (
-
-
+
+
diff --git a/app/api/email/route.ts b/app/api/email/route.ts
index 5332cec..8103c3b 100644
--- a/app/api/email/route.ts
+++ b/app/api/email/route.ts
@@ -4,6 +4,7 @@ import { createUserEmail, getAllUserEmails } from "@/lib/dto/email";
import { checkUserStatus } from "@/lib/dto/user";
import { reservedAddressSuffix } from "@/lib/enums";
import { getCurrentUser } from "@/lib/session";
+import { restrictByTimeRange, Team_Plan_Quota } from "@/lib/team";
// 查询所有 UserEmail 地址
export async function GET(req: NextRequest) {
@@ -41,6 +42,16 @@ export async function POST(req: NextRequest) {
const user = checkUserStatus(await getCurrentUser());
if (user instanceof Response) return user;
+ // check limit
+ const limit = await restrictByTimeRange({
+ model: "userEmail",
+ userId: user.id,
+ limit: Team_Plan_Quota[user.team].EM_EmailAddresses,
+ rangeType: "month",
+ });
+ if (limit.status !== 200)
+ return NextResponse.json(limit.statusText, { status: limit.status });
+
const { emailAddress } = await req.json();
if (!emailAddress) {
@@ -71,6 +82,7 @@ export async function POST(req: NextRequest) {
status: 409,
});
}
+
return NextResponse.json("Internal Server Error", { status: 500 });
}
}
diff --git a/app/api/email/send/route.ts b/app/api/email/send/route.ts
index a3ee906..01023f3 100644
--- a/app/api/email/send/route.ts
+++ b/app/api/email/send/route.ts
@@ -4,7 +4,7 @@ import { getUserSendEmailCount, saveUserSendEmail } from "@/lib/dto/email";
import { checkUserStatus } from "@/lib/dto/user";
import { resend } from "@/lib/email";
import { getCurrentUser } from "@/lib/session";
-import { Team_Plan_Quota } from "@/lib/team";
+import { restrictByTimeRange, Team_Plan_Quota } from "@/lib/team";
import { isValidEmail } from "@/lib/utils";
export async function POST(req: NextRequest) {
@@ -12,16 +12,15 @@ export async function POST(req: NextRequest) {
const user = checkUserStatus(await getCurrentUser());
if (user instanceof Response) return user;
- // check quota
- const count = await getUserSendEmailCount(user.id, false);
- if (count >= Team_Plan_Quota[user.team].EM_SendEmails) {
- return Response.json(
- `Your email addresses have reached the ${user.team} limit.`,
- {
- status: 409,
- },
- );
- }
+ // check limit
+ const limit = await restrictByTimeRange({
+ model: "userSendEmail",
+ userId: user.id,
+ limit: Team_Plan_Quota[user.team].EM_SendEmails,
+ rangeType: "month",
+ });
+ if (limit.status !== 200)
+ return NextResponse.json(limit.statusText, { status: limit.status });
const { from, to, subject, html } = await req.json();
@@ -33,7 +32,7 @@ export async function POST(req: NextRequest) {
return NextResponse.json("Invalid email address", { status: 403 });
}
- const { data, error } = await resend.emails.send({
+ const { error } = await resend.emails.send({
from,
to,
subject,
diff --git a/app/api/url/add/route.ts b/app/api/url/add/route.ts
index 5f097be..18c4b76 100644
--- a/app/api/url/add/route.ts
+++ b/app/api/url/add/route.ts
@@ -2,7 +2,7 @@ import { env } from "@/env.mjs";
import { createUserShortUrl, getUserShortUrlCount } from "@/lib/dto/short-urls";
import { checkUserStatus } from "@/lib/dto/user";
import { getCurrentUser } from "@/lib/session";
-import { Team_Plan_Quota } from "@/lib/team";
+import { restrictByTimeRange, Team_Plan_Quota } from "@/lib/team";
import { createUrlSchema } from "@/lib/validations/url";
export async function POST(req: Request) {
@@ -10,13 +10,15 @@ export async function POST(req: Request) {
const user = checkUserStatus(await getCurrentUser());
if (user instanceof Response) return user;
- // check quota
- const user_urls_count = await getUserShortUrlCount(user.id);
- if (user_urls_count >= Team_Plan_Quota[user.team].SL_NewLinks) {
- return Response.json("Your short urls have reached the free limit.", {
- status: 409,
- });
- }
+ // check limit
+ const limit = await restrictByTimeRange({
+ model: "userUrl",
+ userId: user.id,
+ limit: Team_Plan_Quota[user.team].SL_NewLinks,
+ rangeType: "month",
+ });
+ if (limit.status !== 200)
+ return Response.json(limit.statusText, { status: limit.status });
const { data } = await req.json();
diff --git a/app/api/v1/email/route.ts b/app/api/v1/email/route.ts
index fbc3bf2..9dc5468 100644
--- a/app/api/v1/email/route.ts
+++ b/app/api/v1/email/route.ts
@@ -7,7 +7,7 @@ import {
getAllUserEmailsCount,
} from "@/lib/dto/email";
import { reservedAddressSuffix } from "@/lib/enums";
-import { Team_Plan_Quota } from "@/lib/team";
+import { restrictByTimeRange, Team_Plan_Quota } from "@/lib/team";
import { siteConfig } from "../../../../config/site";
@@ -35,15 +35,15 @@ export async function POST(req: NextRequest) {
});
}
- // 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,
- });
- }
+ // check limit
+ const limit = await restrictByTimeRange({
+ model: "userEmail",
+ userId: user.id,
+ limit: Team_Plan_Quota[user.team!].EM_EmailAddresses,
+ rangeType: "month",
+ });
+ if (limit.status !== 200)
+ return NextResponse.json(limit.statusText, { status: limit.status });
const { emailAddress } = await req.json();
diff --git a/app/api/v1/short/route.ts b/app/api/v1/short/route.ts
index 0b86492..108b501 100644
--- a/app/api/v1/short/route.ts
+++ b/app/api/v1/short/route.ts
@@ -1,6 +1,6 @@
import { checkApiKey } from "@/lib/dto/api-key";
import { createUserShortUrl, getUserShortUrlCount } from "@/lib/dto/short-urls";
-import { Team_Plan_Quota } from "@/lib/team";
+import { restrictByTimeRange, Team_Plan_Quota } from "@/lib/team";
import { createUrlSchema } from "@/lib/validations/url";
export async function POST(req: Request) {
@@ -21,13 +21,15 @@ export async function POST(req: Request) {
);
}
- // 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,
- });
- }
+ // check limit
+ const limit = await restrictByTimeRange({
+ model: "userUrl",
+ userId: user.id,
+ limit: Team_Plan_Quota[user.team!].SL_NewLinks,
+ rangeType: "month",
+ });
+ if (limit.status !== 200)
+ return Response.json(limit.statusText, { status: limit.status });
const data = await req.json();
diff --git a/components/email/EmailSidebar.tsx b/components/email/EmailSidebar.tsx
index ab6c496..ec0d007 100644
--- a/components/email/EmailSidebar.tsx
+++ b/components/email/EmailSidebar.tsx
@@ -17,6 +17,7 @@ import useSWR from "swr";
import { siteConfig } from "@/config/site";
import { UserEmailList } from "@/lib/dto/email";
import { reservedAddressSuffix } from "@/lib/enums";
+import { Team_Plan_Quota } from "@/lib/team";
import { cn, fetcher, timeAgo } from "@/lib/utils";
import { useMediaQuery } from "@/hooks/use-media-query";
import ApiReference from "@/app/emails/api-reference";
@@ -349,6 +350,10 @@ export default function EmailSidebar({
+ {/* {" "}
+
+ / {Team_Plan_Quota[user.team!].EM_SendEmails}
+ */}
diff --git a/components/sections/pricing.tsx b/components/sections/pricing.tsx
index d935b93..ba6ddd6 100644
--- a/components/sections/pricing.tsx
+++ b/components/sections/pricing.tsx
@@ -5,11 +5,60 @@ import type { CSSProperties, ReactNode } from "react";
import { motion } from "framer-motion";
import { Check, X } from "lucide-react";
-import { cn } from "@/lib/utils";
+import { Team_Plan_Quota } from "@/lib/team";
+import { cn, nFormatter } from "@/lib/utils";
import { Icons } from "../shared/icons";
import { Button } from "../ui/button";
+const getBenefits = (plan) => [
+ {
+ text: `${nFormatter(plan.SL_TrackedClicks)} tracked clicks/mo`,
+ checked: true,
+ icon: ,
+ },
+ {
+ text: `${nFormatter(plan.SL_NewLinks)} new links/mo`,
+ checked: true,
+ icon: ,
+ },
+ {
+ text: `${plan.SL_AnalyticsRetention}-day analytics retention`,
+ checked: true,
+ icon: ,
+ },
+ {
+ text: `${nFormatter(plan.EM_EmailAddresses)} email addresses/mo`,
+ checked: true,
+ icon: , // Updated icon to be more relevant
+ },
+ {
+ text: `${nFormatter(plan.EM_SendEmails)} send emails/mo`,
+ checked: true,
+ icon: , // Updated icon to be more relevant
+ },
+ {
+ text: `${plan.SL_Domains === 1 ? "One" : plan.SL_Domains} domain${plan.SL_Domains > 1 ? "s" : ""}`,
+ checked: true,
+ icon: ,
+ },
+ {
+ text: "Advanced analytics",
+ checked: plan.SL_AdvancedAnalytics,
+ icon: ,
+ },
+ {
+ text: `${plan.APP_Support.charAt(0).toUpperCase() + plan.APP_Support.slice(1)} support`,
+ checked: true,
+ icon: ,
+ },
+ {
+ text: "API Access",
+ checked: plan.APP_ApiAccess,
+ icon: ,
+ },
+];
+
export const PricingSection = () => {
return (
@@ -25,7 +74,7 @@ export const PricingSection = () => {
-
+
{
Get started free
}
- benefits={[
- {
- text: "100K tracked clicks",
- checked: true,
- icon: ,
- },
- {
- text: "1k new links",
- checked: true,
- icon: ,
- },
- {
- text: "180-day analytics retention",
- checked: true,
- icon: ,
- },
- {
- text: "1k emails addresses",
- checked: true,
- icon: ,
- },
- {
- text: "One domain",
- checked: true,
- icon: ,
- },
- {
- text: "Advanced analytics",
- checked: true,
- icon: ,
- },
- {
- text: "Basic support",
- checked: true,
- icon: ,
- },
- {
- text: "API Access",
- checked: true,
- icon: ,
- },
- ]}
+ benefits={getBenefits(Team_Plan_Quota.free)}
/>
- {/*
+
}
- benefits={[
- { text: "Five workspaces", checked: true },
- { text: "Email support", checked: true },
- { text: "7 day data retention", checked: true },
- { text: "Custom roles", checked: true },
- { text: "Priority support", checked: false },
- { text: "SSO", checked: false },
- ]}
- /> */}
+ benefits={getBenefits(Team_Plan_Quota.premium)}
+ />
{
Contact us
}
- benefits={[
- {
- text: "Unlimited tracked clicks/mo",
- checked: true,
- icon: ,
- },
- {
- text: "Unlimited new links",
- checked: true,
- icon: ,
- },
- {
- text: "1-year analytics retention",
- checked: true,
- icon: ,
- },
- {
- text: "1k emails addresses",
- checked: true,
- icon: ,
- },
- {
- text: "Three domain",
- checked: true,
- icon: ,
- },
- {
- text: "Advanced analytics",
- checked: true,
- icon: ,
- },
- {
- text: "Basic support",
- checked: true,
- icon: ,
- },
- {
- text: "API Access",
- checked: true,
- icon: ,
- },
- ]}
+ benefits={getBenefits(Team_Plan_Quota.business)}
/>
diff --git a/lib/team.ts b/lib/team.ts
index b84087e..24479f9 100644
--- a/lib/team.ts
+++ b/lib/team.ts
@@ -1,26 +1,30 @@
+import { PrismaClient } from "@prisma/client";
+
+import { prisma } from "./db";
+
export const Team_Plan_Quota = {
free: {
SL_TrackedClicks: 100000,
SL_NewLinks: 1000,
SL_AnalyticsRetention: 180,
- SL_Domains: 1,
+ SL_Domains: 2,
SL_AdvancedAnalytics: true,
RC_NewRecords: 0,
EM_EmailAddresses: 1000,
- EM_Domains: 1,
- EM_SendEmails: 100,
+ EM_Domains: 2,
+ EM_SendEmails: 200,
APP_Support: "basic",
APP_ApiAccess: true,
},
premium: {
- SL_TrackedClicks: 10000000,
- SL_NewLinks: 10000,
+ SL_TrackedClicks: 1000000,
+ SL_NewLinks: 5000,
SL_AnalyticsRetention: 360,
- SL_Domains: 3,
+ SL_Domains: 2,
SL_AdvancedAnalytics: true,
- RC_NewRecords: 3,
- EM_EmailAddresses: 10000,
- EM_Domains: 3,
+ RC_NewRecords: 2,
+ EM_EmailAddresses: 5000,
+ EM_Domains: 2,
EM_SendEmails: 1000,
APP_Support: "live",
APP_ApiAccess: true,
@@ -29,13 +33,88 @@ export const Team_Plan_Quota = {
SL_TrackedClicks: 10000000,
SL_NewLinks: 10000,
SL_AnalyticsRetention: 360,
- SL_Domains: 3,
+ SL_Domains: 2,
SL_AdvancedAnalytics: true,
- RC_NewRecords: 3,
+ RC_NewRecords: 2,
EM_EmailAddresses: 10000,
- EM_Domains: 3,
- EM_SendEmails: 1000,
+ EM_Domains: 2,
+ EM_SendEmails: 2000,
APP_Support: "live",
APP_ApiAccess: true,
},
};
+
+type TimeRangeType = "day" | "month"; // 支持的限制周期
+
+interface RestrictOptions {
+ model: keyof PrismaClient; // Prisma 模型名称,如 'userEmail'
+ userId: string; // 用户 ID
+ limit: number; // 限制数量
+ rangeType: TimeRangeType; // 限制周期:'day' 或 'month'
+ referenceDate?: Date; // 可选,参考日期,默认为当前时间
+}
+
+/**
+ * 根据指定时间范围检查记录数量是否超过限制
+ * @throws 如果超过限制,抛出错误
+ */
+export async function restrictByTimeRange({
+ model,
+ userId,
+ limit,
+ rangeType,
+ referenceDate,
+}: RestrictOptions) {
+ const now = referenceDate || new Date();
+
+ let start: Date;
+ let end: Date;
+
+ if (rangeType === "day") {
+ start = new Date(now.getFullYear(), now.getMonth(), now.getDate());
+ end = new Date(
+ now.getFullYear(),
+ now.getMonth(),
+ now.getDate(),
+ 23,
+ 59,
+ 59,
+ 999,
+ );
+ } else if (rangeType === "month") {
+ start = new Date(now.getFullYear(), now.getMonth(), 1);
+ end = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999);
+ } else {
+ return {
+ status: 400,
+ statusText: `Invalid range type: ${rangeType}`,
+ };
+ }
+
+ const modelInstance = prisma[model] as any;
+
+ if (!modelInstance) {
+ return {
+ status: 400,
+ statusText: `Invalid model: ${model.toString()}`,
+ };
+ }
+
+ const count = await modelInstance.count({
+ where: {
+ userId,
+ createdAt: {
+ gte: start,
+ lte: end,
+ },
+ },
+ });
+
+ if (count >= limit) {
+ return {
+ status: 409,
+ statusText: `You have exceeded the ${rangeType}ly ${model.toString()} creation limit (${limit}). Please try again later.`,
+ };
+ }
+ return { status: 200 };
+}
diff --git a/public/sw.js.map b/public/sw.js.map
index c1f9ec4..32eeb12 100644
--- a/public/sw.js.map
+++ b/public/sw.js.map
@@ -1 +1 @@
-{"version":3,"file":"sw.js","sources":["../../../../../../private/var/folders/9b/3qmyp8zd2xvdspdrp149fyg00000gn/T/98412ccf39afa06d5c43cfd6a4f0a40b/sw.js"],"sourcesContent":["import {registerRoute as workbox_routing_registerRoute} from '/Users/songjunxi/Desktop/repos/wrdo-app/wr.do/node_modules/.pnpm/workbox-routing@6.6.0/node_modules/workbox-routing/registerRoute.mjs';\nimport {NetworkFirst as workbox_strategies_NetworkFirst} from '/Users/songjunxi/Desktop/repos/wrdo-app/wr.do/node_modules/.pnpm/workbox-strategies@6.6.0/node_modules/workbox-strategies/NetworkFirst.mjs';\nimport {NetworkOnly as workbox_strategies_NetworkOnly} from '/Users/songjunxi/Desktop/repos/wrdo-app/wr.do/node_modules/.pnpm/workbox-strategies@6.6.0/node_modules/workbox-strategies/NetworkOnly.mjs';\nimport {clientsClaim as workbox_core_clientsClaim} from '/Users/songjunxi/Desktop/repos/wrdo-app/wr.do/node_modules/.pnpm/workbox-core@6.6.0/node_modules/workbox-core/clientsClaim.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\nimportScripts(\n \n);\n\n\n\n\n\n\n\nself.skipWaiting();\n\nworkbox_core_clientsClaim();\n\n\n\nworkbox_routing_registerRoute(\"/\", new workbox_strategies_NetworkFirst({ \"cacheName\":\"start-url\", plugins: [{ cacheWillUpdate: async ({ request, response, event, state }) => { if (response && response.type === 'opaqueredirect') { return new Response(response.body, { status: 200, statusText: 'OK', headers: response.headers }) } return response } }] }), 'GET');\nworkbox_routing_registerRoute(/.*/i, new workbox_strategies_NetworkOnly({ \"cacheName\":\"dev\", plugins: [] }), 'GET');\n\n\n\n\n"],"names":["importScripts","self","skipWaiting","workbox_core_clientsClaim","workbox_routing_registerRoute","workbox_strategies_NetworkFirst","plugins","cacheWillUpdate","request","response","event","state","type","Response","body","status","statusText","headers","workbox_strategies_NetworkOnly"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"sw.js","sources":["../../../../../../private/var/folders/9b/3qmyp8zd2xvdspdrp149fyg00000gn/T/b91d190853e86f19f3d0336ce4659ce6/sw.js"],"sourcesContent":["import {registerRoute as workbox_routing_registerRoute} from '/Users/songjunxi/Desktop/repos/wrdo-app/wr.do/node_modules/.pnpm/workbox-routing@6.6.0/node_modules/workbox-routing/registerRoute.mjs';\nimport {NetworkFirst as workbox_strategies_NetworkFirst} from '/Users/songjunxi/Desktop/repos/wrdo-app/wr.do/node_modules/.pnpm/workbox-strategies@6.6.0/node_modules/workbox-strategies/NetworkFirst.mjs';\nimport {NetworkOnly as workbox_strategies_NetworkOnly} from '/Users/songjunxi/Desktop/repos/wrdo-app/wr.do/node_modules/.pnpm/workbox-strategies@6.6.0/node_modules/workbox-strategies/NetworkOnly.mjs';\nimport {clientsClaim as workbox_core_clientsClaim} from '/Users/songjunxi/Desktop/repos/wrdo-app/wr.do/node_modules/.pnpm/workbox-core@6.6.0/node_modules/workbox-core/clientsClaim.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\nimportScripts(\n \n);\n\n\n\n\n\n\n\nself.skipWaiting();\n\nworkbox_core_clientsClaim();\n\n\n\nworkbox_routing_registerRoute(\"/\", new workbox_strategies_NetworkFirst({ \"cacheName\":\"start-url\", plugins: [{ cacheWillUpdate: async ({ request, response, event, state }) => { if (response && response.type === 'opaqueredirect') { return new Response(response.body, { status: 200, statusText: 'OK', headers: response.headers }) } return response } }] }), 'GET');\nworkbox_routing_registerRoute(/.*/i, new workbox_strategies_NetworkOnly({ \"cacheName\":\"dev\", plugins: [] }), 'GET');\n\n\n\n\n"],"names":["importScripts","self","skipWaiting","workbox_core_clientsClaim","workbox_routing_registerRoute","workbox_strategies_NetworkFirst","plugins","cacheWillUpdate","request","response","event","state","type","Response","body","status","statusText","headers","workbox_strategies_NetworkOnly"],"mappings":"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"}
\ No newline at end of file