mirror of
https://github.com/hpware/news-analyze.git
synced 2025-06-22 23:31:01 +08:00
commit
a33a2364ef
1
.github/funding.yml
vendored
Normal file
1
.github/funding.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
ko_fi: howard00
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -1,6 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
// FOR THIS MODULE DO NOT USE THE ?APPNAME URL TYPE, IT WILL FALL AT ALL TIMES, I HAVE NO CLUE WHY IS BEHAVIOR HAPPENING RN?
|
||||
import { SparklesIcon, UserIcon, NewspaperIcon } from "lucide-vue-next";
|
||||
import {
|
||||
SparklesIcon,
|
||||
UserIcon,
|
||||
NewspaperIcon,
|
||||
StarIcon,
|
||||
} from "lucide-vue-next";
|
||||
import translate from "translate";
|
||||
|
||||
interface translateInterfaceText {
|
||||
@ -178,6 +183,7 @@ const aiSummary = async () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button><StarIcon /></button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -1,9 +1,11 @@
|
||||
<template>
|
||||
<!--YouTube Embed-->
|
||||
<div class="justify-center absolute inset-0 flex flex-col">
|
||||
<div
|
||||
class="justify-center align-center text-center absolute inset-0 flex flex-col mt-12"
|
||||
>
|
||||
<iframe
|
||||
width="560"
|
||||
height="315"
|
||||
width="600"
|
||||
height="395"
|
||||
src="https://www.youtube-nocookie.com/embed/8P3qgVm6m6g?si=0t8eR0wtWv6b3REE"
|
||||
title="YouTube video player"
|
||||
frameborder="0"
|
||||
|
@ -27,6 +27,10 @@ const userData = ref({
|
||||
});
|
||||
const enteruseremail = ref();
|
||||
onMounted(async () => {
|
||||
await validateUserInfo();
|
||||
});
|
||||
|
||||
const validateUserInfo = async () => {
|
||||
const req = await fetch("/api/user/validateUserToken");
|
||||
const res = await req.json();
|
||||
if (res.current_spot === "LOGOUT") {
|
||||
@ -37,7 +41,12 @@ onMounted(async () => {
|
||||
userData.value = res;
|
||||
useremail.value = res.email;
|
||||
isLoggedIn.value = true;
|
||||
});
|
||||
};
|
||||
|
||||
const intervalTime = 1000 * 60 * 2; // Validate user Info for every ten min while the admin page is opened.
|
||||
setInterval(async () => {
|
||||
await validateUserInfo();
|
||||
}, intervalTime);
|
||||
|
||||
const emit = defineEmits(["windowopener"]);
|
||||
|
||||
@ -91,22 +100,24 @@ const checkValidApiKey = () => {
|
||||
const showDeleteDialog = ref(false);
|
||||
const showLogoutDialog = ref(false);
|
||||
const confirmDelete = async () => {
|
||||
await deleteAccount();
|
||||
showDeleteDialog.value = false;
|
||||
await deleteAccount();
|
||||
await validateUserInfo();
|
||||
};
|
||||
|
||||
const deleteAccount = async () => {
|
||||
const req = await fetch("/api/user/sendUserChanges", {
|
||||
method: "DELETE",
|
||||
});
|
||||
const res = await res.json();
|
||||
const res = await req.json();
|
||||
console.log(res);
|
||||
};
|
||||
|
||||
const submitChangeAction = async (action: string) => {
|
||||
//const allowedColumns = ["firstname", "email"];
|
||||
const actions = [
|
||||
{ name: "NAME", sendValue: enterFirstName.value },
|
||||
{ name: "USER_EMAIL", sendValue: enteruseremail.value },
|
||||
{ name: "NAME", SQLSystem: "firstname", sendValue: enterFirstName.value },
|
||||
{ name: "USER_EMAIL", SQLSystem: "email", sendValue: enteruseremail.value },
|
||||
];
|
||||
|
||||
const actionMatch = actions.find((a) => a.name === action);
|
||||
@ -121,7 +132,7 @@ const submitChangeAction = async (action: string) => {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
action: actionMatch.name,
|
||||
action: actionMatch.SQLSystem,
|
||||
value: actionMatch.sendValue,
|
||||
jsonValue: "",
|
||||
}),
|
||||
@ -130,7 +141,9 @@ const submitChangeAction = async (action: string) => {
|
||||
const response = await req.json();
|
||||
if (response.error) {
|
||||
console.error("Error updating user data:", response.error);
|
||||
return;
|
||||
}
|
||||
await validateUserInfo();
|
||||
} catch (error) {
|
||||
console.error("Failed to submit change:", error);
|
||||
}
|
||||
@ -166,6 +179,7 @@ const submitUserPassword = async () => {
|
||||
success.value = true;
|
||||
console.log(res);
|
||||
userAccount.value = "";
|
||||
await validateUserInfo();
|
||||
} else {
|
||||
error.value = true;
|
||||
errormsg.value = res.error;
|
||||
|
@ -2,5 +2,8 @@
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
<template>
|
||||
<div></div>
|
||||
<div class="justify-center align-center text-center">
|
||||
<h1 class="text-2xl text-bold">{{ t("pages.tos.title") }}</h1>
|
||||
<p>{{ t("pages.tos.content") }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -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
|
||||
)
|
||||
`;
|
||||
|
||||
|
@ -374,7 +374,9 @@ onMounted(async () => {
|
||||
if (openApp.value === "newsView") {
|
||||
return;
|
||||
}
|
||||
openWindow(openApp.value);
|
||||
setTimeout(() => {
|
||||
openWindow(openApp.value);
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -132,7 +132,14 @@ useSeoMeta({
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<NuxtLink :to="localePath('/desktop')">
|
||||
<NuxtLink
|
||||
:to="
|
||||
localePath({
|
||||
path: '/desktop',
|
||||
query: { openapp: 'onboard' },
|
||||
})
|
||||
"
|
||||
>
|
||||
<button
|
||||
class="m-4 mr-1 ml-1 bg-[#8C9393] text-white p-3 rounded-[10px] bg-gradient-to-l from-sky-500 to-purple-600 transition-all duration-150 hover:transform hover:scale-105 hover:shadow-lg"
|
||||
>
|
||||
|
@ -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,
|
||||
};
|
||||
});
|
||||
|
11
server/api/download/news-article-archive.json.ts
Normal file
11
server/api/download/news-article-archive.json.ts
Normal file
@ -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;
|
||||
});
|
@ -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,
|
||||
|
30
server/api/user/[slug]/isThisArticleStarred.ts
Normal file
30
server/api/user/[slug]/isThisArticleStarred.ts
Normal file
@ -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,
|
||||
};
|
||||
}
|
||||
});
|
30
server/api/user/[slug]/star.ts
Normal file
30
server/api/user/[slug]/star.ts
Normal file
@ -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,
|
||||
};
|
||||
}
|
||||
});
|
@ -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,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user