diff --git a/frontend/src/css/main.css b/frontend/src/css/main.css index 68e18c0..0a3858b 100644 --- a/frontend/src/css/main.css +++ b/frontend/src/css/main.css @@ -40,11 +40,19 @@ background-color: rgba(55, 55, 55, 0.45) !important; } -#app { +html, body { margin: 0; - bottom: 0; - width: 100vw; - height: 100vh; + width: 100%; + min-height: 100%; +} + +#app { + width: 100%; + min-height: 100vh; + display: flex; + justify-content: center; + align-items: center; + padding-bottom: 80px; } .news-ui-action { @@ -61,8 +69,6 @@ } .auth-card { - margin-left: 30vw; - margin-right: 30vw; - margin-top: 30vh; - margin-bottom: 30vh; + width: min(430px, calc(100vw - 32px)); + margin: 0; } \ No newline at end of file diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 3e39084..6fd34b6 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -81,8 +81,7 @@ async function main() { }); appElement.style = currentStyle; - //document.body.innerHTML = appElement.build() + navbar.build(); - document.body.innerHTML = appElement.build(); + document.body.innerHTML = appElement.build() + navbar.build(); await changeStyleLogic(appElement, navbar); } diff --git a/src/api/models/news.ts b/src/api/models/news.ts index 11fa85b..c7e06e4 100644 --- a/src/api/models/news.ts +++ b/src/api/models/news.ts @@ -1,7 +1,8 @@ -export interface Source { +export interface NewsSource { name: string, icon?: string, sourceURL?: string, + type: "RSS", itemURL?: string, } @@ -9,6 +10,6 @@ export interface NewsItem { id : number; title : string; content : string; - source: Source; + source: NewsSource; publishedAt : Date; } \ No newline at end of file diff --git a/src/server.ts b/src/server.ts index 417df50..7998140 100644 --- a/src/server.ts +++ b/src/server.ts @@ -16,7 +16,7 @@ declare module 'express-serve-static-core' { // Checking auth app.use((req, res, next) => { req.authenticationRules = { type: "password" }; - req.isAuthenticated = false; // Mock authentication, replace with real logic + req.isAuthenticated = true; // Mock authentication, replace with real logic // Add your authentication logic here next(); }); diff --git a/src/utils/database/index.ts b/src/utils/database/index.ts new file mode 100644 index 0000000..2ad464f --- /dev/null +++ b/src/utils/database/index.ts @@ -0,0 +1,3 @@ +class Database {} + +export = new Database; \ No newline at end of file diff --git a/src/utils/news.ts b/src/utils/news/index.ts similarity index 78% rename from src/utils/news.ts rename to src/utils/news/index.ts index abf49e2..358ae50 100644 --- a/src/utils/news.ts +++ b/src/utils/news/index.ts @@ -1,4 +1,4 @@ -import { NewsItem } from "../api/models/news"; +import { NewsItem } from "../../api/models/news"; export function getTestNews (): NewsItem[] { console.warn("Remove getTestNews from production! Only for tests!"); @@ -10,7 +10,8 @@ export function getTestNews (): NewsItem[] { content: "Lorem Ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet,", publishedAt: new Date(), source: { - name: "Test Destination" + name: "Test Destination", + type: "RSS", } }, { @@ -19,7 +20,8 @@ export function getTestNews (): NewsItem[] { content: "This is the content for news item 1. It contains some sample text to demonstrate the news functionality.", publishedAt: new Date(), source: { - name: "Test Destination" + name: "Test Destination", + type: "RSS", } }, { @@ -28,7 +30,8 @@ export function getTestNews (): NewsItem[] { content: "This is the content for news item 2. Another piece of sample news content for testing purposes.", publishedAt: new Date(), source: { - name: "Test Destination" + name: "Test Destination", + type: "RSS", } }, { @@ -37,7 +40,8 @@ export function getTestNews (): NewsItem[] { content: "News item 3 brings you the latest updates. Stay informed with our regular news feed.", publishedAt: new Date(), source: { - name: "Test Destination" + name: "Test Destination", + type: "RSS", } }, { @@ -46,7 +50,8 @@ export function getTestNews (): NewsItem[] { content: "Content for the fourth news item. This is part of the test data for the application.", publishedAt: new Date(), source: { - name: "Test Destination" + name: "Test Destination", + type: "RSS", } }, { @@ -55,7 +60,8 @@ export function getTestNews (): NewsItem[] { content: "Fifth news item in the list. More sample content to populate the news array.", publishedAt: new Date(), source: { - name: "Test Destination" + name: "Test Destination", + type: "RSS", } }, { @@ -64,7 +70,8 @@ export function getTestNews (): NewsItem[] { content: "This is news item number six. Continuing to add test data for the news feature.", publishedAt: new Date(), source: { - name: "Test Destination" + name: "Test Destination", + type: "RSS", } }, { @@ -73,7 +80,8 @@ export function getTestNews (): NewsItem[] { content: "Seventh item in the news feed. Sample text to ensure the array has sufficient data.", publishedAt: new Date(), source: { - name: "Test Destination" + name: "Test Destination", + type: "RSS", } }, { @@ -82,7 +90,8 @@ export function getTestNews (): NewsItem[] { content: "News item eight provides more content. This helps in testing the display of multiple items.", publishedAt: new Date(), source: { - name: "Test Destination" + name: "Test Destination", + type: "RSS", } }, { @@ -91,7 +100,8 @@ export function getTestNews (): NewsItem[] { content: "The ninth news item. Almost reaching the total of 13 items including the original.", publishedAt: new Date(), source: { - name: "Test Destination" + name: "Test Destination", + type: "RSS", } }, { @@ -100,7 +110,8 @@ export function getTestNews (): NewsItem[] { content: "Tenth item in the array. This completes the set of additional news items requested.", publishedAt: new Date(), source: { - name: "Test Destination" + name: "Test Destination", + type: "RSS", } }, { @@ -109,7 +120,8 @@ export function getTestNews (): NewsItem[] { content: "Eleventh news item. Continuing to add variety to the test data.", publishedAt: new Date(), source: { - name: "Test Destination" + name: "Test Destination", + type: "RSS", } }, { @@ -118,7 +130,8 @@ export function getTestNews (): NewsItem[] { content: "The twelfth and final news item. This brings the total to 13 items in the array.", publishedAt: new Date(), source: { - name: "Test Destination" + name: "Test Destination", + type: "RSS", } } ]; diff --git a/src/utils/news/service.ts b/src/utils/news/service.ts new file mode 100644 index 0000000..771360b --- /dev/null +++ b/src/utils/news/service.ts @@ -0,0 +1,67 @@ +import { NewsSource, NewsItem } from "../../api/models/news"; + +class NewsSourceTarget { + protected source : NewsSource; + protected parent : NewsParserService; + constructor(source : NewsSource, parent : NewsParserService) { + this.source = source; + this.parent = parent; + } + + protected async readerRSS () : Promise { + return []; + } + + async readAll() : Promise { + switch (this.source.type) { + case "RSS": + return this.readerRSS(); + default: + const impossible: never = this.source.type; + return impossible; + } + } +} + +class NewsParserService { + protected sources : Set = new Set; + protected sourceMap : WeakMap = new WeakMap; + + protected newsItems : Set = new Set; + + async setupSources(sources : NewsSource[]) { + this.sources.clear(); + sources.forEach(source => this.addSource(source)); + } + + async addSource(source : NewsSource) { + const sourceTarget = new NewsSourceTarget(source, this); + this.sources.add(sourceTarget); + this.sourceMap.set(source, sourceTarget); + + const news = await sourceTarget.readAll(); + news.forEach(newsItem => new Promise((resolve) => { + this.newsItems.add(newsItem); + resolve(); + })); + } + + async removeSource(source : NewsSource) { + const sourceTarget = this.sourceMap.get(source); + if (sourceTarget) { + this.sources.delete(sourceTarget); + this.sourceMap.delete(source); + + const forDeleting: Set = new Set; + for (let newsItem of this.newsItems) { + if (newsItem.source === source) + forDeleting.add(newsItem); + } + for (let deleteItem of forDeleting) { + this.newsItems.delete(deleteItem); + } + } + } +} + +export const newsParserService = new NewsParserService; \ No newline at end of file diff --git a/src/utils/requests.ts b/src/utils/requests.ts new file mode 100644 index 0000000..23739fd --- /dev/null +++ b/src/utils/requests.ts @@ -0,0 +1,44 @@ +import http from "http"; +import https from "https"; +import { Readable } from "stream"; + +type StringUrl = `http${"s" | ""}://${string}`; +type Results = string | Buffer | Readable; +type ResultType = "string" | "buffer" | "stream"; + +export function get(url: StringUrl, type: "string", queries : Record): Promise; +export function get(url: StringUrl, type: "buffer", queries : Record): Promise; +export function get(url: StringUrl, type: "stream", queries : Record): Promise; + +export async function get( + url : StringUrl, + type : ResultType, + queries : Record = {} +): Promise { + const protocol = url.startsWith("https") ? https : http; + + // Build query string from queries object + const queryString = Object.entries(queries) + .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`) + .join('&'); + + const fullUrl = queryString ? `${url}?${queryString}` : url; + + return new Promise((resolve, reject) => { + protocol.get(fullUrl, (res) => { + if (type === "stream") { + resolve(res); + } else if (type === "string") { + let data = ""; + res.on("data", chunk => data += chunk); + res.on("end", () => resolve(data)); + res.on("error", reject); + } else if (type === "buffer") { + const chunks: Buffer[] = []; + res.on("data", chunk => chunks.push(chunk)); + res.on("end", () => resolve(Buffer.concat(chunks))); + res.on("error", reject); + } + }).on("error", reject); + }); +} \ No newline at end of file