Skip to content

Commit 230eabc

Browse files
committed
⭐(rss) support article image url
1 parent 27afad9 commit 230eabc

File tree

14 files changed

+75
-18
lines changed

14 files changed

+75
-18
lines changed

backend/assets/blogs-original.csv

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ ShrekShao, http://shrekshao.github.io, http://shrekshao.github.io/feed.xml, 编
1212
Python 工匠, https://www.zlovezl.cn, https://www.zlovezl.cn/feeds/latest/, 编程
1313
小明明S À DOMICILE, https://www.dongwm.com, https://www.dongwm.com/atom.xml, 编程
1414
但行好事,莫问前程, https://windard.com, https://windard.com/feed.xml, 编程
15-
罗磊的独立博客, https://luolei.org, http://luolei.org/feed/, 编程; 旅行
15+
罗磊的独立博客, https://luolei.org, https://luolei.org/feed/, 编程; 旅行
1616
阁子, https://newdee.cf, https://newdee.cf/atom.xml, 编程; 算法; 生活
1717
RidiQulous, https://ridiqulous.com, https://ridiqulous.com/feed, 图像处理; 乐高
1818
代码家, https://daimajia.com, https://daimajia.com/feed, 编程; 投资
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Command, Positional } from 'nestjs-command';
2+
import { Injectable } from '@nestjs/common';
3+
import { RssParser } from '../domains/infrastructure/rss-parser';
4+
import { ArticleRepository } from '../domains/hub/respositories/article';
5+
6+
@Injectable()
7+
export class TestRssWithSaveCommand {
8+
constructor(
9+
private readonly articleRepository: ArticleRepository,
10+
) {
11+
}
12+
13+
@Command({command: 'test-rss-with-save <rss>'})
14+
async run(
15+
@Positional({
16+
name: 'rss',
17+
describe: 'rss address of blog',
18+
type: 'string',
19+
}) rss
20+
) {
21+
const output = await new RssParser().fetch(rss);
22+
console.log(JSON.stringify(output, null, 2))
23+
await Promise.all(
24+
output.articles.map(async o => await this.articleRepository.createOrUpdate(o))
25+
)
26+
return
27+
}
28+
}

backend/src/domains/hub/dtos/create-article-dto.ts

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export class CreateArticleDto extends BaseDto {
1313
public tags: string[],
1414
public summary: string,
1515
public content: string,
16+
public imgUrl: string | null,
1617
public date: Date,
1718
) {
1819
super()

backend/src/domains/hub/models/article.ts

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export class Article {
1010
public tags: string[],
1111
public summary: string,
1212
public content: string,
13+
public imgUrl: string | null,
1314
public date: Date,
1415
public views: number = 0,
1516
public createdAt: Date = new Date(),

backend/src/domains/hub/services/rss-crawl-service.ts

+1-8
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,7 @@ export class RssCrawlService {
4949

5050
await Promise.all(
5151
rssParseOutput.articles
52-
.map(async o => {
53-
// try {
54-
await this.articleRepository.createOrUpdate(o)
55-
// } catch (e) {
56-
// console.log(`Try to create or update ${o.repr()} failed`, e)
57-
// }
58-
}
59-
)
52+
.map(async o => await this.articleRepository.createOrUpdate(o))
6053
)
6154
this.logger.log(`Crawl ${feed} success`)
6255
}

backend/src/domains/infrastructure/rss-parser.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export class RssParser {
4141
this.parseCategory(o),
4242
this.parseSummary(o),
4343
o.content?.trim() || o['content:encoded']?.trim() || '',
44+
this.parseImgUrl(o),
4445
this.fixDate(new Date(o.pubDate))
4546
)
4647
)
@@ -56,9 +57,16 @@ export class RssParser {
5657
}
5758
}
5859

60+
private parseImgUrl(article: Parser.Item): string | null {
61+
const imgUrlRegex = /<img[^>]*? src="(.*?)" [^>]*?>/
62+
let imgUrl = article.content?.match(imgUrlRegex)?.[1]
63+
if (!imgUrl) imgUrl = article['content:encoded']?.match(imgUrlRegex)?.[1]
64+
return imgUrl || null
65+
}
66+
5967
private parseCategory(article: Parser.Item): string[] {
6068
let categories = article.categories?.filter(t => typeof t === 'string') || []
61-
if (!categories.length ) {
69+
if (!categories.length) {
6270
// for https://beyondstars.xyz/posts/index.xml
6371
if (typeof article.category === 'string') categories = [article.category]
6472
// for <category term='tag'/>

backend/src/hub/hub.module.ts

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { GenerateTypingsByGraphqlCommand } from '../commands/generate-typings-by
1414
import { CrawlArticlesRegularlyService } from '../crons/crawl-articles-regularly';
1515
import { AddBlogFromIssueCommand } from '../commands/add-blog-from-issue';
1616
import { TestRssCommand } from '../commands/test-rss.command';
17+
import { TestRssWithSaveCommand } from '../commands/test-rss-with-save.command';
1718

1819
const HubTypeOrmModule = TypeOrmModule.forFeature([BlogTable, ArticleTable]);
1920

@@ -32,6 +33,7 @@ const HubTypeOrmModule = TypeOrmModule.forFeature([BlogTable, ArticleTable]);
3233
CrawlArticlesRegularlyService,
3334
AddBlogFromIssueCommand,
3435
TestRssCommand,
36+
TestRssWithSaveCommand,
3537
],
3638
exports: [
3739
HubTypeOrmModule,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import {MigrationInterface, QueryRunner} from "typeorm";
2+
3+
export class addImgUrlOnArticleTable1584491838844 implements MigrationInterface {
4+
name = 'addImgUrlOnArticleTable1584491838844'
5+
6+
public async up(queryRunner: QueryRunner): Promise<void> {
7+
await queryRunner.query("ALTER TABLE `article_table` ADD `imgUrl` varchar(2048) NULL", undefined);
8+
}
9+
10+
public async down(queryRunner: QueryRunner): Promise<void> {
11+
await queryRunner.query("ALTER TABLE `article_table` DROP COLUMN `imgUrl`", undefined);
12+
}
13+
14+
}

backend/src/tables/article.ts

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ export class ArticleTable {
3232
@Column('longtext')
3333
public content: string;
3434

35+
@Column({length: 2048, nullable: true})
36+
public imgUrl: string;
37+
3538
@Column()
3639
public date: Date;
3740

@@ -54,6 +57,7 @@ export class ArticleTable {
5457
this.tags.split(','),
5558
this.summary,
5659
this.content,
60+
this.imgUrl,
5761
this.date,
5862
this.views,
5963
this.createdAt,

frontend/components/article-twitter-card.tsx

+2-4
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@ export default function ArticleTwitterCard({article}: { article: Article }) {
66
let card = TwitterCardType.SUMMARY;
77
let image = DEFAULT_TWITTER_CARD_IMAGE;
88

9-
// Find md image urls
10-
const imageRegex: string[] | null = article.content.match(/!\[.*?]\((.*?)\)/);
11-
if (imageRegex) {
9+
if (article.imgUrl) {
1210
card = TwitterCardType.SUMMARY_LARGE_IMAGE;
13-
image = imageRegex[1];
11+
image = article.imgUrl;
1412
}
1513
return (
1614
<TwitterCard card={card} title={article.title} description={article.summary} image={image}/>

frontend/graphql/queries.ts

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export const RETRIEVE_ARTICLE_BY_SLUG = gql`
5656
...ArticleMetadata
5757
url
5858
summary(length: 99999)
59+
imgUrl
5960
polishedSummary @client
6061
content
6162
}

frontend/pages/articles/[slug].tsx

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Layout from '../../components/layout';
22
import { withApollo } from '../../libs/with-apollo';
3-
import { IArticleOfClient, IBlogOfClient, RETRIEVE_ARTICLE_BY_SLUG } from '../../graphql/queries';
3+
import { IArticleOfClient, RETRIEVE_ARTICLE_BY_SLUG } from '../../graphql/queries';
44
import Head from 'next/head';
55
import ArticleTwitterCard from '../../components/article-twitter-card';
66
import { TimeUtils } from '../../domains/infrastructure/time-utils';
@@ -55,9 +55,14 @@ const Page = ({article}: { article: IArticleOfClient }) => {
5555
</div>
5656
</header>
5757

58-
59-
<article className="text-gray-700 leading-relaxed text-justify">
60-
{article.polishedSummary}……
58+
<article>
59+
{article.imgUrl && <img
60+
className="mb-2 mx-auto"
61+
src={article.imgUrl}
62+
alt={`${article.title}的配图`}/>}
63+
<div className="text-gray-700 leading-relaxed text-justify">
64+
{article.polishedSummary}……
65+
</div>
6166
</article>
6267
</div>
6368

graphql/defines.graphql

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type Article {
3030
tags: [String!]!
3131
summary(length: Int! = 200): String!
3232
content: String!
33+
imgUrl: String
3334
date: DateTime!
3435
views: Int!
3536
createdAt: DateTime!

graphql/graphql.ts

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export interface Article {
2020
tags: string[];
2121
summary: string;
2222
content: string;
23+
imgUrl?: string;
2324
date: DateTime;
2425
views: number;
2526
createdAt: DateTime;

0 commit comments

Comments
 (0)