From c1fb03e3977b556a04af48acb71317f58308ca7c Mon Sep 17 00:00:00 2001 From: journey-ad Date: Sat, 22 Jun 2024 12:17:47 +0800 Subject: [PATCH] :sparkles: Add Stats info to Index page --- app/api/stats/route.ts | 60 +++++++++++++++++++++++++++++++++++++++++ app/page.tsx | 4 +++ components/Stats.tsx | 57 +++++++++++++++++++++++++++++++++++++++ components/icons.tsx | 22 +++++++++++++++ config/site.ts | 2 +- i18n/locales/en.json | 11 +++++++- i18n/locales/zh-CN.json | 12 ++++++--- i18n/locales/zh-TW.json | 8 ++++-- 8 files changed, 168 insertions(+), 8 deletions(-) create mode 100644 app/api/stats/route.ts create mode 100644 components/Stats.tsx diff --git a/app/api/stats/route.ts b/app/api/stats/route.ts new file mode 100644 index 0000000..80aa03c --- /dev/null +++ b/app/api/stats/route.ts @@ -0,0 +1,60 @@ +import { NextResponse } from "next/server"; +import { gql } from "@apollo/client"; + +import client from "@/lib/apolloClient"; + +// Define the GraphQL query to fetch torrent details by hash +const query = gql` + query StatsInfo { + statsInfo { + size + total_count + updated_at + latest_torrent_hash + latest_torrent { + hash + name + size + created_at + updated_at + } + } + } +`; + +// Function to handle GET requests +const handler = async () => { + try { + // Execute the GraphQL query with the provided hash variable + const { data } = await client.query({ query, fetchPolicy: "no-cache" }); + + await new Promise((resolve) => setTimeout(resolve, 5000)); + + // Return a 200 response with the query data + return NextResponse.json( + { + data: data.statsInfo, + message: "success", + status: 200, + }, + { + status: 200, + }, + ); + } catch (error: any) { + console.error(error); + + // Return a 500 response if there's an error during the query execution + return NextResponse.json( + { + message: error?.message || "Internal Server Error", + status: 500, + }, + { + status: 500, + }, + ); + } +}; + +export { handler as GET, handler as POST }; diff --git a/app/page.tsx b/app/page.tsx index b322296..416fbf2 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,12 +1,16 @@ import { HomeLogo } from "@/components/HomeLogo"; import { SearchInput } from "@/components/SearchInput"; import { ToggleTheme, SwitchLanguage } from "@/components/FloatTool"; +import { Stats } from "@/components/Stats"; export default function Home() { return (
+
+ +
diff --git a/components/Stats.tsx b/components/Stats.tsx new file mode 100644 index 0000000..55f89d2 --- /dev/null +++ b/components/Stats.tsx @@ -0,0 +1,57 @@ +import { Suspense } from "react"; +import { getTranslations } from "next-intl/server"; +import { Tooltip } from "@nextui-org/react"; + +import apiFetch from "@/utils/api"; +import { InfoFilledIcon } from "@/components/icons"; +import { formatByteSize, formatDate } from "@/utils"; + +async function StatsCard() { + const t = await getTranslations(); + + const { data } = await apiFetch("/api/stats", { + next: { revalidate: 60 }, + }); + + return ( +
+

{t("Stats.title")}

+
    +
  • {t("Stats.size", { size: formatByteSize(data.size) })}
  • +
  • + {t("Stats.total_count", { + total_count: data.total_count.toLocaleString(), + })} +
  • +
  • + {t("Stats.updated_at", { + updated_at: formatDate( + data.updated_at, + t("COMMON.DATE_FORMAT_DATE"), + ), + })} +
  • +
+
+ ); +} + +export function Stats() { + return ( + Loading...
}> + + + } + delay={0} + radius="sm" + > + + + ); +} diff --git a/components/icons.tsx b/components/icons.tsx index f4aabbe..d38b330 100644 --- a/components/icons.tsx +++ b/components/icons.tsx @@ -236,6 +236,28 @@ export const LangFilledIcon: React.FC = ({ ); +export const InfoFilledIcon: React.FC = ({ + size = 24, + width, + height, + ...props +}: IconSvgProps) => ( + +); + export const FileTypeFolderIcon: React.FC = ( props: IconSvgProps, ) => ( diff --git a/config/site.ts b/config/site.ts index ee5900d..812e8a1 100644 --- a/config/site.ts +++ b/config/site.ts @@ -2,5 +2,5 @@ export type SiteConfig = typeof siteConfig; export const siteConfig = { name: "Bitmagnet Next Web", - description: "Another Bitmagnet frontend", + description: "🧲 A modern BitTorrent indexer, powered by Bitmagnet.", }; diff --git a/i18n/locales/en.json b/i18n/locales/en.json index 794a2b6..13de21e 100644 --- a/i18n/locales/en.json +++ b/i18n/locales/en.json @@ -1,6 +1,15 @@ { "COMMON": { - "DATE_FORMAT": "YYYY-MM-DD HH:mm:ss" + "DATE_FORMAT": "YYYY-MM-DD HH:mm:ss", + "DATE_FORMAT_SHORT": "YYYY-MM-DD HH:mm", + "DATE_FORMAT_DATE": "YYYY-MM-DD", + "DATE_FORMAT_TIME": "HH:mm:ss" + }, + "Stats": { + "title": "Stats", + "size": "Database: {size}", + "total_count": "Torrents: {total_count}", + "updated_at": "Last updated: {updated_at}" }, "ERROR_MESSAGE": { "INTERNAL_SERVER_ERROR": "Something went wrong!", diff --git a/i18n/locales/zh-CN.json b/i18n/locales/zh-CN.json index a233add..0f4e8a3 100644 --- a/i18n/locales/zh-CN.json +++ b/i18n/locales/zh-CN.json @@ -1,12 +1,16 @@ { - "COMMON": { - "DATE_FORMAT": "YYYY-MM-DD HH:mm:ss" + "COMMON": {}, + "Stats": { + "title": "统计信息", + "size": "数据库大小: {size}", + "total_count": "收录数量: {total_count}", + "updated_at": "最后更新日期: {updated_at}" }, "ERROR_MESSAGE": { "INTERNAL_SERVER_ERROR": "发生意外错误", "NOT_FOUND": "资源不存在", "GoHome": "返回首页" - }, + }, "Metadata": { "search": { "title": "{keyword} 的搜索结果" @@ -56,4 +60,4 @@ "keyword_too_short": "关键词需要大于两个字符", "copy_success": "磁力链接已复制到剪贴板" } -} +} \ No newline at end of file diff --git a/i18n/locales/zh-TW.json b/i18n/locales/zh-TW.json index ab9e3d1..52d54fc 100644 --- a/i18n/locales/zh-TW.json +++ b/i18n/locales/zh-TW.json @@ -1,6 +1,10 @@ { - "COMMON": { - "DATE_FORMAT": "YYYY-MM-DD HH:mm:ss" + "COMMON": {}, + "Stats": { + "title": "統計資訊", + "size": "資料庫大小: {size}", + "total_count": "收錄數量: {total_count}", + "updated_at": "最後更新日期: {updated_at}" }, "ERROR_MESSAGE": { "INTERNAL_SERVER_ERROR": "發生意外錯誤",