diff --git a/apps/server/src/factories/db.ts b/apps/server/src/factories/db.ts index 703be053..c38485cc 100644 --- a/apps/server/src/factories/db.ts +++ b/apps/server/src/factories/db.ts @@ -12,6 +12,7 @@ async function createConnection() { uri: GlobalConfig.database_url, multipleStatements: true, waitForConnections: true, + connectTimeout: 10000, // 增加连接超时时间(毫秒) connectionLimit: 10, maxIdle: 10, idleTimeout: 60000, diff --git a/apps/server/src/modules/channel/channel.service.ts b/apps/server/src/modules/channel/channel.service.ts index 92992085..fefee634 100644 --- a/apps/server/src/modules/channel/channel.service.ts +++ b/apps/server/src/modules/channel/channel.service.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DB, DbType } from '../global/providers/db.provider'; import { CreateChannelDto, UpdateChannelDto } from './dto/channel.dto'; import { work } from '@poster-craft/schema'; -import { eq, sql } from 'drizzle-orm'; +import { eq } from 'drizzle-orm'; import { v6 as uuidV6 } from 'uuid'; type ChannelProps = { diff --git a/apps/server/src/modules/page/page.service.ts b/apps/server/src/modules/page/page.service.ts index 698ad479..0aec846a 100644 --- a/apps/server/src/modules/page/page.service.ts +++ b/apps/server/src/modules/page/page.service.ts @@ -2,8 +2,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DB, DbType } from '../global/providers/db.provider'; import { work } from '@poster-craft/schema'; import { eq } from 'drizzle-orm'; -import ReactDOMServer from 'react-dom/server'; -import React from 'react'; @Injectable() export class PageService { diff --git a/apps/server/src/modules/template/template.controller.ts b/apps/server/src/modules/template/template.controller.ts index ff0ef173..fd5bcb16 100644 --- a/apps/server/src/modules/template/template.controller.ts +++ b/apps/server/src/modules/template/template.controller.ts @@ -1,8 +1,7 @@ -import { ApiBearerAuth, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger'; +import { ApiBearerAuth, ApiParam, ApiTags } from '@nestjs/swagger'; import { TemplateService } from './template.service'; -import { Controller, Get, Param, Query, UseGuards } from '@nestjs/common'; +import { Controller, Get, Param, Query } from '@nestjs/common'; import { string } from 'zod'; -import { JwtAuthGuard } from 'src/guards/jwt.guard'; import { GetTemplateListDto } from './dto/template.dto'; @ApiBearerAuth() @@ -12,7 +11,6 @@ export class TemplateController { constructor(private readonly templateService: TemplateService) {} @Get() - @UseGuards(JwtAuthGuard) async getTemplates(@Query() query: GetTemplateListDto) { try { const data = await this.templateService.getTemplatesList(query); @@ -29,7 +27,6 @@ export class TemplateController { } @Get(':workId') - @UseGuards(JwtAuthGuard) @ApiParam({ name: 'workId', required: true, diff --git a/apps/server/src/modules/work/work.controller.ts b/apps/server/src/modules/work/work.controller.ts index dd7df511..d567ec6c 100644 --- a/apps/server/src/modules/work/work.controller.ts +++ b/apps/server/src/modules/work/work.controller.ts @@ -7,7 +7,6 @@ import { Post, Put, Query, - Redirect, UseGuards, } from '@nestjs/common'; import { @@ -84,7 +83,6 @@ export class WorkController { } @Get('list') - @UseGuards(JwtAuthGuard) @ApiOperation({ summary: '获取工作区列表', description: '获取工作区列表', @@ -114,7 +112,6 @@ export class WorkController { } @Get(':workId') - @UseGuards(JwtAuthGuard) @ApiParam({ name: 'workId', description: '工作区Id', diff --git a/apps/server/vercel.json b/apps/server/vercel.json index dcbe5987..acc0ed8e 100644 --- a/apps/server/vercel.json +++ b/apps/server/vercel.json @@ -1,16 +1,20 @@ { "version": 2, + "framework": null, + "buildCommand": "cd ../.. && pnpm build -F server", + "installCommand": "cd ../.. && pnpm install", "builds": [ { - "src": "src/main.ts", + "src": "dist/main.js", "use": "@vercel/node" } ], "routes": [ { "src": "/(.*)", - "dest": "src/main.ts", - "methods": ["GET", "POST", "PUT", "DELETE", "PATCH"] + "dest": "dist/main.js" } - ] + ], + "regions": ["hkg1"], + "public": false } diff --git a/apps/web/api/template.ts b/apps/web/api/template.ts deleted file mode 100644 index 595749b6..00000000 --- a/apps/web/api/template.ts +++ /dev/null @@ -1,18 +0,0 @@ -import http from "@/utils/http"; - -import { ResponseData } from "./types/common"; -import { - getTemplateListBody, - getTemplateListResponse, - getTemplateResponse, -} from "./types/template"; - -/** 获取模板列表 */ -export function getTemplateList(query?: getTemplateListBody) { - return http.get>(`/templates`, query); -} - -/** 获取单个模板 */ -export function getTemplate(workId: string) { - return http.get>(`/templates/${workId}`); -} diff --git a/apps/web/api/types/email.ts b/apps/web/api/types/email.ts deleted file mode 100644 index 232482b7..00000000 --- a/apps/web/api/types/email.ts +++ /dev/null @@ -1,15 +0,0 @@ -export type sendCodeByEmailBody = { - email: string; -}; - -export type updateEmailBody = { - email: string; - otp: string; -}; - -export type bindEmailBody = updateEmailBody; - -export type verifyEmailBody = { - email: string; - otp: string; -}; diff --git a/apps/web/api/types/sms.ts b/apps/web/api/types/sms.ts deleted file mode 100644 index b052ecff..00000000 --- a/apps/web/api/types/sms.ts +++ /dev/null @@ -1,10 +0,0 @@ -export type sendBySMSBody = { - phone: string; -}; - -export type verifyPhoneBody = { - phone: string; - otp: string; -}; - -export type updatePhoneBody = verifyPhoneBody; diff --git a/apps/web/api/types/upload.ts b/apps/web/api/types/upload.ts deleted file mode 100644 index 4dbc9ebf..00000000 --- a/apps/web/api/types/upload.ts +++ /dev/null @@ -1,7 +0,0 @@ -export type uploadFileBody = { - file: string; -}; - -export type uploadFileResponse = { - url: string; -}; diff --git a/apps/web/api/work.ts b/apps/web/api/work.ts deleted file mode 100644 index 5318b9fe..00000000 --- a/apps/web/api/work.ts +++ /dev/null @@ -1,49 +0,0 @@ -import http from "@/utils/http"; - -import { ResponseData } from "./types/common"; -import { - copyWorkResponse, - createWorkBody, - createWorkResponse, - getWorkListBody, - getWorkListResponse, - getWorkResponse, - publishWorkToTemplateResponse, - updateWorkBody, - updateWorkResponse, -} from "./types/work"; - -/** 创建工作区 */ -export function createWork(body: createWorkBody) { - return http.post>(`/work`, body); -} - -/** 获取工作区列表 */ -export function getWorkList(query?: getWorkListBody) { - return http.get>(`/work/list`, query); -} - -/** 复制工作区 */ -export function copyWork(workId: string) { - return http.post>(`/work/copy/${workId}`); -} - -/** 获取单个工作区 */ -export function getWork(workId: string) { - return http.get>(`/work/${workId}`); -} - -/** 更新单个工作区 */ -export function updateWork(workId: string, body: updateWorkBody) { - return http.put>(`/work/${workId}`, body); -} - -/** 删除单个工作区 */ -export function deleteWork(workId: string) { - return http.delete>(`/work/${workId}`); -} - -/** 发布工作区模板 */ -export function publishWorkToTemplate(workId: string) { - return http.post>(`/work/template/publish/${workId}`); -} diff --git a/apps/web/app/[locale]/about/page.tsx b/apps/web/app/[locale]/about/page.tsx deleted file mode 100644 index 0ae6fae0..00000000 --- a/apps/web/app/[locale]/about/page.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import BaseLayout from "@/components/layouts/BaseLayout"; - -function Main() { - return ( - -
About
-
- ); -} - -export default Main; diff --git a/apps/web/app/[locale]/auth/login/page.tsx b/apps/web/app/[locale]/auth/login/page.tsx index a05c41d8..ae6cd5f9 100644 --- a/apps/web/app/[locale]/auth/login/page.tsx +++ b/apps/web/app/[locale]/auth/login/page.tsx @@ -1,6 +1,5 @@ "use client"; -import { defaultSignIn, loginBySMS } from "@/api/auth"; import AuthLayout from "@/components/layouts/AuthLayout"; import Dialog from "@/components/pages/auth/Dialog"; import Oauth2 from "@/components/pages/auth/Oauth2"; @@ -10,19 +9,51 @@ import { Form } from "@/components/ui/form"; import { ToastAction } from "@/components/ui/toast"; import { useToast } from "@/components/ui/use-toast"; import { useToken } from "@/hooks/useToken"; +import { defaultSignIn, loginBySMS } from "@/http/auth"; import { useUserStore } from "@/stores/user"; -import { loginFormSchema, loginFormSchemaType } from "@/utils/formSchema"; import { Link } from "@/utils/i18n/routing"; import { zodResolver } from "@hookform/resolvers/zod"; +import { useTranslations } from "next-intl"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { useForm } from "react-hook-form"; +import { z } from "zod"; export default function Login() { const router = useRouter(); const [_, setTokenHandler] = useToken(); const { setUserId } = useUserStore(); const { toast } = useToast(); + const t = useTranslations(); + const loginFormSchema = z.object({ + email: z.string().email({ + message: t("form.email.invalid"), + }), + phone: z + .string() + .length(11, { + message: t("form.phone.invalid"), + }) + .regex(/^\d+$/, { + message: t("form.phone.invalid"), + }), + password: z.string().min(1, { + message: t("form.password.required"), + }), + code: z + .string() + .length(6, { + message: t("form.code.length"), + }) + .regex(/^\d+$/, { + message: t("form.code.length"), + }), + username: z.string().min(2, { + message: t("form.username.minLength"), + }), + }); + + type loginFormSchemaType = z.infer; const form = useForm({ resolver: zodResolver(loginFormSchema), defaultValues: { @@ -37,7 +68,7 @@ export default function Login() { console.log(values); } - //登录模式(是否为手机短信登录) + /** 登录模式(是否为手机短信登录) */ const [isPhoneMode, setIsPhoneMode] = useState(false); const handleSign = async () => { @@ -52,12 +83,12 @@ export default function Login() { toast({ variant: "success", title: "Success", - description: "登录成功,即将跳转至主页...", + description: t("loginSuccess"), }); if (res.data.token) { setTokenHandler(res.data.token); } - setUserId(res.data.data.userId); + setUserId(res.data.data?.userId); router.push("/"); } else { toast({ @@ -96,7 +127,7 @@ export default function Login() { onClick={() => handleSign()} type="submit" > - 登 录 + {t("login")}
@@ -105,7 +136,7 @@ export default function Login() { href="/auth/register" className="label-text-alt link link-hover hover:text-gray-500 dark:hover:text-white/80 text-[#EF4444] dark:text-white" > - 点此注册 + {t("registerLink")} @@ -113,7 +144,6 @@ export default function Login() {
- ); diff --git a/apps/web/app/[locale]/auth/register/page.tsx b/apps/web/app/[locale]/auth/register/page.tsx index 320b9864..f1340c5e 100644 --- a/apps/web/app/[locale]/auth/register/page.tsx +++ b/apps/web/app/[locale]/auth/register/page.tsx @@ -1,7 +1,5 @@ "use client"; -import { defaultSignUp } from "@/api/auth"; -import { defaultSignUpBody } from "@/api/types/auth"; import AuthLayout from "@/components/layouts/AuthLayout"; import Oauth2 from "@/components/pages/auth/Oauth2"; import CustomFormField from "@/components/shared/CustomFormField"; @@ -9,18 +7,49 @@ import { Button } from "@/components/ui/button"; import { Form } from "@/components/ui/form"; import { ToastAction } from "@/components/ui/toast"; import { useToast } from "@/components/ui/use-toast"; -import { registerFormSchema } from "@/utils/formSchema"; +import { defaultSignUp } from "@/http/auth"; +import { DefaultSignUpBody } from "@/http/types/auth"; import { Link } from "@/utils/i18n/routing"; import { zodResolver } from "@hookform/resolvers/zod"; import { useTranslations } from "next-intl"; import { useRouter } from "next/navigation"; import { useForm } from "react-hook-form"; +import { z } from "zod"; export default function Register() { const router = useRouter(); const { toast } = useToast(); - const t = useTranslations("register"); - const form = useForm({ + const t = useTranslations(); + + /** 表单验证相关 */ + const registerFormSchema = z.object({ + phone: z + .string() + .length(11, { + message: t("form.phone.invalid"), + }) + .regex(/^\d+$/, { + message: t("form.phone.invalid"), + }), + password: z.string().min(1, { + message: t("form.required"), + }), + otp: z + .string() + .length(6, { + message: t("form.code.length"), + }) + .regex(/^\d+$/, { + message: t("form.code.length"), + }), + username: z + .string() + .min(4, { message: t("form.username.minLength") }) + .max(12, { message: t("form.username.maxLength") }), + }); + + /** 表单数据 */ + const form = useForm({ resolver: zodResolver(registerFormSchema), defaultValues: { phone: "", @@ -30,7 +59,7 @@ export default function Register() { }, }); - async function onSubmit(values: defaultSignUpBody) { + async function onSubmit(values: DefaultSignUpBody) { try { const res = await defaultSignUp(values); if (res.data.code === 200) { @@ -97,7 +126,7 @@ export default function Register() {
+  PosterCraft  + + + )}
); diff --git a/apps/web/components/layouts/common/GlobalDrawer.tsx b/apps/web/components/layouts/common/GlobalDrawer.tsx index 5f9b6623..0b65fa23 100644 --- a/apps/web/components/layouts/common/GlobalDrawer.tsx +++ b/apps/web/components/layouts/common/GlobalDrawer.tsx @@ -1,11 +1,11 @@ "use client"; -import { getUserInfo } from "@/api/user"; import MenuItem from "@/components/shared/MenuItem"; import { Drawer, DrawerClose, DrawerContent, DrawerTrigger } from "@/components/ui/drawer"; import { useToast } from "@/components/ui/use-toast"; import { GlobalEnvConfig } from "@/config"; import { useToken } from "@/hooks/useToken"; +import { getUserInfo } from "@/http/user"; import { cn } from "@/lib/utils"; import { useUserStore } from "@/stores/user"; import { usePathname, useRouter } from "@/utils/i18n/routing"; @@ -76,9 +76,9 @@ const GlobalDrawer: React.FC = ({ className }) => { const getUserData = async (id: string) => { const res = await getUserInfo(id); setUserInfo(() => ({ - avatar: res.data.data.avatar || Info.avatar, - username: res.data.data.username || Info.username, - nickname: res.data.data.nickname || Info.nickname, + avatar: res.data.data?.avatar || Info.avatar, + username: res.data.data?.username || Info.username, + nickname: res.data.data?.nickname || Info.nickname, })); }; useEffect(() => { diff --git a/apps/web/components/layouts/common/SideMenu.tsx b/apps/web/components/layouts/common/SideMenu.tsx index 9a79f53b..00d1b325 100644 --- a/apps/web/components/layouts/common/SideMenu.tsx +++ b/apps/web/components/layouts/common/SideMenu.tsx @@ -19,10 +19,6 @@ const Menus = [ href: "/works", label: "Works", }, - { - href: "/about", - label: "About", - }, ]; const SideMenu: React.FC = () => { diff --git a/apps/web/components/pages/auth/Dialog.tsx b/apps/web/components/pages/auth/Dialog.tsx index 08c296e9..63cedae6 100644 --- a/apps/web/components/pages/auth/Dialog.tsx +++ b/apps/web/components/pages/auth/Dialog.tsx @@ -1,19 +1,36 @@ "use client"; -import { defaultSignUp } from "@/api/auth"; import CustomFormField from "@/components/shared/CustomFormField"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Form } from "@/components/ui/form"; import { useToast } from "@/components/ui/use-toast"; +import { defaultSignUp } from "@/http/auth"; import { useGithubUsername, useOauth2Dialog } from "@/stores/auth"; -import { phoneFormSchema, phoneFormSchemaType } from "@/utils/formSchema"; import { zodResolver } from "@hookform/resolvers/zod"; +import { useTranslations } from "next-intl"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; +import { z } from "zod"; function DialogDemo() { + const t = useTranslations(); + const phoneFormSchema = z.object({ + phone: z.string().regex(/^1[3-9]\d{9}$/, { + message: t("form.phone.invalid"), + }), + otp: z + .string() + .length(6, { + message: t("form.code.length"), + }) + .regex(/^\d+$/, { + message: t("form.code.length"), + }), + }); + + type phoneFormSchemaType = z.infer; const form = useForm({ resolver: zodResolver(phoneFormSchema), defaultValues: { diff --git a/apps/web/components/pages/auth/Oauth2.tsx b/apps/web/components/pages/auth/Oauth2.tsx index 7ea23354..57e1d251 100644 --- a/apps/web/components/pages/auth/Oauth2.tsx +++ b/apps/web/components/pages/auth/Oauth2.tsx @@ -1,9 +1,10 @@ "use client"; -import { githubSignIn } from "@/api/auth"; import GithubIcon from "@/components/shared/GithubIcon"; import GoogleIcon from "@/components/shared/GoogleIcon"; import { useToken } from "@/hooks/useToken"; +import { githubSignIn } from "@/http/auth"; +import { useTranslations } from "next-intl"; import { useRouter } from "next/navigation"; import { useGithubUsername, useOauth2Dialog } from "../../../stores/auth"; @@ -11,6 +12,7 @@ import { useGithubUsername, useOauth2Dialog } from "../../../stores/auth"; function Oauth2() { const router = useRouter(); const [_, setTokenHandler] = useToken(); + const t = useTranslations(); const { setGithubUsername } = useGithubUsername(); const { setIsOpen } = useOauth2Dialog(); @@ -30,7 +32,7 @@ function Oauth2() { return (
- 其他登录方式: + {t("otherLoginWays")}:
); } -} +}; -export default renderSignIn; +export default RenderSignIn; diff --git a/apps/web/components/pages/editor/Header/EditorFeature.tsx b/apps/web/components/pages/editor/Header/EditorFeature.tsx index 5233ce72..3fd14dbe 100644 --- a/apps/web/components/pages/editor/Header/EditorFeature.tsx +++ b/apps/web/components/pages/editor/Header/EditorFeature.tsx @@ -1,6 +1,7 @@ import GlobalDrawer from "@/components/layouts/common/GlobalDrawer"; import { cn } from "@/lib/utils"; +import PreviewDialog from "./PreviewDialog"; import PublishDialog from "./PublishDialog"; import SaveDialog from "./SaveDialog"; @@ -12,48 +13,22 @@ const EditorFeature: React.FC = ({ className }) => { return (