+
+ • {t("After v1-0-2, this setup guide is not needed anymore")}.
+
•{" "}
{t(
diff --git a/app/api/admin/configs/route.ts b/app/api/admin/configs/route.ts
index 911407f..b9b2aa5 100644
--- a/app/api/admin/configs/route.ts
+++ b/app/api/admin/configs/route.ts
@@ -23,6 +23,7 @@ export async function GET(req: NextRequest) {
"enable_google_oauth",
"enable_liunxdo_oauth",
"enable_resend_email_login",
+ "enable_email_password_login",
]);
return Response.json(configs, { status: 200 });
diff --git a/app/api/auth/credentials/route.ts b/app/api/auth/credentials/route.ts
new file mode 100644
index 0000000..8e4b777
--- /dev/null
+++ b/app/api/auth/credentials/route.ts
@@ -0,0 +1,46 @@
+import { NextRequest } from "next/server";
+import { compare, hash } from "bcrypt";
+
+import { prisma } from "@/lib/db";
+
+export async function POST(req: NextRequest) {
+ try {
+ const { email, password, name } = await req.json();
+ if (!email || !password) {
+ return Response.json("email and password is required", { status: 400 });
+ }
+
+ const user = await prisma.user.findUnique({
+ where: {
+ email,
+ },
+ });
+
+ if (!user) {
+ const newUser = await prisma.user.create({
+ data: {
+ name: "",
+ email,
+ password: await hash(password, 10),
+ active: 1,
+ role: "USER",
+ team: "free",
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString(),
+ },
+ });
+ return Response.json(newUser, { status: 200 });
+ } else {
+ const passwordCorrect = await compare(password, user.password || "");
+
+ if (passwordCorrect) {
+ return Response.json(user, { status: 200 });
+ }
+ }
+
+ return Response.json(null, { status: 400 });
+ } catch (error) {
+ console.error("[Auth Error]", error);
+ return Response.json(error.message || "Server error", { status: 500 });
+ }
+}
diff --git a/app/api/feature/route.ts b/app/api/feature/route.ts
index 0eb336b..c708fb2 100644
--- a/app/api/feature/route.ts
+++ b/app/api/feature/route.ts
@@ -2,7 +2,7 @@ import { getMultipleConfigs } from "@/lib/dto/system-config";
export const dynamic = "force-dynamic";
-export async function GET(req: Request) {
+export async function GET() {
try {
const configs = await getMultipleConfigs([
"enable_user_registration",
@@ -12,12 +12,14 @@ export async function GET(req: Request) {
"enable_google_oauth",
"enable_liunxdo_oauth",
"enable_resend_email_login",
+ "enable_email_password_login",
]);
return Response.json({
google: configs.enable_google_oauth,
github: configs.enable_github_oauth,
linuxdo: configs.enable_liunxdo_oauth,
resend: configs.enable_resend_email_login,
+ credentials: configs.enable_email_password_login,
registration: configs.enable_user_registration,
});
} catch (error) {
diff --git a/app/api/record/add/route.ts b/app/api/record/add/route.ts
index 4158220..115a016 100644
--- a/app/api/record/add/route.ts
+++ b/app/api/record/add/route.ts
@@ -58,10 +58,10 @@ export async function POST(req: Request) {
if (!matchedZone) {
return Response.json(
- `No matching zone found for domain: ${record_name}`,
+ `No matching zone found for domain: ${record.zone_name}`,
{
status: 400,
- statusText: "Invalid domain",
+ statusText: "Invalid zone name",
},
);
}
@@ -78,6 +78,7 @@ export async function POST(req: Request) {
record.type,
record_name,
record.content,
+ record.zone_name,
1,
);
if (user_record && user_record.length > 0) {
diff --git a/app/api/record/admin/add/route.ts b/app/api/record/admin/add/route.ts
index 26a7ff0..7f07c0a 100644
--- a/app/api/record/admin/add/route.ts
+++ b/app/api/record/admin/add/route.ts
@@ -87,6 +87,7 @@ export async function POST(req: Request) {
record.type,
record_name,
record.content,
+ record.zone_name,
1,
);
if (user_record && user_record.length > 0) {
diff --git a/app/api/record/admin/apply/route.ts b/app/api/record/admin/apply/route.ts
index a23c3c8..6ebb320 100644
--- a/app/api/record/admin/apply/route.ts
+++ b/app/api/record/admin/apply/route.ts
@@ -20,7 +20,6 @@ export async function POST(req: Request) {
const { record: reviewRecord, userId, id } = await req.json();
const record = {
...reviewRecord,
- // comment: "Created by wr.do (review mode)",
id,
};
@@ -40,9 +39,11 @@ export async function POST(req: Request) {
record,
);
+ console.log("[data]", data);
+
if (!data.success || !data.result?.id) {
- return Response.json(data.messages, {
- status: 501,
+ return Response.json(data.errors[0].message, {
+ status: 503,
});
} else {
const res = await updateUserRecordReview(userId, id, {
diff --git a/app/api/record/delete/route.ts b/app/api/record/delete/route.ts
index e2bdebf..155f14e 100644
--- a/app/api/record/delete/route.ts
+++ b/app/api/record/delete/route.ts
@@ -28,14 +28,22 @@ export async function POST(req: Request) {
});
}
- const res = await deleteDNSRecord(
- matchedZone.cf_zone_id!,
- matchedZone.cf_api_key!,
- matchedZone.cf_email!,
- record_id,
- );
+ if (active !== 3) {
+ const res = await deleteDNSRecord(
+ matchedZone.cf_zone_id!,
+ matchedZone.cf_api_key!,
+ matchedZone.cf_email!,
+ record_id,
+ );
- if (res && res.result?.id) {
+ if (res && res.result?.id) {
+ await deleteUserRecord(user.id, record_id, zone_id, active);
+ return Response.json("success", {
+ status: 200,
+ statusText: "success",
+ });
+ }
+ } else {
await deleteUserRecord(user.id, record_id, zone_id, active);
return Response.json("success", {
status: 200,
diff --git a/app/manifest.json b/app/manifest.json
index 54f6e8b..b33d2a2 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -3,7 +3,7 @@
"short_name": "WR.DO",
"description": "Shorten links with analytics, manage emails and control subdomains—all on one platform.",
"appid": "com.wr.do",
- "versionName": "1.0.1",
+ "versionName": "1.0.2",
"versionCode": "1",
"start_url": "/",
"orientation": "portrait",
diff --git a/auth.config.ts b/auth.config.ts
index 3d17fb1..0b84f1a 100644
--- a/auth.config.ts
+++ b/auth.config.ts
@@ -1,4 +1,5 @@
import type { NextAuthConfig } from "next-auth";
+import Credentials from "next-auth/providers/credentials";
import Github from "next-auth/providers/github";
import Google from "next-auth/providers/google";
import Resend from "next-auth/providers/resend";
@@ -67,5 +68,29 @@ export default {
},
}),
linuxDoProvider,
+ Credentials({
+ name: "Credentials",
+ credentials: {
+ name: { label: "name", type: "text" },
+ email: { label: "Email", type: "email" },
+ password: { label: "Password", type: "password" },
+ },
+ async authorize(credentials) {
+ const res = await fetch(
+ process.env.AUTH_URL + "/api/auth/credentials",
+ {
+ method: "POST",
+ body: JSON.stringify(credentials),
+ },
+ );
+ // console.log("[res]", res);
+
+ if (res.ok) {
+ return res.json();
+ }
+
+ return null;
+ },
+ }),
],
} satisfies NextAuthConfig;
diff --git a/auth.ts b/auth.ts
index 8421fbe..a657a09 100644
--- a/auth.ts
+++ b/auth.ts
@@ -53,7 +53,6 @@ export const {
return session;
},
-
async jwt({ token }) {
if (!token.sub) return token;
diff --git a/components/forms/record-form.tsx b/components/forms/record-form.tsx
index f908422..bb445ed 100644
--- a/components/forms/record-form.tsx
+++ b/components/forms/record-form.tsx
@@ -65,9 +65,7 @@ export function RecordForm({
const [currentRecordType, setCurrentRecordType] = useState(
initData?.type || "CNAME",
);
- const [currentZoneName, setCurrentZoneName] = useState(
- initData?.zone_name || "wr.do",
- );
+ const [currentZoneName, setCurrentZoneName] = useState(initData?.zone_name);
const [email, setEmail] = useState(initData?.user.email || user.email);
const [allowedRecordTypes, setAllowedRecordTypes] = useState([]);
const isAdmin = action.indexOf("admin") > -1;
@@ -83,7 +81,7 @@ export function RecordForm({
} = useForm({
resolver: zodResolver(createRecordSchema),
defaultValues: {
- zone_name: initData?.zone_name || "wr.do",
+ zone_name: initData?.zone_name,
type: initData?.type || "CNAME",
ttl: initData?.ttl || 1,
proxied: initData?.proxied || false,
@@ -121,6 +119,7 @@ export function RecordForm({
useEffect(() => {
if (validDefaultDomain) {
+ setValue("zone_name", validDefaultDomain);
setCurrentZoneName(validDefaultDomain);
}
}, [validDefaultDomain]);
@@ -563,7 +562,13 @@ export function RecordForm({
{isPending ? (
) : (
- {type === "edit" ? t("Update") : t("Save")}
+
+ {type === "edit"
+ ? initData?.active === 2 && isAdmin
+ ? t("Agree")
+ : t("Update")
+ : t("Save")}
+
)}
)}
diff --git a/components/forms/url-form.tsx b/components/forms/url-form.tsx
index 9c4fc0b..a3e0231 100644
--- a/components/forms/url-form.tsx
+++ b/components/forms/url-form.tsx
@@ -1,6 +1,12 @@
"use client";
-import { Dispatch, SetStateAction, useMemo, useTransition } from "react";
+import {
+ Dispatch,
+ SetStateAction,
+ useEffect,
+ useMemo,
+ useTransition,
+} from "react";
import { zodResolver } from "@hookform/resolvers/zod";
import { User } from "@prisma/client";
import { Sparkles } from "lucide-react";
@@ -58,7 +64,6 @@ export function UrlForm({
handleSubmit,
register,
formState: { errors },
- getValues,
setValue,
} = useForm({
resolver: zodResolver(createUrlSchema),
@@ -67,7 +72,7 @@ export function UrlForm({
target: initData?.target || "",
url: initData?.url || "",
active: initData?.active || 1,
- prefix: initData?.prefix || "wr.do",
+ prefix: initData?.prefix || "",
visible: initData?.visible || 0,
expiration: initData?.expiration || "-1",
password: initData?.password || "",
@@ -96,6 +101,12 @@ export function UrlForm({
return shortDomains[0].domain_name;
}, [shortDomains, initData?.prefix]);
+ useEffect(() => {
+ if (validDefaultDomain) {
+ setValue("prefix", validDefaultDomain);
+ }
+ }, [validDefaultDomain]);
+
const onSubmit = handleSubmit((data) => {
if (type === "add") {
handleCreateUrl(data);
diff --git a/components/forms/user-api-key-form.tsx b/components/forms/user-api-key-form.tsx
index 9c2d27e..aa7cf75 100644
--- a/components/forms/user-api-key-form.tsx
+++ b/components/forms/user-api-key-form.tsx
@@ -68,6 +68,7 @@ export function UserApiKeyForm({ user }: UserNameFormProps) {
{
type?: string;
}
type FormData = z.infer;
+type FormData2 = z.infer;
export function UserAuthForm({ className, type, ...props }: UserAuthFormProps) {
const {
@@ -34,34 +35,63 @@ export function UserAuthForm({ className, type, ...props }: UserAuthFormProps) {
} = useForm({
resolver: zodResolver(userAuthSchema),
});
- const [isLoading, setIsLoading] = React.useState(false);
+ const {
+ register: register2,
+ handleSubmit: handleSubmit2,
+ formState: { errors: errors2 },
+ } = useForm({
+ resolver: zodResolver(userPasswordAuthSchema),
+ });
+ // const [isLoading, setIsLoading] = React.useState(false);
+ const [isLoading, startTransition] = React.useTransition();
const [isGoogleLoading, setIsGoogleLoading] = React.useState(false);
const [isGithubLoading, setIsGithubLoading] = React.useState(false);
const [isLinuxDoLoading, setIsLinuxDoLoading] =
React.useState(false);
const searchParams = useSearchParams();
+ const router = useRouter();
const t = useTranslations("Auth");
async function onSubmit(data: FormData) {
- setIsLoading(true);
-
- const signInResult = await signIn("resend", {
- email: data.email.toLowerCase(),
- redirect: false,
- callbackUrl: searchParams?.get("from") || "/dashboard",
- });
-
- setIsLoading(false);
-
- if (!signInResult?.ok) {
- return toast.error("Something went wrong.", {
- description: "Your sign in request failed. Please try again.",
+ startTransition(async () => {
+ const signInResult = await signIn("resend", {
+ email: data.email.toLowerCase(),
+ redirect: false,
+ callbackUrl: searchParams?.get("from") || "/dashboard",
});
- }
- return toast.success("Check your email", {
- description: "We sent you a login link. Be sure to check your spam too.",
+ if (!signInResult?.ok) {
+ toast.error(t("Something went wrong"), {
+ description: "Your sign in request failed. Please try again.",
+ });
+ }
+
+ toast.success(t("Check your email"), {
+ description: `${t("We sent you a login link")}. ${t("Be sure to check your spam too")}.`,
+ });
+ });
+ }
+ async function onSubmitPwd(data: FormData2) {
+ startTransition(async () => {
+ const signInResult = await signIn("credentials", {
+ name: data.name,
+ email: data.email,
+ password: data.password,
+ redirect: false,
+ callbackUrl: searchParams?.get("from") || "/dashboard",
+ });
+
+ // console.log("[signInResult]", signInResult);
+
+ if (signInResult?.error) {
+ toast.error(t("Something went wrong"), {
+ description: `[${signInResult?.error}] ${t("Incorrect email or password")}.`,
+ });
+ } else {
+ toast.success(t("Welcome back!"));
+ router.push(searchParams?.get("from") || "/dashboard");
+ }
});
}
@@ -99,12 +129,127 @@ export function UserAuthForm({ className, type, ...props }: UserAuthFormProps) {
);
}
+ const rendeResend = () =>
+ loginMethod["resend"] && (
+
+ );
+
+ const rendeCredentials = () =>
+ loginMethod["credentials"] && (
+
+ );
+
return (
+ {!loginMethod.registration && (
+
+ 📢 {t("Administrator has disabled new user registration")}.
+
+ )}
+
{loginMethod["google"] && (
-
+
)}
{loginMethod["github"] && (
-
+
)}
{loginMethod["linuxdo"] && (
-
+
)}
{(loginMethod["google"] ||
loginMethod["github"] ||
loginMethod["linuxdo"]) &&
- loginMethod["resend"] &&
+ (loginMethod["resend"] || loginMethod["credentials"]) &&
rendeSeparator()}
- {loginMethod["resend"] && (
-
+ {loginMethod["resend"] && loginMethod["credentials"] ? (
+
+
+ {t("Email Code")}
+ {t("Password")}
+
+ {rendeResend()}
+ {rendeCredentials()}
+
+ ) : (
+ <>
+ {rendeResend()}
+ {rendeCredentials()}
+ >
)}
);
diff --git a/components/forms/user-password-form.tsx b/components/forms/user-password-form.tsx
new file mode 100644
index 0000000..417aaaf
--- /dev/null
+++ b/components/forms/user-password-form.tsx
@@ -0,0 +1,110 @@
+"use client";
+
+import { useState, useTransition } from "react";
+import {
+ updateUserPassword,
+ type FormData,
+} from "@/actions/update-user-password";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { User } from "@prisma/client";
+import { useSession } from "next-auth/react";
+import { useTranslations } from "next-intl";
+import { useForm } from "react-hook-form";
+import { toast } from "sonner";
+
+import { userPasswordSchema } from "@/lib/validations/user";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import { SectionColumns } from "@/components/dashboard/section-columns";
+import { Icons } from "@/components/shared/icons";
+
+interface UserPasswordFormProps {
+ user: Pick;
+}
+
+export function UserPasswordForm({ user }: UserPasswordFormProps) {
+ const { update } = useSession();
+ const [updated, setUpdated] = useState(false);
+ const [isPending, startTransition] = useTransition();
+ const updateUserPasswordWithId = updateUserPassword.bind(null, user.id);
+
+ const t = useTranslations("Setting");
+
+ const checkUpdate = (value: string) => {
+ setUpdated(value !== "");
+ };
+
+ const {
+ handleSubmit,
+ register,
+ formState: { errors },
+ } = useForm({
+ resolver: zodResolver(userPasswordSchema),
+ defaultValues: {
+ password: "",
+ },
+ });
+
+ const onSubmit = handleSubmit((data) => {
+ startTransition(async () => {
+ const { status } = await updateUserPasswordWithId(data);
+
+ if (status !== "success") {
+ toast.error("Something went wrong.", {
+ description: "Your password was not updated. Please try again.",
+ });
+ } else {
+ await update();
+ setUpdated(false);
+ toast.success("Your password has been updated.");
+ }
+ });
+ });
+
+ return (
+
+ );
+}
diff --git a/content/docs/developer/deploy-zh.mdx b/content/docs/developer/deploy-zh.mdx
index cf7997c..902cffa 100644
--- a/content/docs/developer/deploy-zh.mdx
+++ b/content/docs/developer/deploy-zh.mdx
@@ -11,7 +11,7 @@ description: 选择你的部署方式
## 使用 Vercel 部署(推荐)
-[](https://vercel.com/new/clone?repository-url=https://github.com/oiov/wr.do.git&project-name=wrdo&env=DATABASE_URL&env=AUTH_SECRET&env=RESEND_API_KEY&env=NEXT_PUBLIC_EMAIL_R2_DOMAIN&env=GITHUB_TOKEN)
+[](https://vercel.com/new/clone?repository-url=https://github.com/oiov/wr.do.git&project-name=wrdo)
## 使用 Docker Compose 部署
diff --git a/content/docs/developer/deploy.mdx b/content/docs/developer/deploy.mdx
index 134c970..76171d6 100644
--- a/content/docs/developer/deploy.mdx
+++ b/content/docs/developer/deploy.mdx
@@ -12,7 +12,7 @@ description: Choose your deployment method
## Deploy with Vercel (Recommended)
-[](https://vercel.com/new/clone?repository-url=https://github.com/oiov/wr.do.git&project-name=wrdo&env=DATABASE_URL&env=AUTH_SECRET&env=RESEND_API_KEY&env=NEXT_PUBLIC_EMAIL_R2_DOMAIN&env=GITHUB_TOKEN)
+[](https://vercel.com/new/clone?repository-url=https://github.com/oiov/wr.do.git&project-name=wrdo)
Remember to fill in the necessary environment variables.
diff --git a/content/docs/developer/installation-zh.mdx b/content/docs/developer/installation-zh.mdx
index 126a674..58d2aee 100644
--- a/content/docs/developer/installation-zh.mdx
+++ b/content/docs/developer/installation-zh.mdx
@@ -12,6 +12,7 @@ description: 简单介绍 WR.DO 部署所需的环境变量
或参考社区优秀部署文档:
- https://linux.do/t/topic/711806
+ - https://bravexist.cn/2025/06/wr.do.html
diff --git a/content/docs/developer/installation.mdx b/content/docs/developer/installation.mdx
index 0d5e341..4b93305 100644
--- a/content/docs/developer/installation.mdx
+++ b/content/docs/developer/installation.mdx
@@ -12,6 +12,7 @@ description: How to install the project.
Or read unofficial deployment tutorials:
- https://linux.do/t/topic/711806
+ - https://bravexist.cn/2025/06/wr.do.html
@@ -26,7 +27,7 @@ npx create-next-app wrdo --example "https://github.com/oiov/wr.do"
Or deploy with Vercel :
-[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Foiov%2Fwr.do)
+[](https://vercel.com/new/clone?repository-url=https://github.com/oiov/wr.do.git&project-name=wrdo)
A good way to create your repository, but the deployment will fail because you
diff --git a/content/docs/developer/quick-start-zh.mdx b/content/docs/developer/quick-start-zh.mdx
index b81e4d7..7c17f99 100644
--- a/content/docs/developer/quick-start-zh.mdx
+++ b/content/docs/developer/quick-start-zh.mdx
@@ -193,27 +193,7 @@ pnpm dev
通过浏览器访问:[http://localhost:3000](http://localhost:3000)
-## 7. 设置系统
-
-#### 创建第一个账号并将账号权限更改为 ADMIN
-
-请按以下步骤操作:
-
-* 1. 通过 [http://localhost:3000/login](http://localhost:3000/login) 注册登录你的第一个账号;
-* 2. 通过 [http://localhost:3000/setup](http://localhost:3000/setup) 将账号权限更改为 ADMIN;
-* 3. 然后根据 **面板引导** 配置系统并添加第一个域名。
-
-
-
-
-
-
- 将账号权限更改为 ADMIN 后,你可以刷新页面并访问 http://localhost:3000/admin。
-
-你必须至少添加一个域名,才能使用短链接、邮件或子域名管理等功能。
-
-
-## 8. 部署
+## 7. 部署教程
详见:[部署指南](/docs/developer/deploy)
@@ -227,12 +207,4 @@ pnpm dev
https://dash.cloudflare.com/[account_id]/[zone_name]/ssl-tls/configuration
```
-将 `SSL/TLS 加密模式` 更改为 `Full` 模式。
-
-### 如何修改团队计划配额?
-
-通过 team.ts 文件修改:
-
-```bash
-https://github.com/oiov/wr.do/tree/main/config/team.ts
-```
\ No newline at end of file
+将 `SSL/TLS 加密模式` 更改为 `Full` 模式。
\ No newline at end of file
diff --git a/content/docs/developer/quick-start.mdx b/content/docs/developer/quick-start.mdx
index d422b02..9cd0c9d 100644
--- a/content/docs/developer/quick-start.mdx
+++ b/content/docs/developer/quick-start.mdx
@@ -182,27 +182,7 @@ pnpm dev
```
Via [http://localhost:3000](http://localhost:3000)
-## 7. Setup System
-
-#### Create the first account and Change the account's role to ADMIN
-
-Follow the steps below:
-
-- 1. Via [http://localhost:3000/login](http://localhost:3000/login), login with your account.
-- 2. Via [http://localhost:3000/setup](http://localhost:3000/setup), change the account's role to ADMIN.
-- 3. Then follow the **panel guide** to config the system and add the first domain.
-
-
-
-
-
-
- After change the account's role to ADMIN, then you can refresh the website and access http://localhost:3000/admin.
-
- You must add at least one domain to start using short links, email or subdomain management features.
-
-
-## 8. Deploy
+## 7. Deploy
See [Deploy Guide](/docs/developer/deploy).
@@ -216,12 +196,4 @@ Via:
https://dash.cloudflare.com/[account_id]/[zone_name]/ssl-tls/configuration
```
-Change the `SSL/TLS Encryption` Mode to `Full` in the Cloudflare dashboard.
-
-### How can I change the team plan quota?
-
-Via team.ts:
-
-```bash
-https://github.com/oiov/wr.do/tree/main/config/team.ts
-```
+Change the `SSL/TLS Encryption` Mode to `Full` in the Cloudflare dashboard.
\ No newline at end of file
diff --git a/lib/cloudflare.ts b/lib/cloudflare.ts
index a2d2ddb..eb503bc 100644
--- a/lib/cloudflare.ts
+++ b/lib/cloudflare.ts
@@ -77,9 +77,11 @@ export const createDNSRecord = async (
body: JSON.stringify(record),
});
- if (!response.ok) {
- throw new Error(`HTTP error status: ${response.status}`);
- }
+ // console.log("response.status", await response.json());
+
+ // if (!response.ok) {
+ // throw new Error(`HTTP error status: ${response.status}`);
+ // }
const data = await response.json();
return data;
@@ -110,10 +112,6 @@ export const deleteDNSRecord = async (
headers,
});
- if (!response.ok) {
- throw new Error(`HTTP error status: ${response.status}`);
- }
-
const data = await response.json();
return data;
} catch (error) {
diff --git a/lib/dto/cloudflare-dns-record.ts b/lib/dto/cloudflare-dns-record.ts
index 8bc2070..8811277 100644
--- a/lib/dto/cloudflare-dns-record.ts
+++ b/lib/dto/cloudflare-dns-record.ts
@@ -103,7 +103,7 @@ export async function updateUserRecordReview(
const res = await prisma.userRecord.update({
where: {
- record_id,
+ id,
},
data: {
userId,
@@ -234,6 +234,7 @@ export async function getUserRecordByTypeNameContent(
type: string,
name: string,
content: string,
+ zone_name: string,
active: number = 1,
) {
return await prisma.userRecord.findMany({
@@ -242,6 +243,7 @@ export async function getUserRecordByTypeNameContent(
type,
// content,
name,
+ zone_name,
active: {
not: 3,
},
diff --git a/lib/validations/auth.ts b/lib/validations/auth.ts
index dc383e6..6178860 100644
--- a/lib/validations/auth.ts
+++ b/lib/validations/auth.ts
@@ -5,6 +5,12 @@ export const userAuthSchema = z.object({
email: z.string().email(),
});
+export const userPasswordAuthSchema = z.object({
+ name: z.string().optional(),
+ email: z.string().email(),
+ password: z.string().min(6),
+});
+
export const updateUserSchema = z.object({
email: z.string().email(),
image: z.string(),
diff --git a/lib/validations/user.ts b/lib/validations/user.ts
index 26296da..c9fb423 100644
--- a/lib/validations/user.ts
+++ b/lib/validations/user.ts
@@ -9,4 +9,8 @@ export const userRoleSchema = z.object({
role: z.nativeEnum(UserRole),
});
+export const userPasswordSchema = z.object({
+ password: z.string().min(6).max(32),
+});
+
export const userApiKeySchema = z.object({});
diff --git a/locales/en.json b/locales/en.json
index db41e53..983a9ef 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -19,7 +19,8 @@
"Domain Name": "Domain Name",
"Please enter a valid domain name (must be hosted on Cloudflare)": "Please enter a valid domain name (must be hosted on Cloudflare)",
"Or add later": "Or add later",
- "Submit": "Submit"
+ "Submit": "Submit",
+ "After v1-0-2, this setup guide is not needed anymore": "After v1.0.2, this setup guide is not needed anymore"
},
"List": {
"Short URLs": "Short URLs",
@@ -73,9 +74,8 @@
"per page": "per page",
"Total Subdomains": "Total Subdomains",
"Subdomain List": "Subdomains",
- "Please read the": "Please read the",
+ "Before using please read the": "Before using please read the",
"Legitimacy review": "Legitimacy review",
- "before using": "before using",
"See": "See",
"examples": "examples",
"for more usage": "for more usage",
@@ -166,7 +166,8 @@
"Plan name must be unique": "Plan name must be unique",
"Record Types": "Record Types",
"Allowed record types": "Allowed record types",
- "use `,` to separate": "use `,` to separate"
+ "use `,` to separate": "use `,` to separate",
+ "Agree": "Agree"
},
"Components": {
"Dashboard": "Dashboard",
@@ -334,7 +335,18 @@
"Or continue with": "Or continue with",
"Email": "Email",
"Sign Up with Email": "Sign Up with Email",
- "Sign In with Email": "Sign In with Email"
+ "Sign In with Email": "Sign In with Email",
+ "Email Code": "Email",
+ "Password": "Password",
+ "Sign In / Sign Up": "Sign In / Sign Up",
+ "Incorrect email or password": "Incorrect email or password",
+ "Something went wrong": "Something went wrong",
+ "Check your email": "Check your email",
+ "We sent you a login link": "We sent you a login link",
+ "Be sure to check your spam too": "Be sure to check your spam too",
+ "Welcome back!": "Welcome back!",
+ "Unregistered users will automatically create an account": "Unregistered users will automatically create an account",
+ "Administrator has disabled new user registration": "Administrator has disabled new user registration"
},
"System": {
"MENU": "Menu",
@@ -466,6 +478,10 @@
"Set system notification, this will be displayed in the header": "Set system notification, this will be displayed in the header",
"Login Methods": "Login Methods",
"Select the login methods that users can use to log in": "Select the login methods that users can use to log in",
- "Resend Email": "Resend Email"
+ "Resend Email": "Resend Email",
+ "Email Password": "Email Password",
+ "Your Password": "Your Password",
+ "Update your password": "Update your password",
+ "At least 6 characters, Max 32 characters": "At least 6 characters, Max 32 characters"
}
}
diff --git a/locales/zh.json b/locales/zh.json
index 12f92fc..c94ef8e 100644
--- a/locales/zh.json
+++ b/locales/zh.json
@@ -19,7 +19,8 @@
"Domain Name": "域名",
"Please enter a valid domain name (must be hosted on Cloudflare)": "请输入有效的域名(确保已经托管到 Cloudflare)",
"Or add later": "或稍后添加",
- "Submit": "提交"
+ "Submit": "提交",
+ "After v1-0-2, this setup guide is not needed anymore": "此初始化引导在 v1.0.2 版本后, 不再是必要步骤"
},
"List": {
"Short URLs": "短链列表",
@@ -73,9 +74,8 @@
"per page": "条/页",
"Total Subdomains": "总计",
"Subdomain List": "子域名列表",
- "Please read the": "请阅读",
+ "Before using please read the": "在使用之前请阅读",
"legitimacy review": "链接合法性审查",
- "before using": "在使用之前",
"See": "查看",
"examples": "示例",
"for more usage": "了解更多用法",
@@ -166,7 +166,8 @@
"Plan name must be unique": "计划名称必须唯一",
"Record Types": "DNS 记录类型",
"Allowed record types": "请填写标准的 DNS 记录类型",
- "use `,` to separate": "使用 `,` 分隔"
+ "use `,` to separate": "使用 `,` 分隔",
+ "Agree": "同意"
},
"Components": {
"Dashboard": "用户面板",
@@ -334,7 +335,18 @@
"Or continue with": "或使用",
"Email": "邮箱",
"Sign Up with Email": "使用邮箱注册",
- "Sign In with Email": "使用邮箱登录"
+ "Sign In with Email": "使用邮箱登录",
+ "Email Code": "邮箱验证",
+ "Password": "账号密码",
+ "Sign In / Sign Up": "点击登录/注册",
+ "Incorrect email or password": "邮箱或密码错误",
+ "Something went wrong": "出错了",
+ "Check your email": "检查您的邮箱",
+ "We sent you a login link": "我们已向您发送登录链接",
+ "Be sure to check your spam too": "请确保检查您的垃圾邮件",
+ "Welcome back!": "欢迎回来!",
+ "Unregistered users will automatically create an account": "未注册用户将自动创建账户",
+ "Administrator has disabled new user registration": "管理员已关闭新用户注册"
},
"System": {
"MENU": "菜单",
@@ -466,6 +478,10 @@
"Set system notification, this will be displayed in the header": "设置系统通知,将在网页顶部显示",
"Login Methods": "登录方式",
"Select the login methods that users can use to log in": "选择用户可以使用的登录方式",
- "Resend Email": "Resend 邮箱登录"
+ "Resend Email": "Resend 邮箱登录",
+ "Email Password": "账号密码登录",
+ "Your Password": "账号密码",
+ "Update your password": "更新您的密码",
+ "At least 6 characters, Max 32 characters": "密码长度至少6位,最多32位"
}
}
diff --git a/package.json b/package.json
index 2e011f8..90e1a1d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "wr.do",
- "version": "1.0.1",
+ "version": "1.0.2",
"author": {
"name": "oiov",
"url": "https://github.com/oiov"
@@ -75,6 +75,7 @@
"@vercel/analytics": "^1.3.1",
"@vercel/functions": "^1.4.0",
"@vercel/og": "^0.6.2",
+ "bcrypt": "^6.0.0",
"chalk": "^4.1.1",
"cheerio": "1.0.0-rc.12",
"class-variance-authority": "^0.7.0",
@@ -140,6 +141,7 @@
"@ianvs/prettier-plugin-sort-imports": "^4.3.1",
"@tailwindcss/line-clamp": "^0.4.4",
"@tailwindcss/typography": "^0.5.13",
+ "@types/bcrypt": "^5.0.2",
"@types/lodash": "^4.17.16",
"@types/node": "^20.14.11",
"@types/qrcode": "^1.5.5",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c31cdc6..e88d239 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -158,6 +158,9 @@ importers:
'@vercel/og':
specifier: ^0.6.2
version: 0.6.2
+ bcrypt:
+ specifier: ^6.0.0
+ version: 6.0.0
chalk:
specifier: ^4.1.1
version: 4.1.2
@@ -348,6 +351,9 @@ importers:
'@tailwindcss/typography':
specifier: ^0.5.13
version: 0.5.13(tailwindcss@3.4.6)
+ '@types/bcrypt':
+ specifier: ^5.0.2
+ version: 5.0.2
'@types/lodash':
specifier: ^4.17.16
version: 4.17.16
@@ -3145,6 +3151,9 @@ packages:
'@types/acorn@4.0.6':
resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==}
+ '@types/bcrypt@5.0.2':
+ resolution: {integrity: sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==}
+
'@types/conventional-commits-parser@5.0.0':
resolution: {integrity: sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==}
@@ -3808,6 +3817,10 @@ packages:
resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==}
engines: {node: ^4.5.0 || >= 5.9}
+ bcrypt@6.0.0:
+ resolution: {integrity: sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==}
+ engines: {node: '>= 18'}
+
big.js@5.2.2:
resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==}
@@ -6171,6 +6184,14 @@ packages:
no-case@3.0.4:
resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
+ node-addon-api@8.4.0:
+ resolution: {integrity: sha512-D9DI/gXHvVmjHS08SVch0Em8G5S1P+QWtU31appcKT/8wFSPRcdHadIFSAntdMMVM5zz+/DL+bL/gz3UDppqtg==}
+ engines: {node: ^18 || ^20 || >= 21}
+
+ node-gyp-build@4.8.4:
+ resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
+ hasBin: true
+
node-releases@2.0.14:
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
@@ -11105,6 +11126,10 @@ snapshots:
dependencies:
'@types/estree': 1.0.5
+ '@types/bcrypt@5.0.2':
+ dependencies:
+ '@types/node': 20.14.11
+
'@types/conventional-commits-parser@5.0.0':
dependencies:
'@types/node': 20.14.11
@@ -11926,6 +11951,11 @@ snapshots:
base64id@2.0.0: {}
+ bcrypt@6.0.0:
+ dependencies:
+ node-addon-api: 8.4.0
+ node-gyp-build: 4.8.4
+
big.js@5.2.2: {}
binary-extensions@2.2.0: {}
@@ -14885,6 +14915,10 @@ snapshots:
lower-case: 2.0.2
tslib: 2.8.1
+ node-addon-api@8.4.0: {}
+
+ node-gyp-build@4.8.4: {}
+
node-releases@2.0.14: {}
node-releases@2.0.18: {}
diff --git a/prisma/migrations/20250617100233/migration.sql b/prisma/migrations/20250617100233/migration.sql
new file mode 100644
index 0000000..97e192e
--- /dev/null
+++ b/prisma/migrations/20250617100233/migration.sql
@@ -0,0 +1,38 @@
+-- AlterTable
+ALTER TABLE "users" ADD COLUMN "password" TEXT;
+
+INSERT INTO "system_configs"
+ (
+ "key",
+ "value",
+ "type",
+ "description"
+ )
+VALUES
+ (
+ 'enable_email_password_login',
+ 'true',
+ 'BOOLEAN',
+ '是否启用邮箱密码登录'
+);
+
+INSERT INTO "users"
+ (
+ id,
+ name,
+ email,
+ password,
+ active,
+ role,
+ team
+ )
+VALUES
+ (
+ 'cmadvu9w874j2sczhg174pftq',
+ 'admin',
+ 'admin@admin.com',
+ '$2b$10$FQIPnvwTQ2dwL2F3SIiKDOf.qTvMcwfc0KsbqHQBWflpFT2o8Uwji',
+ 1,
+ 'ADMIN',
+ 'free'
+);
\ No newline at end of file
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 917dfb7..993f88e 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -61,6 +61,7 @@ model User {
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @default(now()) @map(name: "updated_at")
role UserRole @default(USER)
+ password String?
accounts Account[]
sessions Session[]
diff --git a/public/manifest.json b/public/manifest.json
index 54f6e8b..b33d2a2 100644
--- a/public/manifest.json
+++ b/public/manifest.json
@@ -3,7 +3,7 @@
"short_name": "WR.DO",
"description": "Shorten links with analytics, manage emails and control subdomains—all on one platform.",
"appid": "com.wr.do",
- "versionName": "1.0.1",
+ "versionName": "1.0.2",
"versionCode": "1",
"start_url": "/",
"orientation": "portrait",
diff --git a/public/site.webmanifest b/public/site.webmanifest
index 54f6e8b..b33d2a2 100644
--- a/public/site.webmanifest
+++ b/public/site.webmanifest
@@ -3,7 +3,7 @@
"short_name": "WR.DO",
"description": "Shorten links with analytics, manage emails and control subdomains—all on one platform.",
"appid": "com.wr.do",
- "versionName": "1.0.1",
+ "versionName": "1.0.2",
"versionCode": "1",
"start_url": "/",
"orientation": "portrait",
diff --git a/public/sw.js.map b/public/sw.js.map
index 5a8c061..85103de 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/6ce495a00cb1e0e48fb7047c3c68904a/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/77937c383c2f0f6b52d02a41dcd1ccc9/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