A publisher api. & modded the news.vue file to use the publisherId instead of the name. & cleaned the code using prettier.

This commit is contained in:
yuanhau 2025-06-02 00:47:23 +08:00
parent 1dd324e0ca
commit 5392974261
6 changed files with 99 additions and 36 deletions

View file

@ -241,7 +241,7 @@ const openPublisher = (text: string) => {
<TooltipProvider> <TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger> <TooltipTrigger>
<button @click="openPublisher(item.publisher)"> <button @click="openPublisher(item.publisherId)">
{{ item.publisher }} {{ item.publisher }}
</button> </button>
</TooltipTrigger> </TooltipTrigger>

View file

@ -69,9 +69,7 @@ const aiSummary = async () => {
/>Activate />Activate
</button> </button>
<div v-else> <div v-else>
<div v-if="!summaryText"> <div v-if="!summaryText">Loading...</div>
Loading...
</div>
<div v-else>{{ summaryText }}</div> <div v-else>{{ summaryText }}</div>
</div> </div>
</div> </div>

View file

@ -1,5 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import { ArrowBigRightDashIcon, BadgeCheckIcon, BadgeXIcon, PanelTopIcon, RssIcon, NewspaperIcon, BotMessageSquareIcon, CombineIcon } from "lucide-vue-next"; import {
ArrowBigRightDashIcon,
BadgeCheckIcon,
BadgeXIcon,
PanelTopIcon,
RssIcon,
NewspaperIcon,
BotMessageSquareIcon,
CombineIcon,
} from "lucide-vue-next";
const apis = [ const apis = [
{ {
icon: ArrowBigRightDashIcon, icon: ArrowBigRightDashIcon,
@ -19,7 +28,7 @@ const apis = [
openAccess: true, openAccess: true,
}, },
{ {
icon:RssIcon, icon: RssIcon,
apiroute: "/api/home/lt", apiroute: "/api/home/lt",
name: "Get the news feed of LT", name: "Get the news feed of LT",
content: content:
@ -40,7 +49,8 @@ const apis = [
icon: BotMessageSquareIcon, icon: BotMessageSquareIcon,
apiroute: "/api/ai/chat/[slug]", apiroute: "/api/ai/chat/[slug]",
name: "A Chating API", name: "A Chating API",
content: "This is for the desktop app & talk about news articles. Using Groq's free tier.", content:
"This is for the desktop app & talk about news articles. Using Groq's free tier.",
caching: false, caching: false,
openAccess: false, openAccess: false,
}, },
@ -56,14 +66,15 @@ const apis = [
</script> </script>
<template> <template>
<div class="h-4"></div> <div class="h-4"></div>
<div <div class="justify-center align-center text-center flex flex-col">
class="justify-center align-center text-center flex flex-col"
>
<h1 class="text-4xl text-bold m-2">APIs</h1> <h1 class="text-4xl text-bold m-2">APIs</h1>
<div class="items flex flex-row flex-wrap gap-2 justify-center align-center"> <div
class="items flex flex-row flex-wrap gap-2 justify-center align-center"
>
<div <div
class="px-10 bg-gray-900/70 w-[400px] h-[300px] group rounded-xl hover:-translate-y-1 shadow-lg hover:shadow-sky-600/90 backdrop-blur-sm border border-gray-800 hover:border-sky-600/70 transition-all duration-700 justify-center align-middle flex flex-col text-left" class="px-10 bg-gray-900/70 w-[400px] h-[300px] group rounded-xl hover:-translate-y-1 shadow-lg hover:shadow-sky-600/90 backdrop-blur-sm border border-gray-800 hover:border-sky-600/70 transition-all duration-700 justify-center align-middle flex flex-col text-left"
v-for="item in apis"> v-for="item in apis"
>
<component <component
:is="item.icon" :is="item.icon"
class="w-8 h-8 text-white group-hover:text-sky-500 transition-colors duration-300" class="w-8 h-8 text-white group-hover:text-sky-500 transition-colors duration-300"
@ -72,13 +83,27 @@ const apis = [
<h2>API: {{ item.apiroute }}</h2> <h2>API: {{ item.apiroute }}</h2>
<p class="text-sm">{{ item.content || "N/A" }}</p> <p class="text-sm">{{ item.content || "N/A" }}</p>
<div class="gap-0 m-1"> <div class="gap-0 m-1">
<div class="text-md flex flex-row gap-2 text-center p-2">Caching: <div class="text-md flex flex-row gap-2 text-center p-2">
<BadgeCheckIcon v-if="item.caching" class="w-7 h-7 p-1 group-hover:text-green-300 transition-all duration-200"/> Caching:
<BadgeXIcon v-else class="w-7 h-7 p-1 group-hover:text-red-400 transition-all duration-200"/> <BadgeCheckIcon
v-if="item.caching"
class="w-7 h-7 p-1 group-hover:text-green-300 transition-all duration-200"
/>
<BadgeXIcon
v-else
class="w-7 h-7 p-1 group-hover:text-red-400 transition-all duration-200"
/>
</div> </div>
<div class="text-md flex flex-row gap-2 text-center p-2">Open Access: <div class="text-md flex flex-row gap-2 text-center p-2">
<BadgeCheckIcon v-if="item.caching" class="w-7 h-7 p-1 group-hover:text-green-300 transition-all duration-200"/> Open Access:
<BadgeXIcon v-else class="w-7 h-7 p-1 group-hover:text-red-400 transition-all duration-200"/> <BadgeCheckIcon
v-if="item.caching"
class="w-7 h-7 p-1 group-hover:text-green-300 transition-all duration-200"
/>
<BadgeXIcon
v-else
class="w-7 h-7 p-1 group-hover:text-red-400 transition-all duration-200"
/>
</div> </div>
</div> </div>
</div> </div>

View file

@ -4,6 +4,45 @@ export default defineEventHandler(async (event) => {
const slug = getRouterParam(event, "slug"); const slug = getRouterParam(event, "slug");
const buildUrl = "https://today.line.me/tw/v3/publisher/" + slug; const buildUrl = "https://today.line.me/tw/v3/publisher/" + slug;
try { try {
const req = await fetch(buildUrl) const req = await fetch(buildUrl, {
} catch (e) {} headers: {
}) "User-Agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
Accept: "*",
"Accept-Language": "zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7",
"Accept-Encoding": "gzip, deflate, br",
Connection: "keep-alive",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "same-origin",
"Cache-Control": "max-age=0",
},
});
const data = await req.text();
const html = cheerio.load(data);
const newsOrgName = html("div.profileHead")
.text()
.replace(/.css-.*\}/, "");
const description = html("p.description").text();
const logo =
html("div.editor div figure img").attr("srcset") ||
html("div.editor div figure img").attr("src") ||
"";
const articles = [];
const otherArticles = html("section.moduleContainer div");
for (const item in otherArticles) {
}
return {
name: newsOrgName,
description: description,
logo: logo,
articles: []
};
} catch (e) {
console.log(e);
return {
error: "SERVER_SIDE_ERROR",
};
}
});

View file

@ -90,10 +90,11 @@ async function lineToday(slug: string) {
publishedAt = findTime(publishMatch[1].trim()); publishedAt = findTime(publishMatch[1].trim());
} }
const findPublisherUrl = html("a.entityPublishInfo-avatarLink").attr("href") || ""; const findPublisherUrl =
const publisherIdMatch = findPublisherUrl.match(/[0-9]{6}/); html("a.entityPublishInfo-avatarLink").attr("href") || "";
const publisherId = publisherIdMatch ? publisherIdMatch[0] : ""; const publisherIdMatch = findPublisherUrl.match(/[0-9]{6}/);
console.log(publisherId); const publisherId = publisherIdMatch ? publisherIdMatch[0] : "";
console.log(publisherId);
return { return {
title: title, title: title,
@ -103,7 +104,7 @@ console.log(publisherId);
images: images, images: images,
updateat: updatedAt, updateat: updatedAt,
publishedat: publishedAt, publishedat: publishedAt,
publisherId: publisherId publisherId: publisherId,
}; };
} }