mirror of
https://github.com/hpware/news-analyze.git
synced 2025-06-24 00:01:03 +08:00
Compare commits
No commits in common. "1eb15058d7931b7b8c5383995c085c8a2ffcc682" and "b356afe7668e9f39f9369846c3f5c7689d29e3a7" have entirely different histories.
1eb15058d7
...
b356afe766
@ -18,7 +18,7 @@ Reverse engineering 文檔: [about](/about/)
|
|||||||
## 在部署之前,請先知道:
|
## 在部署之前,請先知道:
|
||||||
此程式碼絕對不是為在 Vercel 或 Netlify 上啟動而設計的,它現在在主網站程式碼中具有crawling,而且整個「快取功能」都基於Ram,所以請不要使用這些平台,對於 Zeabur 來說,您的成本一定會比較貴一點。網址:https://news.yuanhau.com 託管在我自己的infra上,你也應該這麼做。可以在Yahoo拍賣、蝦皮取得伺服器來執行這個程式。
|
此程式碼絕對不是為在 Vercel 或 Netlify 上啟動而設計的,它現在在主網站程式碼中具有crawling,而且整個「快取功能」都基於Ram,所以請不要使用這些平台,對於 Zeabur 來說,您的成本一定會比較貴一點。網址:https://news.yuanhau.com 託管在我自己的infra上,你也應該這麼做。可以在Yahoo拍賣、蝦皮取得伺服器來執行這個程式。
|
||||||
|
|
||||||
## Note for developing
|
## Note for deing
|
||||||
The desktop enviroment is super unstable when even using a beefy computer, even so, the desktop will lag when opening the newsView, like it's just hates being in a dev env. Prod app works tho, so you can demo it using `bun run build && bun run preview` for demoing. Please don't file a issue request for this matter. If you have the fix, please contribute using Github PRs.
|
The desktop enviroment is super unstable when even using a beefy computer, even so, the desktop will lag when opening the newsView, like it's just hates being in a dev env. Prod app works tho, so you can demo it using `bun run build && bun run preview` for demoing. Please don't file a issue request for this matter. If you have the fix, please contribute using Github PRs.
|
||||||
|
|
||||||
## 為什麼?
|
## 為什麼?
|
||||||
|
@ -15,9 +15,6 @@ Deploy: [via docker compose](/deploy.md)
|
|||||||
## Demo:
|
## Demo:
|
||||||
You can try out the app RIGHT NOW via this link: https://yhw.tw/news?goto=desktop
|
You can try out the app RIGHT NOW via this link: https://yhw.tw/news?goto=desktop
|
||||||
|
|
||||||
## Using Translations:
|
|
||||||
A few pages now contains translations, like the aboutNewsOrg & newsView pages. This project currently is using Google Translate. However, muiti translate platform support is coming soon™ (If you login with your account). The translations are not accrate at all, like something that should be `I just want to write about sports` becomes `I just want to write`, that bro, what is even that?
|
|
||||||
|
|
||||||
## Before deploying, please know this:
|
## Before deploying, please know this:
|
||||||
This code is absolutly NOT designed to be spinned up at Vercel or Netlify, it has the scraping system now inside of the main website code, oh also the entire "caching feature" is based in memory, so please don't use those platforms, for Zeabur your cost might be expensive. idk, I haven't tried hit yet. The web url: https://news.yuanhau.com is hosted on my own infra, you should too. Please get a server off of yahoo 拍賣, 蝦皮 or eBay to do so.
|
This code is absolutly NOT designed to be spinned up at Vercel or Netlify, it has the scraping system now inside of the main website code, oh also the entire "caching feature" is based in memory, so please don't use those platforms, for Zeabur your cost might be expensive. idk, I haven't tried hit yet. The web url: https://news.yuanhau.com is hosted on my own infra, you should too. Please get a server off of yahoo 拍賣, 蝦皮 or eBay to do so.
|
||||||
|
|
||||||
|
@ -6,12 +6,6 @@ import { ScrambleTextPlugin } from "gsap/dist/ScrambleTextPlugin";
|
|||||||
gsap.registerPlugin(ScrambleTextPlugin);
|
gsap.registerPlugin(ScrambleTextPlugin);
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
const { t, locale } = useI18n();
|
const { t, locale } = useI18n();
|
||||||
import translate from "translate";
|
|
||||||
|
|
||||||
interface translateInterfaceText {
|
|
||||||
translateText: string;
|
|
||||||
}
|
|
||||||
const translateItem: Record<string, translateInterfaceText> = {};
|
|
||||||
|
|
||||||
const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
"windowopener",
|
"windowopener",
|
||||||
@ -25,7 +19,6 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
applyForTranslation: Boolean,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const staticProps = props;
|
const staticProps = props;
|
||||||
@ -66,62 +59,8 @@ watch(
|
|||||||
const openNews = (url: string, titleName: string) => {
|
const openNews = (url: string, titleName: string) => {
|
||||||
emit("openArticles", url, titleName);
|
emit("openArticles", url, titleName);
|
||||||
};
|
};
|
||||||
|
|
||||||
const startTranslating = async (text: string) => {
|
|
||||||
try {
|
|
||||||
console.log(text);
|
|
||||||
translateItem[text] = {
|
|
||||||
translateText: await translate(text, { from: "zh", to: "en" }),
|
|
||||||
};
|
|
||||||
console.log(translateItem[text]);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Translation failed:", error);
|
|
||||||
traslateFailed.value = true;
|
|
||||||
translateItem[text] = { translateText: text }; // fallback to original text
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Translating logic
|
|
||||||
const translateText = ref(false);
|
|
||||||
const translatedBefore = ref(false);
|
|
||||||
const traslateFailed = ref(false);
|
|
||||||
const displayTranslatedText = ref(false);
|
|
||||||
const loadingTranslations = ref(false);
|
|
||||||
watch(
|
|
||||||
() => props.applyForTranslation,
|
|
||||||
(value) => {
|
|
||||||
if (value === true) {
|
|
||||||
translateText.value = true;
|
|
||||||
if (!fetchNewsOrgInfo.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (translatedBefore.value === true) {
|
|
||||||
displayTranslatedText.value = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
loadingTranslations.value = true;
|
|
||||||
startTranslating(fetchNewsOrgInfo.value?.title);
|
|
||||||
startTranslating(fetchNewsOrgInfo.value?.description);
|
|
||||||
for (const articles of fetchNewsOrgInfo.value?.articles) {
|
|
||||||
startTranslating(articles.title.replaceAll("獨家專欄》", ""));
|
|
||||||
startTranslating(articles.date);
|
|
||||||
}
|
|
||||||
// NOT retranslating AGAIN when disabling the feat
|
|
||||||
translatedBefore.value = true;
|
|
||||||
setTimeout(() => {
|
|
||||||
displayTranslatedText.value = true;
|
|
||||||
loadingTranslations.value = false;
|
|
||||||
}, 3000);
|
|
||||||
} else {
|
|
||||||
translateText.value = false;
|
|
||||||
displayTranslatedText.value = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
); // Translate when requested?
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div v-if="loadingTranslations">Loading...</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="text-center align-center justify-center">
|
<div class="text-center align-center justify-center">
|
||||||
<div
|
<div
|
||||||
@ -137,11 +76,7 @@ watch(
|
|||||||
class="text-4xl font-bold m-2 text-center"
|
class="text-4xl font-bold m-2 text-center"
|
||||||
ref="orgNameAnimation"
|
ref="orgNameAnimation"
|
||||||
>
|
>
|
||||||
{{
|
{{ fetchNewsOrgInfo?.title }}
|
||||||
displayTranslatedText
|
|
||||||
? translateItem[fetchNewsOrgInfo?.title].translateText
|
|
||||||
: fetchNewsOrgInfo?.title
|
|
||||||
}}
|
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div v-if="pending" class="flex flex-col gap-2 m-1 mt-5">
|
<div v-if="pending" class="flex flex-col gap-2 m-1 mt-5">
|
||||||
@ -150,11 +85,7 @@ watch(
|
|||||||
<div class="h-4 bg-gray-200 animate-pulse rounded w-4/6"></div>
|
<div class="h-4 bg-gray-200 animate-pulse rounded w-4/6"></div>
|
||||||
</div>
|
</div>
|
||||||
<span v-else class="text-ms m-1 mt-5 text-left text-wrap">
|
<span v-else class="text-ms m-1 mt-5 text-left text-wrap">
|
||||||
{{
|
{{ fetchNewsOrgInfo?.description }}
|
||||||
displayTranslatedText
|
|
||||||
? translateItem[fetchNewsOrgInfo?.description].translateText
|
|
||||||
: fetchNewsOrgInfo?.description
|
|
||||||
}}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -178,16 +109,9 @@ watch(
|
|||||||
<div>
|
<div>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<span class="title text-bold texxt-sm">{{
|
<span class="title text-bold texxt-sm">{{
|
||||||
displayTranslatedText
|
item.title.replaceAll("獨家專欄》", "")
|
||||||
? translateItem[item.title.replaceAll("獨家專欄》", "")]
|
|
||||||
.translateText
|
|
||||||
: item.title.replaceAll("獨家專欄》", "")
|
|
||||||
}}</span>
|
|
||||||
<span class="date text-xs">{{
|
|
||||||
displayTranslatedText
|
|
||||||
? translateItem[item.date].translateText
|
|
||||||
: item.date
|
|
||||||
}}</span>
|
}}</span>
|
||||||
|
<span class="date text-xs">{{ item.date }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
@ -216,12 +216,6 @@ const openPublisher = (slug: string, title: string) => {
|
|||||||
};
|
};
|
||||||
const isLoading = computed(() => contentArray.value.length === 0);
|
const isLoading = computed(() => contentArray.value.length === 0);
|
||||||
const testmessage = await translate("嗨", { from: "zh", to: "en" });
|
const testmessage = await translate("嗨", { from: "zh", to: "en" });
|
||||||
const shouldHideItem = (item) => {
|
|
||||||
return (
|
|
||||||
item.contentType !== "GENERAL" ||
|
|
||||||
item.publisher?.toLowerCase().includes("line")
|
|
||||||
);
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="justify-center align-center text-center">
|
<div class="justify-center align-center text-center">
|
||||||
@ -263,7 +257,7 @@ const shouldHideItem = (item) => {
|
|||||||
<div>
|
<div>
|
||||||
<!-- Loading State -->
|
<!-- Loading State -->
|
||||||
<template v-if="isLoading">
|
<template v-if="isLoading">
|
||||||
<div v-for="n in 7" :key="n" class="p-2 bg-gray-200 rounded m-1">
|
<div v-for="n in 5" :key="n" class="p-2 bg-gray-200 rounded m-1">
|
||||||
<!-- Title Skeleton -->
|
<!-- Title Skeleton -->
|
||||||
<div
|
<div
|
||||||
class="h-8 bg-gray-300 animate-pulse rounded-lg w-3/4 mx-auto mb-2"
|
class="h-8 bg-gray-300 animate-pulse rounded-lg w-3/4 mx-auto mb-2"
|
||||||
@ -309,9 +303,9 @@ const shouldHideItem = (item) => {
|
|||||||
<div
|
<div
|
||||||
v-for="item in contentArray"
|
v-for="item in contentArray"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:class="shouldHideItem(item) && 'hidden'"
|
:class="item.contentType !== 'GENERAL' && 'hidden'"
|
||||||
>
|
>
|
||||||
<div class="p-2 bg-gray-200 rounded m-1">
|
<div class="p-2 bg-gray-200 rounded m-1 p-1">
|
||||||
<h1
|
<h1
|
||||||
class="text-2xl text-bold"
|
class="text-2xl text-bold"
|
||||||
:class="getCheckResult(item.title) ? 'text-red-600' : ''"
|
:class="getCheckResult(item.title) ? 'text-red-600' : ''"
|
||||||
|
@ -29,9 +29,6 @@ const likeart = ref([]);
|
|||||||
// Translating logic
|
// Translating logic
|
||||||
const translateText = ref(false);
|
const translateText = ref(false);
|
||||||
const translatedBefore = ref(false);
|
const translatedBefore = ref(false);
|
||||||
const traslateFailed = ref(false);
|
|
||||||
const displayTranslatedText = ref(false);
|
|
||||||
const loadingTranslations = ref(false);
|
|
||||||
watch(
|
watch(
|
||||||
() => props.applyForTranslation,
|
() => props.applyForTranslation,
|
||||||
(value) => {
|
(value) => {
|
||||||
@ -40,49 +37,32 @@ watch(
|
|||||||
if (!data.value) {
|
if (!data.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (translatedBefore.value === true) {
|
|
||||||
displayTranslatedText.value = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
loadingTranslations.value = true;
|
|
||||||
startTranslating(data.value.title);
|
startTranslating(data.value.title);
|
||||||
startTranslating(data.value.origin);
|
startTranslating(data.value.origin);
|
||||||
startTranslating(data.value.author);
|
startTranslating(data.value.author);
|
||||||
for (const paragraph of data.value.paragraph) {
|
data.value.paragraph.forEach((i, element) => {
|
||||||
startTranslating(paragraph);
|
console.log(element);
|
||||||
}
|
//startTranslating(data.value.)
|
||||||
// NOT retranslating AGAIN when disabling the feat
|
});
|
||||||
|
// NOT retranslating AGAIN
|
||||||
translatedBefore.value = true;
|
translatedBefore.value = true;
|
||||||
setTimeout(() => {
|
|
||||||
displayTranslatedText.value = true;
|
|
||||||
loadingTranslations.value = false;
|
|
||||||
}, 3000);
|
|
||||||
} else {
|
} else {
|
||||||
translateText.value = false;
|
translateText.value = false;
|
||||||
displayTranslatedText.value = false;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
); // Translate when requested?
|
); // Translate when requested?
|
||||||
|
|
||||||
const startTranslating = async (text: string) => {
|
const startTranslating = async (text: string) => {
|
||||||
try {
|
|
||||||
console.log(text);
|
|
||||||
translateItem[text] = {
|
translateItem[text] = {
|
||||||
translateText: await translate(text, { from: "zh", to: "en" }),
|
translateText: await translate(text, { from: "zh", to: "en" }),
|
||||||
};
|
};
|
||||||
console.log(translateItem[text]);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Translation failed:", error);
|
|
||||||
traslateFailed.value = true;
|
|
||||||
translateItem[text] = { translateText: text }; // fallback to original text
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const aiSummary = async () => {
|
const aiSummary = async () => {
|
||||||
activateAiSummary.value = true;
|
activateAiSummary.value = true;
|
||||||
isGenerating.value = true;
|
isGenerating.value = true;
|
||||||
try {
|
try {
|
||||||
const req = await fetch(`/api/ai/summarize/${slug}?lang=${String(locale)}`);
|
const req = await fetch(`/api/ai/summarize/${slug}?lang=${locale}`);
|
||||||
const reader = req.body?.getReader();
|
const reader = req.body?.getReader();
|
||||||
const decoder = new TextDecoder();
|
const decoder = new TextDecoder();
|
||||||
while (reader) {
|
while (reader) {
|
||||||
@ -100,52 +80,28 @@ const aiSummary = async () => {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div
|
|
||||||
class="flex flex-col bg-gray-200/50 text-black w-full h-full absolute inset-0 justify-center align-middle text-center z-[20] backdrop-blur-sm"
|
|
||||||
v-if="traslateFailed"
|
|
||||||
>
|
|
||||||
<div class="m-2 flex flex-col">
|
|
||||||
<span
|
|
||||||
>Translate Failed. <br />
|
|
||||||
Oops, your translation failed.</span
|
|
||||||
>
|
|
||||||
<button></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!--TODO: Get a better animation later.-->
|
|
||||||
<div v-if="loadingTranslations">Loading...</div>
|
|
||||||
<div
|
<div
|
||||||
class="justify-center align-center text-center flex flex-col md:flex-row flex-wrap"
|
class="justify-center align-center text-center flex flex-col md:flex-row flex-wrap"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div class="group">
|
<div class="group">
|
||||||
<h2 class="text-3xl text-bold">
|
<h2 class="text-3xl text-bold">
|
||||||
{{
|
{{ translateText ? translateItem[data.title] : data.title }}
|
||||||
displayTranslatedText
|
|
||||||
? translateItem[data.title].translateText
|
|
||||||
: data.title
|
|
||||||
}}
|
|
||||||
</h2>
|
</h2>
|
||||||
<span
|
<span
|
||||||
class="text-lg text-bold flex flex-row justify-center text-center align-center"
|
class="text-lg text-bold flex flex-row justify-center text-center align-center"
|
||||||
><NewspaperIcon class="w-7 h-7 p-1" />{{
|
><NewspaperIcon class="w-7 h-7 p-1" />{{
|
||||||
displayTranslatedText
|
translateText ? translateItem[data.origin] : data.origin
|
||||||
? translateItem[data.origin].translateText
|
|
||||||
: data.origin
|
|
||||||
}}
|
}}
|
||||||
• <UserIcon class="w-7 h-7 p-1" />{{
|
• <UserIcon class="w-7 h-7 p-1" />{{
|
||||||
displayTranslatedText
|
translateText ? translateItem[data.author] : data.author
|
||||||
? translateItem[data.author].translateText
|
|
||||||
: data.author
|
|
||||||
}}</span
|
}}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-4 w-full h-fit pt-0 mt-0">
|
<div class="p-4 w-full h-fit pt-0 mt-0">
|
||||||
<img v-if="data.images[0]" :src="data.images[0]" class="rounded" />
|
<img v-if="data.images[0]" :src="data.images[0]" class="rounded" />
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center" v-for="item in data.paragraph">
|
<div class="text-center" v-for="item in data.paragraph">{{ item }}</div>
|
||||||
{{ displayTranslatedText ? translateItem[item]?.translateText : item }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col w-full justify-center align-center text-center">
|
<div class="flex flex-col w-full justify-center align-center text-center">
|
||||||
<div
|
<div
|
||||||
|
@ -32,29 +32,9 @@ CREATE TABLE IF NOT EXISTS chat_history (
|
|||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
)`;
|
)`;
|
||||||
|
|
||||||
const createUserOtherData = await sql`
|
const createSources = await sql``;
|
||||||
create table if not exists user_other_data (
|
|
||||||
user_id text primary key,
|
|
||||||
username text not null unique,
|
|
||||||
groq_api_key text,
|
|
||||||
starred_news JSON not null,
|
|
||||||
translate_provider text,
|
|
||||||
translate_enabled boolean not null,
|
|
||||||
remove_translate_popup boolean not null
|
|
||||||
)`;
|
|
||||||
|
|
||||||
const createSources = await sql`
|
|
||||||
create table if not exists lt_news_org (
|
|
||||||
news_id text primary key,
|
|
||||||
name text not null,
|
|
||||||
description text
|
|
||||||
)
|
|
||||||
`;
|
|
||||||
|
|
||||||
console.log("Creation Complete");
|
console.log("Creation Complete");
|
||||||
console.log(
|
|
||||||
"If the script still does not quit after 2 seconds after the 'Creation Complete' message, please stop it by using Ctrl + C or on mac Control + C",
|
|
||||||
);
|
|
||||||
|
|
||||||
await sql.end();
|
await sql.end();
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
@ -24,7 +24,6 @@ interface associAppWindowInterface {
|
|||||||
height: string;
|
height: string;
|
||||||
black: boolean;
|
black: boolean;
|
||||||
translatable: boolean;
|
translatable: boolean;
|
||||||
translateState: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface minAppWindowInterface {
|
interface minAppWindowInterface {
|
||||||
@ -513,12 +512,9 @@ const openNewsSourcePage = async (slug: string, title: string) => {
|
|||||||
passedValues.value = null;
|
passedValues.value = null;
|
||||||
}, 1000);
|
}, 1000);
|
||||||
};
|
};
|
||||||
const toggleTranslate = (windowId: string) => {
|
const toggleTranslate = (id: string) => {
|
||||||
const windowIndex = activeWindows.value.findIndex((w) => w.id === windowId);
|
console.log("windowId", id);
|
||||||
if (windowIndex !== -1) {
|
applyForTranslation.value = true;
|
||||||
activeWindows.value[windowIndex].translateState =
|
|
||||||
!activeWindows.value[windowIndex].translateState;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const translateAvailable = () => {};
|
const translateAvailable = () => {};
|
||||||
@ -695,7 +691,7 @@ onMounted(async () => {
|
|||||||
:values="passedValues"
|
:values="passedValues"
|
||||||
:windows="activeWindows"
|
:windows="activeWindows"
|
||||||
@closeWindow="closeWindow"
|
@closeWindow="closeWindow"
|
||||||
:applyForTranslation="window.translateState"
|
:applyForTranslation="applyForTranslation"
|
||||||
:windowTranslateState="window.translatable"
|
:windowTranslateState="window.translatable"
|
||||||
:notLoggedInState="notLoggedInState"
|
:notLoggedInState="notLoggedInState"
|
||||||
/>
|
/>
|
||||||
|
30
server/api/ai/loadCustomGroqApi.ts
Normal file
30
server/api/ai/loadCustomGroqApi.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import sql from "~/server/components/postgres";
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
if (event.method !== "POST") {
|
||||||
|
return {
|
||||||
|
error: "ERR_METHOD_NOT_ALLOWED",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const body = readBody(event);
|
||||||
|
if (!body.apiKey) {
|
||||||
|
return {
|
||||||
|
error: "ERR_API_KEY_REQUIRED",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const readUserToken = getCookie(event, "token");
|
||||||
|
if (!readUserToken) {
|
||||||
|
return {
|
||||||
|
error: "ERR_NOT_USER_LOGIN",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const verifyUserToken = await sql`
|
||||||
|
SELECT * FROM usertokens
|
||||||
|
where token=${readUserToken}
|
||||||
|
`;
|
||||||
|
if (verifyUserToken.length === 0) {
|
||||||
|
return {
|
||||||
|
error: "ERR_NOT_USER_LOGIN",
|
||||||
|
requested_action: "LOGOUT_USER",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
@ -1,33 +1,19 @@
|
|||||||
import { Groq } from "groq-sdk";
|
import { Groq } from "groq-sdk";
|
||||||
import sql from "~/server/components/postgres";
|
import sql from "~/server/components/postgres";
|
||||||
import { checkIfUserHasCustomGroqKey } from "~/server/components/customgroqsystem";
|
|
||||||
|
|
||||||
const groq = new Groq({
|
const groq = new Groq();
|
||||||
apiKey: process.env.GROQ_API_KEY,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const host = getRequestHost(event);
|
const host = getRequestHost(event);
|
||||||
const protocol = getRequestProtocol(event);
|
const protocol = getRequestProtocol(event);
|
||||||
const slug = getRouterParam(event, "slug");
|
const slug = getRouterParam(event, "slug");
|
||||||
const userToken = getCookie(event, "token") || "";
|
|
||||||
console.log("Token: ", userToken);
|
|
||||||
const doesTheUserHasACustomGroqApiAndWhatIsIt =
|
|
||||||
await checkIfUserHasCustomGroqKey(userToken);
|
|
||||||
let groqClient = groq;
|
|
||||||
if (doesTheUserHasACustomGroqApiAndWhatIsIt.status === true) {
|
|
||||||
groqClient = new Groq({
|
|
||||||
apiKey: doesTheUserHasACustomGroqApiAndWhatIsIt.customApi,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const query = getQuery(event);
|
const query = getQuery(event);
|
||||||
const locale = query.locale;
|
const locale = query.locale;
|
||||||
const buildURL = protocol + "://" + host + "/api/news/get/lt/" + slug;
|
const buildURL = protocol + "://" + host + "/api/news/get/lt/" + slug;
|
||||||
const data = await fetch(buildURL);
|
const data = await fetch(buildURL);
|
||||||
const fetchNewsArticle = await data.json();
|
const fetchNewsArticle = await data.json();
|
||||||
console.log(locale);
|
console.log(locale);
|
||||||
const chatCompletion = await groqClient.chat.completions.create({
|
const chatCompletion = await groq.chat.completions.create({
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
role: "user",
|
role: "user",
|
||||||
|
@ -31,31 +31,11 @@ export default defineEventHandler(async (event) => {
|
|||||||
content TEXT NOT NULL,
|
content TEXT NOT NULL,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
)`;
|
)`;
|
||||||
|
const createSources = await sql``;
|
||||||
const createUserOtherData = await sql`
|
|
||||||
create table if not exists user_other_data (
|
|
||||||
user_id text primary key ,
|
|
||||||
user text not null unique,
|
|
||||||
groq_api_key text,
|
|
||||||
starred_news JSON not null,
|
|
||||||
translate_provider text,
|
|
||||||
translate_enabled boolean not null,
|
|
||||||
remove_translate_popup boolean not null
|
|
||||||
)`;
|
|
||||||
|
|
||||||
const createSources = await sql`
|
|
||||||
create table if not exists lt_news_org (
|
|
||||||
news_id text primary key,
|
|
||||||
name text not null,
|
|
||||||
description text
|
|
||||||
)
|
|
||||||
`;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createUsers: createUsers,
|
createUsers: createUsers,
|
||||||
usersList: usersList,
|
usersList: usersList,
|
||||||
createUserAiChatHistory: createUserAiChatHistory,
|
createUserAiChatHistory: createUserAiChatHistory,
|
||||||
createSources: createSources,
|
createSources: createSources,
|
||||||
createUserOtherData: createUserOtherData,
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -1 +0,0 @@
|
|||||||
export default defineEventHandler(async (event) => {});
|
|
@ -34,10 +34,9 @@ export default defineEventHandler(async (event) => {
|
|||||||
console.log(fetchUserInfo[0]);
|
console.log(fetchUserInfo[0]);
|
||||||
if (fetchUserInfo.length === 0) {
|
if (fetchUserInfo.length === 0) {
|
||||||
const hashedPassword = await argon2.hash(salt + password);
|
const hashedPassword = await argon2.hash(salt + password);
|
||||||
const userUUID = uuidv4();
|
|
||||||
const createNewUser = await sql`
|
const createNewUser = await sql`
|
||||||
insert into users (uuid, username, passwordhash, avatarurl)
|
insert into users (uuid, username, passwordhash, avatarurl)
|
||||||
values (${userUUID}, ${username}, ${hashedPassword}, ${defaultAvatarUrl})
|
values (${uuidv4()}, ${username}, ${hashedPassword}, ${defaultAvatarUrl})
|
||||||
`;
|
`;
|
||||||
console.log(createNewUser);
|
console.log(createNewUser);
|
||||||
if (fetchUserInfo.length !== 0) {
|
if (fetchUserInfo.length !== 0) {
|
||||||
@ -45,19 +44,10 @@ export default defineEventHandler(async (event) => {
|
|||||||
error: "CANNOT_CREATE_NEW_USER",
|
error: "CANNOT_CREATE_NEW_USER",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const createOtherFields = await sql`
|
|
||||||
insert into user_other_data(user_id, username, translate_enabled, translate_provider, remove_translate_popup, starred_news)
|
|
||||||
values (${userUUID}, ${username}, false, 'google', false, '{}'::JSON)
|
|
||||||
`;
|
|
||||||
const newToken = uuidv4();
|
const newToken = uuidv4();
|
||||||
await sql`
|
|
||||||
INSERT INTO usertokens (username, token)
|
|
||||||
VALUES (${username}, ${newToken})
|
|
||||||
`;
|
|
||||||
|
|
||||||
setCookie(event, "token", newToken);
|
|
||||||
return {
|
return {
|
||||||
user: fetchUserInfoAgain,
|
user: fetchUserInfo,
|
||||||
|
token: newToken,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const isValid = await argon2.verify(
|
const isValid = await argon2.verify(
|
||||||
@ -69,20 +59,19 @@ export default defineEventHandler(async (event) => {
|
|||||||
error: "PASSWORD_NO_MATCH",
|
error: "PASSWORD_NO_MATCH",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const newToken = uuidv4();
|
const newToken = uuidv4();
|
||||||
const fetchUserInfoAgain = await sql`
|
const fetchUserInfoAgain = await sql`
|
||||||
select * from users
|
select * from users
|
||||||
where username = ${username}`;
|
where username = ${username}`;
|
||||||
await sql`
|
await sql`
|
||||||
INSERT INTO usertokens (username, token)
|
INSERT INTO usertokens (user, token)
|
||||||
VALUES (${fetchUserInfoAgain[0].username}, ${newToken})
|
VALUES ('${fetchUserInfo[0].username}', '${newToken}')
|
||||||
`;
|
`;
|
||||||
|
|
||||||
setCookie(event, "token", newToken);
|
setCookie(event, "token", newToken);
|
||||||
return {
|
return {
|
||||||
user: fetchUserInfoAgain,
|
user: fetchUserInfoAgain,
|
||||||
};
|
};
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
return {
|
return {
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
import sql from "~/server/components/postgres";
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
const body = await readBody(event);
|
|
||||||
const { request_change } = body;
|
|
||||||
const userToken = getCookie(event, "token");
|
|
||||||
if (!userToken) {
|
|
||||||
return {
|
|
||||||
error: "ERR_NOT_ALLOWED",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const checkUserToken = await sql`
|
|
||||||
select * from usertokens
|
|
||||||
where token=${userToken}
|
|
||||||
`;
|
|
||||||
if (checkUserToken.length === 0) {
|
|
||||||
return {
|
|
||||||
error: "ERR_NOT_ALLOWED",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (request_change === "groq_api_key") {
|
|
||||||
const updateListing = await sql``;
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,33 +0,0 @@
|
|||||||
import sql from "./postgres";
|
|
||||||
|
|
||||||
export async function checkIfUserHasCustomGroqKey(token?: string) {
|
|
||||||
if (!token) {
|
|
||||||
return {
|
|
||||||
status: false,
|
|
||||||
customApi: "",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const checkRealToken = await sql`
|
|
||||||
select * from usertokens
|
|
||||||
where token = ${token}
|
|
||||||
`;
|
|
||||||
if (checkRealToken.length === 0) {
|
|
||||||
return {
|
|
||||||
status: false,
|
|
||||||
customApi: "",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const fetchUserToken = await sql`
|
|
||||||
select groq_api_key from user_other_data
|
|
||||||
where username=${checkRealToken[0].username}`;
|
|
||||||
if (fetchUserToken.length === 0) {
|
|
||||||
return {
|
|
||||||
status: false,
|
|
||||||
customApi: "",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
status: true,
|
|
||||||
customApi: fetchUserToken[0].groq_api_key,
|
|
||||||
};
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user