Skip to content

Commit

Permalink
feat: 自定义文章路径 #159
Browse files Browse the repository at this point in the history
  • Loading branch information
Mereithhh committed Feb 26, 2023
1 parent baf4e3c commit 14394b5
Show file tree
Hide file tree
Showing 34 changed files with 198 additions and 63 deletions.
8 changes: 8 additions & 0 deletions packages/admin/src/components/NewArticleModal/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ export default function (props) {
label="置顶优先级"
placeholder="留空或0表示不置顶,其余数字越大表示优先级越高"
/>
<ProFormText
width="md"
id="pathnameC"
name="pathnameC"
label="自定义路径名"
tooltip="文章发布后的路径将为 /post/[自定义路径名],如果未设置则使用文章 id 作为路径名"
placeholder="留空或为空则使用 id 作为路径名"
/>
<ProFormSelect
mode="tags"
tokenSeparators={[',']}
Expand Down
8 changes: 8 additions & 0 deletions packages/admin/src/components/PublishDraftModal/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ export default function (props) {
autocomplete: 'new-password',
}}
/>
<ProFormText
width="md"
id="pathname"
name="pathname"
label="自定义路径名"
tooltip="文章发布后的路径将为 /post/[自定义路径名],如果未设置则使用文章 id 作为路径名"
placeholder="留空或为空则使用 id 作为路径名"
/>
<ProFormText.Password
label="密码"
width="md"
Expand Down
8 changes: 8 additions & 0 deletions packages/admin/src/components/UpdateModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ export default function (props: {
label="置顶优先级"
placeholder="留空或0表示不置顶,其余数字越大表示优先级越高"
/>
<ProFormText
width="md"
id="pathname"
name="pathname"
label="自定义路径名"
tooltip="文章发布后的路径将为 /post/[自定义路径名],如果未设置则使用文章 id 作为路径名"
placeholder="留空或为空则使用 id 作为路径名"
/>
<ProFormSelect
width="md"
name="private"
Expand Down
5 changes: 3 additions & 2 deletions packages/admin/src/pages/Article/columns.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ColumnsToolBar from '@/components/ColumnsToolBar';
import UpdateModal from '@/components/UpdateModal';
import { deleteArticle, getAllCategories, getArticleById, getTags } from '@/services/van-blog/api';
import { getPathname } from '@/services/van-blog/getPathname';
import { parseObjToMarkdown } from '@/services/van-blog/parseMarkdownFile';
import { message, Modal, Space, Tag } from 'antd';
import { history } from 'umi';
Expand Down Expand Up @@ -134,7 +135,7 @@ export const columns = [
编辑
</a>,
<a
href={`/post/${record.id}`}
href={`/post/${getPathname(record)}`}
onClick={(ev) => {
if (record?.hidden) {
Modal.confirm({
Expand All @@ -158,7 +159,7 @@ export const columns = [
</div>
),
onOk: () => {
window.open(`/post/${record.id}`, '_blank');
window.open(`/post/${getPathname(record)}`, '_blank');
return true;
},
okText: '仍然访问',
Expand Down
5 changes: 3 additions & 2 deletions packages/admin/src/pages/Editor/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
updateArticle,
updateDraft,
} from '@/services/van-blog/api';
import { getPathname } from '@/services/van-blog/getPathname';
import { parseMarkdownFile, parseObjToMarkdown } from '@/services/van-blog/parseMarkdownFile';
import { useCacheState } from '@/services/van-blog/useCacheState';
import { DownOutlined } from '@ant-design/icons';
Expand Down Expand Up @@ -301,15 +302,15 @@ export default function () {
</div>
),
onOk: () => {
window.open(`/post/${currObj.id}`, '_blank');
window.open(`/post/${getPathname(currObj)}`, '_blank');
return true;
},
okText: '仍然访问',
cancelText: '返回',
});
return;
}
url = `/post/${currObj?.id}`;
url = `/post/${getPathname(currObj)}`;
} else {
url = '/about';
}
Expand Down
6 changes: 6 additions & 0 deletions packages/admin/src/services/van-blog/getPathname.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const getPathname = (obj: any) => {
if (!obj.pathname) {
return obj.id;
}
return obj.pathname;
};
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ export class ArticleController {
}

@Get('/:id')
async getOneById(@Param('id') id: number) {
const data = await this.articleProvider.getById(id, 'admin');
async getOneByIdOrPathname(@Param('id') id: string) {
const data = await this.articleProvider.getByIdOrPathname(id, 'admin');
return {
statusCode: 200,
data,
Expand Down
15 changes: 9 additions & 6 deletions packages/server/src/controller/public/public.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,19 @@ export class PublicController {
};
}
@Get('/article/:id')
async getArticleById(@Param('id') id: number) {
const data = await this.articleProvider.getByIdWithPreNext(id, 'public');
async getArticleByIdOrPathname(@Param('id') id: string) {
const data = await this.articleProvider.getByIdOrPathnameWithPreNext(
id,
'public',
);
return {
statusCode: 200,
data: data,
};
}
@Post('/article/:id')
async getArticleByIdWithPassword(
@Param('id') id: number,
async getArticleByIdOrPathnameWithPassword(
@Param('id') id: number | string,
@Body() body: { password: string },
) {
const data = await this.articleProvider.getByIdWithPassword(
Expand Down Expand Up @@ -91,7 +94,7 @@ export class PublicController {
}
const data = await this.metaProvider.addViewer(
isNew,
url.pathname,
decodeURIComponent(url.pathname),
isNewByPath,
);
return {
Expand All @@ -109,7 +112,7 @@ export class PublicController {
};
}
@Get('/article/viewer/:id')
async getViewerByArticleId(@Param('id') id: number) {
async getViewerByArticleIdOrPathname(@Param('id') id: number | string) {
const data = await this.visitProvider.getByArticleId(id);
return {
statusCode: 200,
Expand Down
79 changes: 72 additions & 7 deletions packages/server/src/provider/article/article.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export class ArticleProvider {
hidden: 1,
author: 1,
copyright: 1,
pathname: 1,
};

adminView = {
Expand All @@ -69,6 +70,7 @@ export class ArticleProvider {
visited: 1,
author: 1,
copyright: 1,
pathname: 1,
};

listView = {
Expand All @@ -87,6 +89,7 @@ export class ArticleProvider {
visited: 1,
author: 1,
copyright: 1,
pathname: 1,
};

toPublic(oldArticles: Article[]) {
Expand Down Expand Up @@ -157,6 +160,26 @@ export class ArticleProvider {
return res;
}

async updateViewerByPathname(pathname: string, isNew: boolean) {
let article = await this.getByPathName(pathname, 'list');
if (!article) {
// 这是通过 id 的吧。
article = await this.getById(Number(pathname), 'list');
if (!article) {
return;
}
}
const oldViewer = article.viewer || 0;
const oldVIsited = article.visited || 0;
const newViewer = oldViewer + 1;
const newVisited = isNew ? oldVIsited + 1 : oldVIsited;
const nowTime = new Date();
await this.articleModel.updateOne(
{ id: article.id },
{ visited: newVisited, viewer: newViewer, lastVisitedTime: nowTime },
);
}

async updateViewer(id: number, isNew: boolean) {
const article = await this.getById(id, 'list');
if (!article) {
Expand Down Expand Up @@ -663,6 +686,43 @@ export class ArticleProvider {
return resData;
}

async getByIdOrPathname(id: string | number, view: ArticleView) {
const articleByPathname = await this.getByPathName(
String(id),
this.getView(view),
);

if (articleByPathname) {
return articleByPathname;
}
return await this.getById(Number(id), this.getView(view));
}

async getByPathName(pathname: string, view: ArticleView): Promise<Article> {
const $and: any = [
{
$or: [
{
deleted: false,
},
{
deleted: { $exists: false },
},
],
},
];

return await this.articleModel
.findOne(
{
pathname: decodeURIComponent(pathname),
$and,
},
this.getView(view),
)
.exec();
}

async getById(id: number, view: ArticleView): Promise<Article> {
const $and: any = [
{
Expand All @@ -687,17 +747,22 @@ export class ArticleProvider {
)
.exec();
}
async getByIdWithPassword(id: number, password: string): Promise<any> {
const article: any = await this.getById(id, 'admin');
async getByIdWithPassword(
id: number | string,
password: string,
): Promise<any> {
const article: any = await this.getByIdOrPathname(id, 'admin');
if (!password) {
return null;
}
if (!article) {
return null;
}
const category = await this.categoryModal.findOne({
name: article.category,
});
const category =
(await this.categoryModal.findOne({
name: article.category,
})) || ({} as any);

const categoryPassword = category.private ? category.password : undefined;
const targetPassword = categoryPassword
? categoryPassword
Expand All @@ -712,8 +777,8 @@ export class ArticleProvider {
}
}
}
async getByIdWithPreNext(id: number, view: ArticleView) {
const curArticle = await this.getById(id, view);
async getByIdOrPathnameWithPreNext(id: string | number, view: ArticleView) {
const curArticle = await this.getByIdOrPathname(id, view);
if (!curArticle) {
throw new NotFoundException('找不到文章');
}
Expand Down
14 changes: 11 additions & 3 deletions packages/server/src/provider/isr/isr.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,15 @@ export class ISRProvider {
// ! 配置差的机器可能并发多了会卡,所以改成串行的。

await this.activeUrls(this.urlList, false);
await this.activePath('post', activeConfig?.postId || undefined);
let postId: any = null;
const articleWithThisId = await this.articleProvider.getById(
postId,
'list',
);
if (articleWithThisId) {
postId = articleWithThisId.pathname || articleWithThisId.id;
}
await this.activePath('post', postId || undefined);
await this.activePath('page');
await this.activePath('category');
await this.activePath('tag');
Expand Down Expand Up @@ -135,7 +143,7 @@ export class ISRProvider {
beforeObj?: Article,
) {
const { article, pre, next } =
await this.articleProvider.getByIdWithPreNext(id, 'list');
await this.articleProvider.getByIdOrPathnameWithPreNext(id, 'list');
// 无论是什么事件都先触发文章本身、标签和分类。
this.activeUrl(`/post/${id}`, true);
if (pre) {
Expand Down Expand Up @@ -208,7 +216,7 @@ export class ISRProvider {
async getArticleUrls() {
const articles = await this.articleProvider.getAll('list', true, true);
return articles.map((a) => {
return `/post/${a.id}`;
return `/post/${a.pathname || a.id}`;
});
}
}
4 changes: 2 additions & 2 deletions packages/server/src/provider/meta/meta.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ export class MetaProvider {
const r = /\/post\//;
const isArticlePath = r.test(pathname);
if (isArticlePath) {
await this.articleProvider.updateViewer(
parseInt(pathname.replace('/post/', '')),
await this.articleProvider.updateViewerByPathname(
pathname.replace('/post/', ''),
isNewByPath,
);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/server/src/provider/rss/rss.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export class RssProvider {
author,
});
for (const article of articles) {
const url = `${siteUrl}post/${article.id}`;
const url = `${siteUrl}post/${article.pathname || article.id}`;
const category = {
name: article.category,
domain: `${siteUrl}/category/${article.category}`,
Expand Down
2 changes: 1 addition & 1 deletion packages/server/src/provider/sitemap/sitemap.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export class SiteMapProvider {
async getArticleUrls() {
const articles = await this.articleProvider.getAll('list', false, false);
return articles.map((a) => {
return `/post/${a.id}`;
return `/post/${a.pathname || a.id}`;
});
}
async getCategoryUrls() {
Expand Down
2 changes: 1 addition & 1 deletion packages/server/src/provider/visit/visit.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export class VisitProvider {
async findByDateAndPath(date: string, pathname: string): Promise<Visit> {
return this.visitModel.findOne({ date, pathname }).exec();
}
async getByArticleId(id: number) {
async getByArticleId(id: number | string) {
const pathname = id == 0 ? `/about` : `/post/${id}`;
const result = await this.visitModel
.find({
Expand Down
3 changes: 3 additions & 0 deletions packages/server/src/scheme/article.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export class Article extends Document {
@Prop({ index: true })
author: string;

@Prop({ default: '', index: true })
pathname: string;

@Prop({ default: false, index: true })
private: boolean;

Expand Down
2 changes: 2 additions & 0 deletions packages/server/src/types/article.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class CreateArticleDto {
createdAt?: Date;
author?: string;
copyright?: string;
pathname?: string;
}
export class UpdateArticleDto {
title?: string;
Expand All @@ -29,6 +30,7 @@ export class UpdateArticleDto {
updatedAt?: Date;
author?: string;
copyright?: string;
pathname?: string;
}
export class SearchArticleOption {
page: number;
Expand Down
1 change: 1 addition & 0 deletions packages/server/src/types/draft.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export class UpdateDraftDto {
}
export class PublishDraftDto {
hidden?: boolean;
pathname?: string;
private?: boolean;
password?: string;
copyright?: string;
Expand Down
Loading

0 comments on commit 14394b5

Please sign in to comment.