From e9feb1f8cac3ea03a89915ba691fbbf9eef6a1d5 Mon Sep 17 00:00:00 2001 From: tone <3341154833@qq.com> Date: Mon, 23 Jun 2025 00:07:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=8D=9A=E5=AE=A2=E6=9D=83?= =?UTF-8?q?=E9=99=90=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/admin-web-blog.controller.ts | 13 ++- .../dto/admin-web/set-blog-password.dto.ts | 6 ++ .../admin/dto/admin-web/update-blog.dto.ts | 16 ++++ tone-page-server/src/blog/blog.service.ts | 12 +++ .../web/blog/components/BlogEdit.tsx | 33 ++++++++ .../web/blog/components/SetPasswordDialog.tsx | 82 +++++++++++++++++++ tone-page-web/lib/api/admin/web/blog/index.ts | 3 +- .../lib/api/admin/web/blog/setPassword.ts | 10 +++ .../lib/api/admin/web/blog/update.ts | 2 + tone-page-web/lib/types/blog.ts | 3 + 10 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 tone-page-server/src/admin/dto/admin-web/set-blog-password.dto.ts create mode 100644 tone-page-server/src/admin/dto/admin-web/update-blog.dto.ts create mode 100644 tone-page-web/app/console/(with-menu)/web/blog/components/SetPasswordDialog.tsx create mode 100644 tone-page-web/lib/api/admin/web/blog/setPassword.ts diff --git a/tone-page-server/src/admin/controller/web/admin-web-blog.controller.ts b/tone-page-server/src/admin/controller/web/admin-web-blog.controller.ts index 8e9aab6..0c106be 100644 --- a/tone-page-server/src/admin/controller/web/admin-web-blog.controller.ts +++ b/tone-page-server/src/admin/controller/web/admin-web-blog.controller.ts @@ -11,7 +11,10 @@ import { UseGuards, } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; +import { UpdatePasswordDto } from 'src/admin/dto/admin-user/update-password.dto'; import { CreateBlogDto } from 'src/admin/dto/admin-web/create-blog.dto'; +import { SetBlogPasswordDto } from 'src/admin/dto/admin-web/set-blog-password.dto'; +import { UpdateBlogDto } from 'src/admin/dto/admin-web/update-blog.dto'; import { Role } from 'src/auth/role.enum'; import { BlogPermission } from 'src/blog/Blog.Permission.enum'; import { BlogService } from 'src/blog/blog.service'; @@ -38,11 +41,19 @@ export class AdminWebBlogController { @Put(':id') async update( @Param('id', new ParseUUIDPipe({ version: '4' })) id: string, - @Body() dto: CreateBlogDto, + @Body() dto: UpdateBlogDto, ) { return this.adminWebBlogService.update(id, dto); } + @Post(':id/password') + async setPassword( + @Param('id', new ParseUUIDPipe({ version: '4' })) id: string, + @Body() dto: SetBlogPasswordDto, + ) { + return this.adminWebBlogService.setPassword(id, dto.password); + } + @Get(':id') async get(@Param('id', new ParseUUIDPipe({ version: '4' })) id: string) { return this.adminWebBlogService.findById(id); diff --git a/tone-page-server/src/admin/dto/admin-web/set-blog-password.dto.ts b/tone-page-server/src/admin/dto/admin-web/set-blog-password.dto.ts new file mode 100644 index 0000000..028d2a1 --- /dev/null +++ b/tone-page-server/src/admin/dto/admin-web/set-blog-password.dto.ts @@ -0,0 +1,6 @@ +import { IsString } from "class-validator"; + +export class SetBlogPasswordDto { + @IsString() + password: string; +} \ No newline at end of file diff --git a/tone-page-server/src/admin/dto/admin-web/update-blog.dto.ts b/tone-page-server/src/admin/dto/admin-web/update-blog.dto.ts new file mode 100644 index 0000000..65caef1 --- /dev/null +++ b/tone-page-server/src/admin/dto/admin-web/update-blog.dto.ts @@ -0,0 +1,16 @@ +import { IsEnum, IsString } from 'class-validator'; +import { BlogPermission } from 'src/blog/Blog.Permission.enum'; + +export class UpdateBlogDto { + @IsString() + title: string; + + @IsString() + description: string; + + @IsString() + contentUrl: string; + + @IsEnum(BlogPermission, { each: true, message: '请求类型错误' }) + permissions: BlogPermission[]; +} diff --git a/tone-page-server/src/blog/blog.service.ts b/tone-page-server/src/blog/blog.service.ts index f96b3b3..9e10262 100644 --- a/tone-page-server/src/blog/blog.service.ts +++ b/tone-page-server/src/blog/blog.service.ts @@ -36,6 +36,18 @@ export class BlogService { return this.blogRepository.save(newBlog); } + async setPassword(id: string, password: string) { + const blog = await this.findById(id); + if (!blog) { + throw new Error('博客不存在'); + } + + return (await this.blogRepository.update(id, { + ...blog, + password_hash: createHash('sha256').update(`${password}`).digest('hex'), + })).affected > 0; + } + async update(id: string, blog: Partial) { await this.blogRepository.update(id, blog); return this.blogRepository.findOneBy({ id }); diff --git a/tone-page-web/app/console/(with-menu)/web/blog/components/BlogEdit.tsx b/tone-page-web/app/console/(with-menu)/web/blog/components/BlogEdit.tsx index 6f50c14..9768648 100644 --- a/tone-page-web/app/console/(with-menu)/web/blog/components/BlogEdit.tsx +++ b/tone-page-web/app/console/(with-menu)/web/blog/components/BlogEdit.tsx @@ -16,6 +16,9 @@ import { toast } from "sonner" import { AdminApi } from "@/lib/api" import useSWR from "swr" import { ApiError } from "next/dist/server/api-utils" +import { BlogPermissionCheckBoxs } from "./BlogPermissionCheckBoxs" +import { BlogPermission } from "@/lib/types/Blog.Permission.enum" +import { SetPasswordDialog } from "./SetPasswordDialog" interface BlogEditProps { id: string; @@ -43,6 +46,7 @@ export default function BlogEdit({ id, children, onRefresh }: BlogEditProps) { title: blog.title, description: blog.description, contentUrl: blog.contentUrl, + permissions: blog.permissions, }); toast.success("更新成功") setOpen(false); @@ -109,6 +113,35 @@ export default function BlogEdit({ id, children, onRefresh }: BlogEditProps) { onChange={(e) => mutate({ ...blog, contentUrl: e.target.value }, false)} /> +
+ +
+ { + mutate({ + ...blog, + permissions: newState ? + [...blog.permissions, permission] : + blog.permissions.filter(p => p !== permission) + }, false) + }} + /> +
+
+ { + blog.permissions.includes(BlogPermission.ByPassword) && +
+ + + + +
+ }
diff --git a/tone-page-web/app/console/(with-menu)/web/blog/components/SetPasswordDialog.tsx b/tone-page-web/app/console/(with-menu)/web/blog/components/SetPasswordDialog.tsx new file mode 100644 index 0000000..5e400a9 --- /dev/null +++ b/tone-page-web/app/console/(with-menu)/web/blog/components/SetPasswordDialog.tsx @@ -0,0 +1,82 @@ +'use client'; + +import { Button } from "@/components/ui/button" +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { AdminApi } from "@/lib/api"; +import React, { useEffect, useState } from "react"; +import { toast } from "sonner"; + +interface SetPasswordDialogProps { + id: string; + children: React.ReactNode; +} + +export function SetPasswordDialog({ id, children }: SetPasswordDialogProps) { + const [open, setOpen] = useState(false); + const [password, setPassword] = useState(''); + + const handleSubmit = async () => { + if (!password) { + return toast.error('请输入密码'); + } + + await AdminApi.web.blog.setPassword(id, password).then(() => { + toast.success('修改成功'); + setOpen(false); + }).catch(e => { + toast.error(`${e.message || e || '请求失败'}`) + }); + } + + useEffect(() => { + if (open) { + setPassword(''); + } + }, [open]) + + return ( + setOpen(v)}> +
+ + {children} + + + + 修改密码 + + 通过密码访问受保护的文章,需开启“受密码保护”权限 + + +
+
+ + setPassword(e.target.value)} + /> +
+
+ + + + + + +
+
+
+ ) +} \ No newline at end of file diff --git a/tone-page-web/lib/api/admin/web/blog/index.ts b/tone-page-web/lib/api/admin/web/blog/index.ts index 1dcb4de..ddc07a5 100644 --- a/tone-page-web/lib/api/admin/web/blog/index.ts +++ b/tone-page-web/lib/api/admin/web/blog/index.ts @@ -2,4 +2,5 @@ export * from './create'; export * from './remove'; export * from './list'; export * from './update'; -export * from './get'; \ No newline at end of file +export * from './get'; +export * from './setPassword'; \ No newline at end of file diff --git a/tone-page-web/lib/api/admin/web/blog/setPassword.ts b/tone-page-web/lib/api/admin/web/blog/setPassword.ts new file mode 100644 index 0000000..49065f8 --- /dev/null +++ b/tone-page-web/lib/api/admin/web/blog/setPassword.ts @@ -0,0 +1,10 @@ +import fetcher from "@/lib/api/fetcher"; + +export async function setPassword(id: string, password: string) { + return fetcher(`/api/admin/web/blog/${id}/password`, { + method: 'POST', + body: JSON.stringify({ + password, + }) + }) +} \ No newline at end of file diff --git a/tone-page-web/lib/api/admin/web/blog/update.ts b/tone-page-web/lib/api/admin/web/blog/update.ts index 25ec5f9..2acbb03 100644 --- a/tone-page-web/lib/api/admin/web/blog/update.ts +++ b/tone-page-web/lib/api/admin/web/blog/update.ts @@ -1,9 +1,11 @@ import fetcher from "@/lib/api/fetcher"; +import { BlogPermission } from "@/lib/types/Blog.Permission.enum"; type UpdateBlogParams = { title: string; description: string; contentUrl: string; + permissions: BlogPermission[], } export async function update(id: string, data: UpdateBlogParams) { diff --git a/tone-page-web/lib/types/blog.ts b/tone-page-web/lib/types/blog.ts index ec9b1c0..4c3744b 100644 --- a/tone-page-web/lib/types/blog.ts +++ b/tone-page-web/lib/types/blog.ts @@ -1,3 +1,5 @@ +import { BlogPermission } from "./Blog.Permission.enum"; + export interface Blog { id: string; title: string; @@ -5,4 +7,5 @@ export interface Blog { viewCount: number; contentUrl: string; createdAt: string; + permissions: BlogPermission[]; } \ No newline at end of file