check point, notebook basic structure defined
This commit is contained in:
1 parent
98c9328a3d
commit
5a01897a44
11 files changed
+303
-43
No files matched your search
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Article" ADD COLUMN "imageUrl" TEXT;
|
||||
@@ -35,6 +35,7 @@ model Article {
|
||||
updatedAt DateTime @updatedAt
|
||||
categories String[]
|
||||
authors String[]
|
||||
imageUrl String?
|
||||
Note Note[]
|
||||
|
||||
@@index([url])
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Controller, Get, Query } from '@nestjs/common';
|
||||
import { Controller, Get, Query, Param } from '@nestjs/common';
|
||||
import { ArticlesService } from './articles.service';
|
||||
|
||||
@Controller('api/articles')
|
||||
@@ -20,4 +20,9 @@ export class ArticlesController {
|
||||
async getCount() {
|
||||
return { count: await this.articlesService.count() };
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async findOne(@Param('id') id: string) {
|
||||
return this.articlesService.findOne(id);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { PaginatedArticles } from './types';
|
||||
|
||||
@@ -46,6 +46,28 @@ export class ArticlesService {
|
||||
};
|
||||
}
|
||||
|
||||
async findOne(id: string) {
|
||||
const article = await this.prisma.article.findUnique({
|
||||
where: { id },
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
content: true,
|
||||
source: true,
|
||||
url: true,
|
||||
publishedAt: true,
|
||||
categories: true,
|
||||
authors: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!article) {
|
||||
throw new NotFoundException(`Article with ID ${id} not found`);
|
||||
}
|
||||
|
||||
return article;
|
||||
}
|
||||
|
||||
async count(): Promise<number> {
|
||||
return this.prisma.article.count();
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ export class RssController {
|
||||
constructor(private readonly rssService: RssService) {}
|
||||
|
||||
@Post('fetch')
|
||||
async fetchAndStore(@Body('feedUrl') feedUrl: string): Promise<{ message: string }> {
|
||||
async fetchAndStore(
|
||||
@Body('feedUrl') feedUrl: string,
|
||||
): Promise<{ message: string }> {
|
||||
await this.rssService.fetchAndStoreArticles(feedUrl);
|
||||
return { message: 'RSS feed processed and articles stored successfully.' };
|
||||
}
|
||||
@@ -15,4 +17,4 @@ export class RssController {
|
||||
async getAllArticles() {
|
||||
return this.rssService.getAllArticles();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,25 +5,76 @@ import * as Parser from 'rss-parser';
|
||||
@Injectable()
|
||||
export class RssService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
feddUrl: string = 'https://www.rt.com/rss/';
|
||||
async fetchAndStoreArticles(feedUrl: string): Promise<void> {
|
||||
const parser = new Parser();
|
||||
const feed = await parser.parseURL(feedUrl);
|
||||
|
||||
for (const item of feed.items) {
|
||||
await this.prisma.article.upsert({
|
||||
where: { url: item.link },
|
||||
update: {},
|
||||
create: {
|
||||
title: item.title || 'Untitled',
|
||||
content: item.contentSnippet || '',
|
||||
source: feed.title || 'Unknown Source',
|
||||
url: item.link || '',
|
||||
publishedAt: item.isoDate ? new Date(item.isoDate) : new Date(),
|
||||
categories: item.categories || [],
|
||||
authors: item.creator ? [item.creator] : [],
|
||||
},
|
||||
});
|
||||
private parser: Parser;
|
||||
|
||||
onModuleInit() {
|
||||
// Configure parser with custom fields
|
||||
this.parser = new Parser({
|
||||
customFields: {
|
||||
item: [
|
||||
['content:encoded', 'contentEncoded'],
|
||||
['dc:creator', 'creator'],
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private cleanHtml(html: string): string {
|
||||
// Remove HTML tags and decode entities
|
||||
return html
|
||||
.replace(/<[^>]*>/g, '') // Remove HTML tags
|
||||
.replace(/ /g, ' ') // Replace with space
|
||||
.replace(/&/g, '&') // Replace & with &
|
||||
.replace(/"/g, '"') // Replace " with "
|
||||
.replace(/'/g, "'"); // Replace ' with '
|
||||
}
|
||||
|
||||
async fetchAndStoreArticles(feedUrl: string): Promise<void> {
|
||||
try {
|
||||
const feed = await this.parser.parseURL(feedUrl);
|
||||
|
||||
for (const item of feed.items) {
|
||||
// Extract content from either contentEncoded or description
|
||||
const rawContent = item.contentEncoded || item.content || item.description || '';
|
||||
const cleanContent = this.cleanHtml(rawContent);
|
||||
|
||||
// Get the first image URL from content if available
|
||||
const imageMatch = rawContent.match(/<img[^>]+src="([^">]+)"/);
|
||||
const imageUrl = imageMatch ? imageMatch[1] : null;
|
||||
|
||||
// Extract categories and authors
|
||||
const categories = Array.isArray(item.categories) ? item.categories : [];
|
||||
const authors = item.creator ? [item.creator] : [];
|
||||
|
||||
await this.prisma.article.upsert({
|
||||
where: { url: item.link || '' },
|
||||
update: {
|
||||
title: item.title || 'Untitled',
|
||||
content: cleanContent,
|
||||
source: feed.title || 'RT',
|
||||
url: item.link || '',
|
||||
publishedAt: item.isoDate ? new Date(item.isoDate) : new Date(),
|
||||
categories,
|
||||
authors,
|
||||
imageUrl,
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
create: {
|
||||
title: item.title || 'Untitled',
|
||||
content: cleanContent,
|
||||
source: feed.title || 'RT',
|
||||
url: item.link || '',
|
||||
publishedAt: item.isoDate ? new Date(item.isoDate) : new Date(),
|
||||
categories,
|
||||
authors,
|
||||
imageUrl,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching RSS feed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user