feat: 调整博客页以支持slug
This commit is contained in:
@@ -31,12 +31,16 @@ export class BlogController {
|
|||||||
return this.blogService.list();
|
return this.blogService.list();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id/slug')
|
||||||
async getBlog(
|
async getBlogBySlug(
|
||||||
@Param('id', new ParseUUIDPipe({ version: '4' })) id: string,
|
@Param('id') slug: string,
|
||||||
@Query('p') password?: string,
|
@Query('p') password?: string,
|
||||||
) {
|
) {
|
||||||
const blog = await this.blogService.findById(id);
|
if (slug.trim().length === 0) {
|
||||||
|
throw new BadRequestException('文章不存在');
|
||||||
|
}
|
||||||
|
|
||||||
|
const blog = await this.blogService.findBySlug(slug);
|
||||||
if (!blog) throw new BadRequestException('文章不存在或无权限访问');
|
if (!blog) throw new BadRequestException('文章不存在或无权限访问');
|
||||||
|
|
||||||
if (!blog.permissions.includes(BlogPermission.Public)) {
|
if (!blog.permissions.includes(BlogPermission.Public)) {
|
||||||
@@ -46,7 +50,7 @@ export class BlogController {
|
|||||||
} else {
|
} else {
|
||||||
// 判断密码是否正确
|
// 判断密码是否正确
|
||||||
if (
|
if (
|
||||||
!password ||
|
typeof password !== 'string' ||
|
||||||
this.blogService.hashPassword(password) !== blog.password_hash
|
this.blogService.hashPassword(password) !== blog.password_hash
|
||||||
) {
|
) {
|
||||||
throw new BadRequestException('文章不存在或无权限访问');
|
throw new BadRequestException('文章不存在或无权限访问');
|
||||||
@@ -57,7 +61,7 @@ export class BlogController {
|
|||||||
const blogDataRes = await fetch(`${blog.contentUrl}`);
|
const blogDataRes = await fetch(`${blog.contentUrl}`);
|
||||||
const blogContent = await blogDataRes.text();
|
const blogContent = await blogDataRes.text();
|
||||||
|
|
||||||
await this.blogService.incrementViewCount(id);
|
this.blogService.incrementViewCount(blog.id).catch(() => null);
|
||||||
return {
|
return {
|
||||||
id: blog.id,
|
id: blog.id,
|
||||||
title: blog.title,
|
title: blog.title,
|
||||||
|
|||||||
@@ -35,13 +35,14 @@ export class BlogService {
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { createdAt, updatedAt, deletedAt, id, title, viewCount, description } = i;
|
const { createdAt, updatedAt, deletedAt, id, title, viewCount, description, slug } = i;
|
||||||
return {
|
return {
|
||||||
createdAt,
|
createdAt,
|
||||||
updatedAt,
|
updatedAt,
|
||||||
deletedAt,
|
deletedAt,
|
||||||
id,
|
id,
|
||||||
title,
|
title,
|
||||||
|
slug,
|
||||||
viewCount,
|
viewCount,
|
||||||
description,
|
description,
|
||||||
};
|
};
|
||||||
@@ -96,6 +97,12 @@ export class BlogService {
|
|||||||
return await this.blogRepository.findOneBy({ id });
|
return await this.blogRepository.findOneBy({ id });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async findBySlug(slug: string) {
|
||||||
|
return this.blogRepository.findOne({
|
||||||
|
where: { slug }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async incrementViewCount(id: string) {
|
async incrementViewCount(id: string) {
|
||||||
await this.blogRepository.increment({ id }, 'viewCount', 1);
|
await this.blogRepository.increment({ id }, 'viewCount', 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,17 +27,8 @@ async function parseBlogParams({ params: paramsPromise, searchParams: searchPara
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hex = Array.from(base62.decode(params.id as string)).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
||||||
const id = [
|
|
||||||
hex.slice(0, 8),
|
|
||||||
hex.slice(8, 12),
|
|
||||||
hex.slice(12, 16),
|
|
||||||
hex.slice(16, 20),
|
|
||||||
hex.slice(20, 32)
|
|
||||||
].join('-');
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id: params.id,
|
||||||
p: searchParams.p,
|
p: searchParams.p,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,7 +41,7 @@ async function getBlog(paramsResult: ReturnType<typeof parseBlogParams>) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
const data = await BlogAPI.getBlog(`${id}`, p);
|
const data = await BlogAPI.getBlogBySlug(`${id}`, p);
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
}
|
}
|
||||||
@@ -83,7 +74,7 @@ export default async function Page({ params, searchParams }: PageRouteProps) {
|
|||||||
let { errorMsg } = res;
|
let { errorMsg } = res;
|
||||||
|
|
||||||
const data = errorMsg ? null
|
const data = errorMsg ? null
|
||||||
: await BlogAPI.getBlog(`${id}`, p).catch(e => handleAPIError(e, ({ message }) => { errorMsg = message; return null }));
|
: await BlogAPI.getBlogBySlug(`${id}`, p).catch(e => handleAPIError(e, ({ message }) => { errorMsg = message; return null }));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-hidden">
|
<div className="w-full overflow-x-hidden">
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
AlertTitle,
|
AlertTitle,
|
||||||
} from "@/components/ui/alert";
|
} from "@/components/ui/alert";
|
||||||
import { AlertCircle } from "lucide-react";
|
import { AlertCircle } from "lucide-react";
|
||||||
import { base62 } from "@/lib/utils";
|
|
||||||
import { BlogAPI } from "@/lib/api/server";
|
import { BlogAPI } from "@/lib/api/server";
|
||||||
import { handleAPIError } from "@/lib/api/common";
|
import { handleAPIError } from "@/lib/api/common";
|
||||||
|
|
||||||
@@ -18,13 +17,10 @@ const formatNumber = (num: number): string => {
|
|||||||
return num.toString();
|
return num.toString();
|
||||||
};
|
};
|
||||||
|
|
||||||
const getBlogDetailUrl = (id: string): string => {
|
const getBlogDetailUrl = (slug: string): string => {
|
||||||
const cleanId = id.replace(/-/g, '');
|
return `/blog/${slug}`;
|
||||||
const encoded = base62.encode(Buffer.from(cleanId, 'hex'));
|
|
||||||
return `/blog/${encoded}`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: '日志 - 特恩的日志',
|
title: '日志 - 特恩的日志',
|
||||||
description: '我随便发点,你也随便看看~',
|
description: '我随便发点,你也随便看看~',
|
||||||
@@ -56,7 +52,7 @@ export default async function Blog() {
|
|||||||
<h2 className="text-2xl font-medium">
|
<h2 className="text-2xl font-medium">
|
||||||
<a
|
<a
|
||||||
className="hover:underline focus:outline-none focus:ring-2 focus:ring-zinc-400 rounded"
|
className="hover:underline focus:outline-none focus:ring-2 focus:ring-zinc-400 rounded"
|
||||||
href={getBlogDetailUrl(blog.id)}
|
href={getBlogDetailUrl(blog.slug)}
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
{blog.title}
|
{blog.title}
|
||||||
|
|||||||
@@ -3,16 +3,17 @@ import { serverFetch } from "../server";
|
|||||||
|
|
||||||
export async function list() {
|
export async function list() {
|
||||||
return serverFetch<Pick<Blog,
|
return serverFetch<Pick<Blog,
|
||||||
'id' | 'title' | 'description' | 'viewCount' | 'createdAt' | 'updatedAt' | 'deletedAt'
|
'id' | 'title' | 'slug' | 'description' | 'viewCount' | 'createdAt' | 'updatedAt' | 'deletedAt'
|
||||||
>[]>('/api/blog')
|
>[]>('/api/blog')
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getBlog(id: string, password?: string) {
|
export async function getBlogBySlug(slug: string, password?: string) {
|
||||||
return serverFetch<{
|
return serverFetch<{
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
content: string;
|
content: string;
|
||||||
}>(`/api/blog/${id}` + (password ? `?p=${password}` : ''));
|
}>(`/api/blog/${slug}/slug` + (password ? `?p=${password}` : ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user