diff --git a/.github/funding.yml b/.github/funding.yml new file mode 100644 index 0000000..b2ced52 --- /dev/null +++ b/.github/funding.yml @@ -0,0 +1 @@ +ko_fi: howard00 diff --git a/README.md b/README.md index df40a9b..322db69 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Video Guide: [YouTube](https://youtu.be/8P3qgVm6m6g) ## Demo: Production (Latest Docker Image): https://yhw.tw/news -Beta (Beta Docekr Image): https://newsbeta.20090526.xyz +Beta (Beta Docker Image): https://newsbeta.20090526.xyz ## Video Guide diff --git a/components/app/windows/news.vue b/components/app/windows/news.vue index cb82713..8ce825a 100644 --- a/components/app/windows/news.vue +++ b/components/app/windows/news.vue @@ -81,7 +81,15 @@ const updateContent = async (url: string, tabAction: boolean) => { const req = await fetch(`/api/home/lt?query=${url.trim()}`); const data = await req.json(); if (data) { - contentArray.value = [...data.uuidData, ...(data.nuuiddata?.items || [])]; + // Made by coderabbit: https://github.com/hpware/news-analyze/pull/6#discussion_r2144713017 + const coolArray = [ + ...(data.uuidData ?? []), + ...(data.nuuiddata?.items ?? []), + ]; + contentArray.value = + coolArray.sort( + (title1, title2) => title2.publishTimeUnix - title1.publishTimeUnix, + ) || []; switchTabs.value = false; isDataCached.value = data.cached || false; displayTranslateContent.value = false; diff --git a/components/app/windows/newsView.vue b/components/app/windows/newsView.vue index 32a3354..8b0fff2 100644 --- a/components/app/windows/newsView.vue +++ b/components/app/windows/newsView.vue @@ -1,6 +1,11 @@ - + + {{ t("pages.tos.title") }} + {{ t("pages.tos.content") }} + diff --git a/createDatabase.ts b/createDatabase.ts index 4ebc340..b4959e4 100644 --- a/createDatabase.ts +++ b/createDatabase.ts @@ -54,9 +54,9 @@ const createSources = await sql` const createArticlesArchive = await sql` create table if not exists news_articles ( uuid text primary key, - article_id text primary key, + article_id text, jsondata json not null, - archive_timestamp timestamp default CURRENT_TIMESTAMP, + archive_timestamp timestamp default CURRENT_TIMESTAMP ) `; diff --git a/pages/desktop.vue b/pages/desktop.vue index dcc127d..04c70bf 100644 --- a/pages/desktop.vue +++ b/pages/desktop.vue @@ -374,7 +374,9 @@ onMounted(async () => { if (openApp.value === "newsView") { return; } - openWindow(openApp.value); + setTimeout(() => { + openWindow(openApp.value); + }, 2000); } }); diff --git a/pages/home.vue b/pages/home.vue index bbe7524..5b4cb0f 100644 --- a/pages/home.vue +++ b/pages/home.vue @@ -132,7 +132,14 @@ useSeoMeta({ - + diff --git a/server/api/create_database.ts b/server/api/create_database.ts index 25f326d..bf6fbfb 100644 --- a/server/api/create_database.ts +++ b/server/api/create_database.ts @@ -34,8 +34,8 @@ export default defineEventHandler(async (event) => { const createUserOtherData = await sql` create table if not exists user_other_data ( - user_id text primary key , - user text not null unique, + user_id text primary key, + username text not null unique, groq_api_key text, starred_news JSON not null, translate_provider text, @@ -51,11 +51,21 @@ export default defineEventHandler(async (event) => { ) `; + const createArticlesArchive = await sql` + create table if not exists news_articles ( + uuid text primary key, + article_id text primary key, + jsondata json not null, + archive_timestamp timestamp default CURRENT_TIMESTAMP, + ) + `; + return { createUsers: createUsers, usersList: usersList, createUserAiChatHistory: createUserAiChatHistory, createSources: createSources, createUserOtherData: createUserOtherData, + createArticlesArchive: createArticlesArchive, }; }); diff --git a/server/api/download/news-article-archive.json.ts b/server/api/download/news-article-archive.json.ts new file mode 100644 index 0000000..d07ff91 --- /dev/null +++ b/server/api/download/news-article-archive.json.ts @@ -0,0 +1,11 @@ +import sql from "~/server/components/postgres"; +export default defineEventHandler(async (event) => { + const articles = await sql` + SELECT * FROM news_articles; + `; + setHeaders(event, { + "Content-Type": "application/json", + "Content-Disposition": `attachment; filename="news-articles-export-${new Date().toISOString().split("T")[0]}.json"`, + }); + return articles; +}); diff --git a/server/api/publishers/lt/[slug].ts b/server/api/publishers/lt/[slug].ts index f088046..722c054 100644 --- a/server/api/publishers/lt/[slug].ts +++ b/server/api/publishers/lt/[slug].ts @@ -1,6 +1,7 @@ import sql from "~/server/components/postgres"; import CheckKidUnfriendlyContent from "~/components/checks/checkKidUnfriendlyContent"; import * as cheerio from "cheerio"; +import { v4 as uuidv4 } from "uuid"; // Caching @@ -91,9 +92,16 @@ export default defineEventHandler(async (event) => { }); } }); - /*const pushNewsOrg = await sql` - insert into - `*/ + const pushNewsOrg = await sql` + insert into lt_news_org (news_id, name, description) + values (${uuidv4()}, ${newsOrgName}, ${description}) + `; + console.log(pushNewsOrg); + /** + * news_id text primary key, + name text not null, + description text + */ cache[slug] = { slug: slug, title: newsOrgName, diff --git a/server/api/user/[slug]/isThisArticleStarred.ts b/server/api/user/[slug]/isThisArticleStarred.ts new file mode 100644 index 0000000..b6d9e5c --- /dev/null +++ b/server/api/user/[slug]/isThisArticleStarred.ts @@ -0,0 +1,30 @@ +import sql from "~/server/components/postgres"; +import getUserTokenMinusSQLInjection from "~/server/components/getUserToken"; +export default defineEventHandler(async (event) => { + try { + const slug = getRouterParam(event, "slug"); + const token = await getUserTokenMinusSQLInjection(event); + if (token.error.length !== 0) { + return { + error: token.error, + }; + } + const getOtherUserDataJsonFile = await sql` + SELECT starred_news from user_other_data + where username = ${token.user} + `; + if (getOtherUserDataJsonFile.length === 0) { + return { + error: "ERR_NO_DATA", + }; + } + const jsonData = getOtherUserDataJsonFile[0].starred_news; + return jsonData; + } catch (e) { + console.log(e); + return { + error: "INTERNAL_SERVER_ERR", + e: e.message, + }; + } +}); diff --git a/server/api/user/[slug]/star.ts b/server/api/user/[slug]/star.ts new file mode 100644 index 0000000..b6d9e5c --- /dev/null +++ b/server/api/user/[slug]/star.ts @@ -0,0 +1,30 @@ +import sql from "~/server/components/postgres"; +import getUserTokenMinusSQLInjection from "~/server/components/getUserToken"; +export default defineEventHandler(async (event) => { + try { + const slug = getRouterParam(event, "slug"); + const token = await getUserTokenMinusSQLInjection(event); + if (token.error.length !== 0) { + return { + error: token.error, + }; + } + const getOtherUserDataJsonFile = await sql` + SELECT starred_news from user_other_data + where username = ${token.user} + `; + if (getOtherUserDataJsonFile.length === 0) { + return { + error: "ERR_NO_DATA", + }; + } + const jsonData = getOtherUserDataJsonFile[0].starred_news; + return jsonData; + } catch (e) { + console.log(e); + return { + error: "INTERNAL_SERVER_ERR", + e: e.message, + }; + } +}); diff --git a/server/api/user/loadInfo.ts b/server/api/user/loadInfo.ts index 71188b0..1806233 100644 --- a/server/api/user/loadInfo.ts +++ b/server/api/user/loadInfo.ts @@ -1,7 +1,6 @@ -// Fixed data for testing +/*// Fixed data for testing export default defineEventHandler(async (event) => { return { - langPref: "en", doNotShowLangPrefPopUp: false, email: "test@yuanhau.com", name: "Howard", @@ -13,3 +12,48 @@ export default defineEventHandler(async (event) => { }, }; }); +*/ +import sql from "~/server/components/postgres"; +import getUserTokenMinusSQLInjection from "~/server/components/getUserToken"; +export default defineEventHandler(async (event) => { + try { + const token = await getUserTokenMinusSQLInjection(event); + if (token.error.length !== 0) { + return { + error: token.error, + }; + } + const fetchMainData = await sql` + SELECT * FROM users + WHERE username = ${token.user} + `; + const fetchOtherUserData = await sql` + SELECT * FROM user_other_data + WHERE username = ${token.user} + `; + + if (fetchMainData.length === 0 || fetchOtherUserData.length === 0) { + return { + error: "ERR_USER_DOESNT_EXIST", + }; + } + return { + doNotShowLangPrefPopUp: + fetchOtherUserData[0].remove_translate_popup || false, + email: fetchMainData[0].email || "", + name: fetchMainData[0].firstname || "", + useCustomGroqKey: +(fetchOtherUserData[0].groq_api_key?.length ?? 0) > 0, + translate: { + enabled: fetchOtherUserData[0].translate_enabled || false, + lang: "en", + provider: fetchOtherUserData[0].translate_provider || "google", + }, + }; + } catch (e) { + console.log(e); + return { + error: "ERR_SERVER_SIDE", + e: e.message, + }; + } +}); diff --git a/server/api/user/login.ts b/server/api/user/login.ts index 0bd32f1..67dfd42 100644 --- a/server/api/user/login.ts +++ b/server/api/user/login.ts @@ -81,6 +81,9 @@ export default defineEventHandler(async (event) => { VALUES (${fetchUserInfoAgain[0].username}, ${newToken}) `; + const getUserFirstName = await sql` + select * from user_other_data`; + setCookie(event, "token", newToken); return { user: fetchUserInfoAgain, diff --git a/server/api/user/sendUserChanges.post.ts b/server/api/user/sendUserChanges.post.ts index 1dc17a4..6bbf0da 100644 --- a/server/api/user/sendUserChanges.post.ts +++ b/server/api/user/sendUserChanges.post.ts @@ -12,23 +12,34 @@ export default defineEventHandler(async (event) => { const body = await readBody(event); if (body.jsonValue.length === 0) { const clearBadDataRegex = /[@-_.+a-zA-Z0-9]{2,}/; - let allowed = true; - if (body.value.match()) { - allowed = false; - } // Use Static values for now. - const requestChange = "groq_api_key"; + const requestChange = body.action || ""; const apiKeyqq = body.value.match(clearBadDataRegex); - const allowedColumns = ["groq_api_key", "another_column_name"]; + const allowedColumns = ["firstname", "email"]; if (!allowedColumns.includes(requestChange)) { - throw new Error("Invalid column name provided"); + return { + error: "ERR_NOT_ALLOWED", + }; + } else if (requestChange === "firstname") { + const sqlC = await sql` + UPDATE users SET firstname = ${apiKeyqq[0]} + WHERE username = ${token.user}`; + return { + sqlC: sqlC, + success: true, + }; + } else if (requestChange === "email") { + const sqlC = await sql` + UPDATE users SET email = ${apiKeyqq[0]} + WHERE username = ${token.user}`; + return { + sqlC: sqlC, + success: true, + }; } - const sqlC = await sql.unsafe( - ` - UPDATE user_other_data SET ${requestChange} = $1 - WHERE username = $2`, + `UPDATE user_other_data SET ${requestChange} = $1 WHERE username = $2`, [apiKeyqq[0], token.user], ); return { diff --git a/server/api/user/validateUserToken.ts b/server/api/user/validateUserToken.ts index 76a0fa3..f147a86 100644 --- a/server/api/user/validateUserToken.ts +++ b/server/api/user/validateUserToken.ts @@ -43,7 +43,7 @@ export default defineEventHandler(async (event) => { } return { userAccount: fetchViaSQL[0].username, - firstName: fetchViaSQL[0].firstName, + firstName: "", requested_action: "CONTINUE", current_spot: "KEEP_LOGIN", email: fetchViaSQL[0].email,
{{ t("pages.tos.content") }}