core: move misc scripts to structured addon/pve paths | Refactor JSON Editor & Script Mapping (#3765)
* Move Scripts to Tools / Add-Ons * fix json editor slug generating * update type in jsons * remove wrong method * move copy-data to tools
This commit is contained in:
@@ -1,12 +1,5 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { basePath, mostPopularScripts } from "@/config/siteConfig";
|
||||
import { extractDate } from "@/lib/time";
|
||||
import { Category, Script } from "@/lib/types";
|
||||
@@ -23,7 +16,8 @@ export const getDisplayValueFromType = (type: string) => {
|
||||
return "LXC";
|
||||
case "vm":
|
||||
return "VM";
|
||||
case "misc":
|
||||
case "pve":
|
||||
case "addon":
|
||||
return "";
|
||||
default:
|
||||
return "";
|
||||
@@ -35,7 +29,7 @@ export function LatestScripts({ items }: { items: Category[] }) {
|
||||
|
||||
const latestScripts = useMemo(() => {
|
||||
if (!items) return [];
|
||||
|
||||
|
||||
const scripts = items.flatMap((category) => category.scripts || []);
|
||||
|
||||
// Filter out duplicates by slug
|
||||
@@ -47,8 +41,7 @@ export function LatestScripts({ items }: { items: Category[] }) {
|
||||
});
|
||||
|
||||
return Array.from(uniqueScriptsMap.values()).sort(
|
||||
(a, b) =>
|
||||
new Date(b.date_created).getTime() - new Date(a.date_created).getTime(),
|
||||
(a, b) => new Date(b.date_created).getTime() - new Date(a.date_created).getTime(),
|
||||
);
|
||||
}, [items]);
|
||||
|
||||
@@ -59,7 +52,7 @@ export function LatestScripts({ items }: { items: Category[] }) {
|
||||
const goToPreviousPage = () => {
|
||||
setPage((prevPage) => prevPage - 1);
|
||||
};
|
||||
|
||||
|
||||
const startIndex = (page - 1) * ITEMS_PER_PAGE;
|
||||
const endIndex = page * ITEMS_PER_PAGE;
|
||||
|
||||
@@ -74,18 +67,12 @@ export function LatestScripts({ items }: { items: Category[] }) {
|
||||
<h2 className="text-lg font-semibold">Newest Scripts</h2>
|
||||
<div className="flex items-center justify-end gap-1">
|
||||
{page > 1 && (
|
||||
<div
|
||||
className="cursor-pointer select-none p-2 text-sm font-semibold"
|
||||
onClick={goToPreviousPage}
|
||||
>
|
||||
<div className="cursor-pointer select-none p-2 text-sm font-semibold" onClick={goToPreviousPage}>
|
||||
Previous
|
||||
</div>
|
||||
)}
|
||||
{endIndex < latestScripts.length && (
|
||||
<div
|
||||
onClick={goToNextPage}
|
||||
className="cursor-pointer select-none p-2 text-sm font-semibold"
|
||||
>
|
||||
<div onClick={goToNextPage} className="cursor-pointer select-none p-2 text-sm font-semibold">
|
||||
{page === 1 ? "More.." : "Next"}
|
||||
</div>
|
||||
)}
|
||||
@@ -94,10 +81,7 @@ export function LatestScripts({ items }: { items: Category[] }) {
|
||||
)}
|
||||
<div className="min-w flex w-full flex-row flex-wrap gap-4">
|
||||
{latestScripts.slice(startIndex, endIndex).map((script) => (
|
||||
<Card
|
||||
key={script.slug}
|
||||
className="min-w-[250px] flex-1 flex-grow bg-accent/30"
|
||||
>
|
||||
<Card key={script.slug} className="min-w-[250px] flex-1 flex-grow bg-accent/30">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-3">
|
||||
<div className="flex h-16 w-16 min-w-16 items-center justify-center rounded-lg bg-accent p-1">
|
||||
@@ -107,10 +91,7 @@ export function LatestScripts({ items }: { items: Category[] }) {
|
||||
height={64}
|
||||
width={64}
|
||||
alt=""
|
||||
onError={(e) =>
|
||||
((e.currentTarget as HTMLImageElement).src =
|
||||
`/${basePath}/logo.png`)
|
||||
}
|
||||
onError={(e) => ((e.currentTarget as HTMLImageElement).src = `/${basePath}/logo.png`)}
|
||||
className="h-11 w-11 object-contain"
|
||||
/>
|
||||
</div>
|
||||
@@ -126,9 +107,7 @@ export function LatestScripts({ items }: { items: Category[] }) {
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<CardDescription className="line-clamp-3 text-card-foreground">
|
||||
{script.description}
|
||||
</CardDescription>
|
||||
<CardDescription className="line-clamp-3 text-card-foreground">{script.description}</CardDescription>
|
||||
</CardContent>
|
||||
<CardFooter className="">
|
||||
<Button asChild variant="outline">
|
||||
@@ -151,9 +130,7 @@ export function LatestScripts({ items }: { items: Category[] }) {
|
||||
|
||||
export function MostViewedScripts({ items }: { items: Category[] }) {
|
||||
const mostViewedScripts = items.reduce((acc: Script[], category) => {
|
||||
const foundScripts = category.scripts.filter((script) =>
|
||||
mostPopularScripts.includes(script.slug),
|
||||
);
|
||||
const foundScripts = category.scripts.filter((script) => mostPopularScripts.includes(script.slug));
|
||||
return acc.concat(foundScripts);
|
||||
}, []);
|
||||
|
||||
@@ -166,10 +143,7 @@ export function MostViewedScripts({ items }: { items: Category[] }) {
|
||||
)}
|
||||
<div className="min-w flex w-full flex-row flex-wrap gap-4">
|
||||
{mostViewedScripts.map((script) => (
|
||||
<Card
|
||||
key={script.slug}
|
||||
className="min-w-[250px] flex-1 flex-grow bg-accent/30"
|
||||
>
|
||||
<Card key={script.slug} className="min-w-[250px] flex-1 flex-grow bg-accent/30">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-3">
|
||||
<div className="flex size-16 min-w-16 items-center justify-center rounded-lg bg-accent p-1">
|
||||
@@ -179,10 +153,7 @@ export function MostViewedScripts({ items }: { items: Category[] }) {
|
||||
height={64}
|
||||
width={64}
|
||||
alt=""
|
||||
onError={(e) =>
|
||||
((e.currentTarget as HTMLImageElement).src =
|
||||
`/${basePath}/logo.png`)
|
||||
}
|
||||
onError={(e) => ((e.currentTarget as HTMLImageElement).src = `/${basePath}/logo.png`)}
|
||||
className="h-11 w-11 object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -14,12 +14,12 @@ import { Suspense } from "react";
|
||||
import { ResourceDisplay } from "./ResourceDisplay";
|
||||
import { getDisplayValueFromType } from "./ScriptInfoBlocks";
|
||||
import Alerts from "./ScriptItems/Alerts";
|
||||
import Buttons from "./ScriptItems/Buttons";
|
||||
import DefaultPassword from "./ScriptItems/DefaultPassword";
|
||||
import Description from "./ScriptItems/Description";
|
||||
import InstallCommand from "./ScriptItems/InstallCommand";
|
||||
import Tooltips from "./ScriptItems/Tooltips";
|
||||
import InterFaces from "./ScriptItems/InterFaces";
|
||||
import Buttons from "./ScriptItems/Buttons";
|
||||
import Tooltips from "./ScriptItems/Tooltips";
|
||||
|
||||
interface ScriptItemProps {
|
||||
item: Script;
|
||||
@@ -142,7 +142,9 @@ export function ScriptItem({ item, setSelectedScript }: ScriptItemProps) {
|
||||
|
||||
<div className="mt-4 rounded-lg border shadow-sm">
|
||||
<div className="flex gap-3 px-4 py-2 bg-accent/25">
|
||||
<h2 className="text-lg font-semibold">How to {item.type === "misc" ? "use" : "install"}</h2>
|
||||
<h2 className="text-lg font-semibold">
|
||||
How to {item.type === "pve" ? "use" : item.type === "addon" ? "apply" : "install"}
|
||||
</h2>
|
||||
<Tooltips item={item} />
|
||||
</div>
|
||||
<Separator />
|
||||
|
||||
@@ -16,7 +16,17 @@ const generateInstallSourceUrl = (slug: string) => {
|
||||
|
||||
const generateSourceUrl = (slug: string, type: string) => {
|
||||
const baseUrl = `https://raw.githubusercontent.com/community-scripts/${basePath}/main`;
|
||||
return type === "vm" ? `${baseUrl}/vm/${slug}.sh` : `${baseUrl}/misc/${slug}.sh`;
|
||||
|
||||
switch (type) {
|
||||
case "vm":
|
||||
return `${baseUrl}/vm/${slug}.sh`;
|
||||
case "pve":
|
||||
return `${baseUrl}/tools/pve/${slug}.sh`;
|
||||
case "addon":
|
||||
return `${baseUrl}/tools/addon/${slug}.sh`;
|
||||
default:
|
||||
return `${baseUrl}/ct/${slug}.sh`; // fallback for "ct"
|
||||
}
|
||||
};
|
||||
|
||||
const generateUpdateUrl = (slug: string) => {
|
||||
|
||||
@@ -5,85 +5,73 @@ import { Script } from "@/lib/types";
|
||||
import { getDisplayValueFromType } from "../ScriptInfoBlocks";
|
||||
|
||||
const getInstallCommand = (scriptPath = "", isAlpine = false) => {
|
||||
const url = `https://raw.githubusercontent.com/community-scripts/${basePath}/main/${scriptPath}`;
|
||||
return isAlpine
|
||||
? `bash -c "$(curl -fsSL ${url})"`
|
||||
: `bash -c "$(curl -fsSL ${url})"`;
|
||||
const url = `https://raw.githubusercontent.com/community-scripts/${basePath}/main/${scriptPath}`;
|
||||
return isAlpine ? `bash -c "$(curl -fsSL ${url})"` : `bash -c "$(curl -fsSL ${url})"`;
|
||||
};
|
||||
|
||||
|
||||
export default function InstallCommand({ item }: { item: Script }) {
|
||||
const alpineScript = item.install_methods.find(
|
||||
(method) => method.type === "alpine",
|
||||
);
|
||||
const alpineScript = item.install_methods.find((method) => method.type === "alpine");
|
||||
|
||||
const defaultScript = item.install_methods.find(
|
||||
(method) => method.type === "default",
|
||||
);
|
||||
const defaultScript = item.install_methods.find((method) => method.type === "default");
|
||||
|
||||
const renderInstructions = (isAlpine = false) => (
|
||||
const renderInstructions = (isAlpine = false) => (
|
||||
<>
|
||||
<p className="text-sm mt-2">
|
||||
{isAlpine ? (
|
||||
<>
|
||||
As an alternative option, you can use Alpine Linux and the {item.name} package to create a {item.name}{" "}
|
||||
{getDisplayValueFromType(item.type)} container with faster creation time and minimal system resource usage.
|
||||
You are also obliged to adhere to updates provided by the package maintainer.
|
||||
</>
|
||||
) : item.type === "pve" ? (
|
||||
<>
|
||||
To use the {item.name} script, run the command below **only** in the Proxmox VE Shell. This script is
|
||||
intended for managing or enhancing the host system directly.
|
||||
</>
|
||||
) : item.type === "addon" ? (
|
||||
<>
|
||||
This script enhances an existing setup. You can use it inside a running LXC container or directly on the
|
||||
Proxmox VE host to extend functionality with {item.name}.
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
To create a new Proxmox VE {item.name} {getDisplayValueFromType(item.type)}, run the command below in the
|
||||
Proxmox VE Shell.
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
{isAlpine && (
|
||||
<p className="mt-2 text-sm">
|
||||
To create a new Proxmox VE Alpine-{item.name} {getDisplayValueFromType(item.type)}, run the command below in
|
||||
the Proxmox VE Shell.
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
{alpineScript ? (
|
||||
<Tabs defaultValue="default" className="mt-2 w-full max-w-4xl">
|
||||
<TabsList>
|
||||
<TabsTrigger value="default">Default</TabsTrigger>
|
||||
<TabsTrigger value="alpine">Alpine Linux</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="default">
|
||||
{renderInstructions()}
|
||||
<CodeCopyButton>{getInstallCommand(defaultScript?.script)}</CodeCopyButton>
|
||||
</TabsContent>
|
||||
<TabsContent value="alpine">
|
||||
{renderInstructions(true)}
|
||||
<CodeCopyButton>{getInstallCommand(alpineScript.script, true)}</CodeCopyButton>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
) : defaultScript?.script ? (
|
||||
<>
|
||||
<p className="text-sm mt-2">
|
||||
{isAlpine ? (
|
||||
<>
|
||||
As an alternative option, you can use Alpine Linux and the{" "}
|
||||
{item.name} package to create a {item.name}{" "}
|
||||
{getDisplayValueFromType(item.type)} container with faster creation
|
||||
time and minimal system resource usage. You are also obliged to
|
||||
adhere to updates provided by the package maintainer.
|
||||
</>
|
||||
) : item.type == "misc" ? (
|
||||
<>
|
||||
To use the {item.name} script, run the command below in the shell.
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{" "}
|
||||
To create a new Proxmox VE {item.name}{" "}
|
||||
{getDisplayValueFromType(item.type)}, run the command below in the
|
||||
Proxmox VE Shell.
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
{isAlpine && (
|
||||
<p className="mt-2 text-sm">
|
||||
To create a new Proxmox VE Alpine-{item.name}{" "}
|
||||
{getDisplayValueFromType(item.type)}, run the command below in the
|
||||
Proxmox VE Shell
|
||||
</p>
|
||||
)}
|
||||
{renderInstructions()}
|
||||
<CodeCopyButton>{getInstallCommand(defaultScript.script)}</CodeCopyButton>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
{alpineScript ? (
|
||||
<Tabs defaultValue="default" className="mt-2 w-full max-w-4xl">
|
||||
<TabsList>
|
||||
<TabsTrigger value="default">Default</TabsTrigger>
|
||||
<TabsTrigger value="alpine">Alpine Linux</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="default">
|
||||
{renderInstructions()}
|
||||
<CodeCopyButton>
|
||||
{getInstallCommand(defaultScript?.script)}
|
||||
</CodeCopyButton>
|
||||
</TabsContent>
|
||||
<TabsContent value="alpine">
|
||||
{renderInstructions(true)}
|
||||
<CodeCopyButton>
|
||||
{getInstallCommand(alpineScript.script, true)}
|
||||
</CodeCopyButton>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
) : defaultScript?.script ? (
|
||||
<>
|
||||
{renderInstructions()}
|
||||
<CodeCopyButton>
|
||||
{getInstallCommand(defaultScript.script)}
|
||||
</CodeCopyButton>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user