From d19976242734866df26101ffce23ffafe9805993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Gj=C3=B8by=20Thom?= <34199185+havardthom@users.noreply.github.com> Date: Sat, 9 Nov 2024 20:06:54 +0100 Subject: [PATCH] Use static assets instead of fetching from github (#156) --- frontend/public/json | 1 + frontend/src/app/api/categories/route.ts | 53 +++++++++++-------- frontend/src/app/layout.tsx | 5 +- frontend/src/app/page.tsx | 15 ++++-- .../app/scripts/_components/ScriptItem.tsx | 4 +- .../ScriptItems/DefaultPassword.tsx | 15 ++++-- .../ScriptItems/DefaultSettings.tsx | 3 +- .../ScriptItems/InstallCommand.tsx | 8 +-- .../_components/ScriptItems/InterFaces.tsx | 19 +++---- .../src/app/scripts/_components/Sidebar.tsx | 11 ++-- frontend/src/components/ui/codeblock.tsx | 4 +- .../src/components/ui/navigation-menu.tsx | 2 +- .../components/ui/star-on-github-button.tsx | 13 +++-- frontend/src/components/ui/theme-toggle.tsx | 9 +++- frontend/src/config/siteConfig.tsx | 4 +- frontend/src/lib/data.ts | 25 +++------ frontend/src/lib/types.ts | 18 ++++--- json/metadata.json | 32 +++++------ 18 files changed, 135 insertions(+), 106 deletions(-) create mode 120000 frontend/public/json diff --git a/frontend/public/json b/frontend/public/json new file mode 120000 index 000000000..cd8045684 --- /dev/null +++ b/frontend/public/json @@ -0,0 +1 @@ +../../json \ No newline at end of file diff --git a/frontend/src/app/api/categories/route.ts b/frontend/src/app/api/categories/route.ts index 52470a129..2d33a3ab0 100644 --- a/frontend/src/app/api/categories/route.ts +++ b/frontend/src/app/api/categories/route.ts @@ -1,26 +1,30 @@ -import { basePath } from "@/config/siteConfig"; -import { Category, Script } from "@/lib/types"; +import { Metadata, Script } from "@/lib/types"; +import { promises as fs } from "fs"; import { NextResponse } from "next/server"; +import path from "path"; export const dynamic = "force-static"; -const fetchCategories = async (): Promise => { - const response = await fetch( - `https://raw.githubusercontent.com/community-scripts/${basePath}/refs/heads/main/json/metadata.json`, - ); - const data = await response.json(); - return data.categories; +const jsonDir = "public/json"; +const metadataFileName = "metadata.json"; +const encoding = "utf-8"; + +const getMetadata = async () => { + const filePath = path.resolve(jsonDir, metadataFileName); + const fileContent = await fs.readFile(filePath, encoding); + const metadata: Metadata = JSON.parse(fileContent); + return metadata; }; -const fetchScripts = async (): Promise => { - const response = await fetch( - `https://api.github.com/repos/community-scripts/${basePath}/contents/json`, - ); - const files: { download_url: string }[] = await response.json(); +const getScripts = async () => { + const filePaths = (await fs.readdir(jsonDir)) + .filter((fileName) => fileName !== metadataFileName) + .map((fileName) => path.resolve(jsonDir, fileName)); + const scripts = await Promise.all( - files.map(async (file) : Promise - diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 67577c6c7..e692d2e18 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -1,17 +1,24 @@ "use client"; import AnimatedGradientText from "@/components/ui/animated-gradient-text"; -import Particles from "@/components/ui/particles"; import { Button } from "@/components/ui/button"; +import { CardFooter } from "@/components/ui/card"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import Particles from "@/components/ui/particles"; import { Separator } from "@/components/ui/separator"; +import { basePath } from "@/config/siteConfig"; import { cn } from "@/lib/utils"; import { ArrowRightIcon, ExternalLink } from "lucide-react"; import { useTheme } from "next-themes"; import Link from "next/link"; import { useEffect, useState } from "react"; -import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; -import { CardFooter } from "@/components/ui/card"; import { FaGithub } from "react-icons/fa"; -import { basePath } from "@/config/siteConfig"; function CustomArrowRightIcon() { return ; diff --git a/frontend/src/app/scripts/_components/ScriptItem.tsx b/frontend/src/app/scripts/_components/ScriptItem.tsx index 840246d9c..2d9848dbd 100644 --- a/frontend/src/app/scripts/_components/ScriptItem.tsx +++ b/frontend/src/app/scripts/_components/ScriptItem.tsx @@ -52,7 +52,9 @@ function ScriptItem({
-

{item.name} {getDisplayValueFromType(item.type)}

+

+ {item.name} {getDisplayValueFromType(item.type)} +

Date added: {extractDate(item.date_created)}

diff --git a/frontend/src/app/scripts/_components/ScriptItems/DefaultPassword.tsx b/frontend/src/app/scripts/_components/ScriptItems/DefaultPassword.tsx index 55193d8f9..4bd555a10 100644 --- a/frontend/src/app/scripts/_components/ScriptItems/DefaultPassword.tsx +++ b/frontend/src/app/scripts/_components/ScriptItems/DefaultPassword.tsx @@ -1,10 +1,11 @@ +import handleCopy from "@/components/handleCopy"; import { Button } from "@/components/ui/button"; import { Separator } from "@/components/ui/separator"; -import handleCopy from "@/components/handleCopy"; import { Script } from "@/lib/types"; export default function DefaultPassword({ item }: { item: Script }) { - const hasDefaultLogin = item.default_credentials.username && item.default_credentials.password; + const hasDefaultLogin = + item.default_credentials.username && item.default_credentials.password; return (
@@ -25,7 +26,10 @@ export default function DefaultPassword({ item }: { item: Script }) { variant={"secondary"} size={"null"} onClick={() => - handleCopy("username", item.default_credentials.username ?? "") + handleCopy( + "username", + item.default_credentials.username ?? "", + ) } > {item.default_credentials.username} @@ -37,7 +41,10 @@ export default function DefaultPassword({ item }: { item: Script }) { variant={"secondary"} size={"null"} onClick={() => - handleCopy("password", item.default_credentials.password ?? "") + handleCopy( + "password", + item.default_credentials.password ?? "", + ) } > {item.default_credentials.password} diff --git a/frontend/src/app/scripts/_components/ScriptItems/DefaultSettings.tsx b/frontend/src/app/scripts/_components/ScriptItems/DefaultSettings.tsx index f1b2d8bbd..1c7d89c78 100644 --- a/frontend/src/app/scripts/_components/ScriptItems/DefaultSettings.tsx +++ b/frontend/src/app/scripts/_components/ScriptItems/DefaultSettings.tsx @@ -44,7 +44,8 @@ export default function DefaultSettings({ item }: { item: Script }) { CPU: {defaultAlpineSettings?.resources.cpu}vCPU

- RAM: {getDisplayValueFromRAM(defaultAlpineSettings?.resources.ram ?? 0)} + RAM:{" "} + {getDisplayValueFromRAM(defaultAlpineSettings?.resources.ram ?? 0)}

HDD: {defaultAlpineSettings?.resources.hdd}GB diff --git a/frontend/src/app/scripts/_components/ScriptItems/InstallCommand.tsx b/frontend/src/app/scripts/_components/ScriptItems/InstallCommand.tsx index 90eeaff86..b5cba9e52 100644 --- a/frontend/src/app/scripts/_components/ScriptItems/InstallCommand.tsx +++ b/frontend/src/app/scripts/_components/ScriptItems/InstallCommand.tsx @@ -6,7 +6,7 @@ import { getDisplayValueFromType } from "../ScriptInfoBlocks"; const getInstallCommand = (scriptPath?: string) => { return `bash -c "$(wget -qLO - https://github.com/community-scripts/${basePath}/raw/main/${scriptPath})"`; -} +}; export default function InstallCommand({ item }: { item: Script }) { const alpineScript = item.install_methods.find( @@ -14,7 +14,7 @@ export default function InstallCommand({ item }: { item: Script }) { ); const defaultScript = item.install_methods.find( - (method) => method.type === "default" + (method) => method.type === "default", ); const renderInstructions = (isAlpine = false) => ( @@ -60,7 +60,9 @@ export default function InstallCommand({ item }: { item: Script }) { {renderInstructions()} - {getInstallCommand(defaultScript?.script)} + + {getInstallCommand(defaultScript?.script)} + {renderInstructions(true)} diff --git a/frontend/src/app/scripts/_components/ScriptItems/InterFaces.tsx b/frontend/src/app/scripts/_components/ScriptItems/InterFaces.tsx index 2d2ae1f11..99f855fad 100644 --- a/frontend/src/app/scripts/_components/ScriptItems/InterFaces.tsx +++ b/frontend/src/app/scripts/_components/ScriptItems/InterFaces.tsx @@ -1,8 +1,8 @@ -import { Button, buttonVariants } from "@/components/ui/button"; import handleCopy from "@/components/handleCopy"; +import { buttonVariants } from "@/components/ui/button"; +import { Script } from "@/lib/types"; import { cn } from "@/lib/utils"; import { ClipboardIcon } from "lucide-react"; -import { Script } from "@/lib/types"; const CopyButton = ({ label, @@ -11,7 +11,12 @@ const CopyButton = ({ label: string; value: string | number; }) => ( - + {value} handleCopy(label, String(value))} @@ -20,8 +25,7 @@ const CopyButton = ({ ); -export default function InterFaces({item} : {item : Script}) { - +export default function InterFaces({ item }: { item: Script }) { return (

{item.interface_port !== null ? ( @@ -29,10 +33,7 @@ export default function InterFaces({item} : {item : Script}) {

{"Default Interface:"}

{" "} - +
) : null}
diff --git a/frontend/src/app/scripts/_components/Sidebar.tsx b/frontend/src/app/scripts/_components/Sidebar.tsx index f1618e170..24f9dce20 100644 --- a/frontend/src/app/scripts/_components/Sidebar.tsx +++ b/frontend/src/app/scripts/_components/Sidebar.tsx @@ -17,15 +17,16 @@ const Sidebar = ({

Categories

- {items.reduce( - (acc, category) => acc + category.scripts.length, - 0, - )}{" "} + {items.reduce((acc, category) => acc + category.scripts.length, 0)}{" "} Total scripts

- +
); diff --git a/frontend/src/components/ui/codeblock.tsx b/frontend/src/components/ui/codeblock.tsx index 16cc1bcc2..cb8517c6c 100644 --- a/frontend/src/components/ui/codeblock.tsx +++ b/frontend/src/components/ui/codeblock.tsx @@ -1,5 +1,6 @@ "use client"; +import { basePath } from "@/config/siteConfig"; import { cn } from "@/lib/utils"; import { cva, type VariantProps } from "class-variance-authority"; import { Clipboard, Copy } from "lucide-react"; @@ -8,7 +9,6 @@ import * as React from "react"; import { toast } from "sonner"; import { Button } from "./button"; import { Separator } from "./separator"; -import { basePath } from "@/config/siteConfig"; const buttonVariants = cva( "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", @@ -135,4 +135,4 @@ const CodeBlock = React.forwardRef( ); CodeBlock.displayName = "CodeBlock"; -export { CodeBlock, buttonVariants }; +export { buttonVariants, CodeBlock }; diff --git a/frontend/src/components/ui/navigation-menu.tsx b/frontend/src/components/ui/navigation-menu.tsx index e823ebcfb..203466d69 100644 --- a/frontend/src/components/ui/navigation-menu.tsx +++ b/frontend/src/components/ui/navigation-menu.tsx @@ -123,6 +123,6 @@ export { NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, - NavigationMenuViewport, navigationMenuTriggerStyle, + NavigationMenuViewport, }; diff --git a/frontend/src/components/ui/star-on-github-button.tsx b/frontend/src/components/ui/star-on-github-button.tsx index f7e59b98f..fd8b4edcc 100644 --- a/frontend/src/components/ui/star-on-github-button.tsx +++ b/frontend/src/components/ui/star-on-github-button.tsx @@ -1,10 +1,10 @@ +import { basePath } from "@/config/siteConfig"; import { cn } from "@/lib/utils"; import Link from "next/link"; import { useEffect, useState } from "react"; import { FaGithub, FaStar } from "react-icons/fa"; -import NumberTicker from "./number-ticker"; import { buttonVariants } from "./button"; -import { basePath } from "@/config/siteConfig"; +import NumberTicker from "./number-ticker"; export default function StarOnGithubButton() { const [stars, setStars] = useState(0); @@ -12,9 +12,12 @@ export default function StarOnGithubButton() { useEffect(() => { const fetchStars = async () => { try { - const res = await fetch(`https://api.github.com/repos/community-scripts/${basePath}`, { - next: { revalidate: 60 * 60 * 24 }, - }); + const res = await fetch( + `https://api.github.com/repos/community-scripts/${basePath}`, + { + next: { revalidate: 60 * 60 * 24 }, + }, + ); if (res.ok) { const data = await res.json(); diff --git a/frontend/src/components/ui/theme-toggle.tsx b/frontend/src/components/ui/theme-toggle.tsx index e2ca4d277..6240554de 100644 --- a/frontend/src/components/ui/theme-toggle.tsx +++ b/frontend/src/components/ui/theme-toggle.tsx @@ -1,9 +1,14 @@ "use client"; +import { MoonIcon, SunIcon } from "@radix-ui/react-icons"; import { useTheme } from "next-themes"; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip"; import { Button } from "./button"; -import { MoonIcon, SunIcon } from "@radix-ui/react-icons"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "./tooltip"; export function ThemeToggle() { const { setTheme, theme: currentTheme } = useTheme(); diff --git a/frontend/src/config/siteConfig.tsx b/frontend/src/config/siteConfig.tsx index d32952c9a..301a08c6d 100644 --- a/frontend/src/config/siteConfig.tsx +++ b/frontend/src/config/siteConfig.tsx @@ -1,7 +1,7 @@ import { MessagesSquare, Scroll } from "lucide-react"; import { FaGithub } from "react-icons/fa"; -export const basePath = process.env.BASE_PATH; +export const basePath = process.env.BASE_PATH; export const navbarLinks = [ { @@ -17,7 +17,7 @@ export const navbarLinks = [ text: "Change Log", }, { - href: `https://github.com/community-scripts/${basePath}/discussions`, + href: `https://github.com/community-scripts/${basePath}/discussions`, event: "Discussions", icon: , text: "Discussions", diff --git a/frontend/src/lib/data.ts b/frontend/src/lib/data.ts index 8c665e1ab..99678031a 100644 --- a/frontend/src/lib/data.ts +++ b/frontend/src/lib/data.ts @@ -1,23 +1,10 @@ import { Category } from "./types"; -const sortCategories = (categories: Category[]) => { - return categories.sort((a, b) => { - if (a.name === "Proxmox VE Tools") { - return -1; - } else if (b.name === "Proxmox VE Tools") { - return 1; - } else if (a.name === "Miscellaneous") { - return 1; - } else if (b.name === "Miscellaneous") { - return -1; - } else { - return a.name.localeCompare(b.name); - } - }); -}; - -export const fetchCategories = async (): Promise => { +export const fetchCategories = async () => { const response = await fetch("api/categories"); - const categories = await response.json(); - return sortCategories(categories); + if (!response.ok) { + throw new Error(`Failed to fetch categories: ${response.statusText}`); + } + const categories: Category[] = await response.json(); + return categories; }; diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index 3ce26cd11..4f8d2ef23 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -26,19 +26,21 @@ export type Script = { username: string | null; password: string | null; }; - notes: [{ - text: string; - type: string; - }] -} + notes: [ + { + text: string; + type: string; + }, + ]; +}; export type Category = { name: string; id: number; sort_order: number; scripts: Script[]; -} +}; -export type ScriptList = { +export type Metadata = { categories: Category[]; -} \ No newline at end of file +}; diff --git a/json/metadata.json b/json/metadata.json index 53660dd8d..cc2431b86 100644 --- a/json/metadata.json +++ b/json/metadata.json @@ -1,23 +1,23 @@ { "categories": [ - {"name": "Miscellaneous", "id": 0, "sort_order": 99.0}, {"name": "Proxmox VE Tools", "id": 1, "sort_order": 1.0}, - {"name": "Home Assistant", "id": 2, "sort_order": 2.0}, + {"name": "AdBlocker - DNS", "id": 13, "sort_order": 2.0}, {"name": "Automation", "id": 3, "sort_order": 3.0}, - {"name": "MQTT", "id": 4, "sort_order": 4.0}, + {"name": "Dashboards", "id": 15, "sort_order": 4.0}, {"name": "Database", "id": 5, "sort_order": 5.0}, - {"name": "Zigbee - Zwave", "id": 6, "sort_order": 6.0}, - {"name": "Monitoring - Analytics", "id": 7, "sort_order": 7.0}, - {"name": "Docker - Kubernetes", "id": 8, "sort_order": 8.0}, - {"name": "Operating System", "id": 9, "sort_order": 9.0}, - {"name": "TurnKey", "id": 10, "sort_order": 10.0}, - {"name": "Server - Networking", "id": 11, "sort_order": 11.0}, - {"name": "Media - Photo", "id": 12, "sort_order": 12.0}, - {"name": "AdBlocker - DNS", "id": 13, "sort_order": 13.0}, - {"name": "Document - Notes", "id": 14, "sort_order": 14.0}, - {"name": "Dashboards", "id": 15, "sort_order": 15.0}, - {"name": "File - Code", "id": 16, "sort_order": 16.0}, - {"name": "NVR - DVR", "id": 17, "sort_order": 17.0} + {"name": "Docker - Kubernetes", "id": 8, "sort_order": 6.0}, + {"name": "Document - Notes", "id": 14, "sort_order": 7.0}, + {"name": "File - Code", "id": 16, "sort_order": 8.0}, + {"name": "Home Assistant", "id": 2, "sort_order": 9.0}, + {"name": "Media - Photo", "id": 12, "sort_order": 10.0}, + {"name": "Monitoring - Analytics", "id": 7, "sort_order": 11.0}, + {"name": "MQTT", "id": 4, "sort_order": 12.0}, + {"name": "NVR - DVR", "id": 17, "sort_order": 13.0}, + {"name": "Operating System", "id": 9, "sort_order": 14.0}, + {"name": "Server - Networking", "id": 11, "sort_order": 15.0}, + {"name": "TurnKey", "id": 10, "sort_order": 16.0}, + {"name": "Zigbee - Zwave", "id": 6, "sort_order": 17.0}, + {"name": "Miscellaneous", "id": 0, "sort_order": 99.0} ] -} \ No newline at end of file +}