check point, notebook basic structure defined

This commit is contained in:
echo committed 2025-04-10 01:30:47 +02:00
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;
+1
View File
@@ -35,6 +35,7 @@ model Article {
updatedAt DateTime @updatedAt
categories String[]
authors String[]
imageUrl String?
Note Note[]
@@index([url])
+6 -1
View File
@@ -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);
}
}
+23 -1
View File
@@ -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();
}
+4 -2
View File
@@ -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();
}
}
}
+69 -18
View File
@@ -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(/&nbsp;/g, ' ') // Replace &nbsp; with space
.replace(/&amp;/g, '&') // Replace &amp; with &
.replace(/&quot;/g, '"') // Replace &quot; with "
.replace(/&#39;/g, "'"); // Replace &#39; 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;
}
}