Update the database by creating separate tables for alerts and default login details. Add a tab for default login details, and implement badges for updatable scripts and scripts requiring privileged access.
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
"@radix-ui/react-separator": "^1.1.0",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-tabs": "^1.1.0",
|
||||
"@radix-ui/react-tooltip": "^1.1.2",
|
||||
"@vercel/analytics": "^1.2.2",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Category } from "@/lib/types";
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
const response = await pb.collection("categories").getFullList({
|
||||
expand: "items.alpine_script",
|
||||
expand: "items.alerts,items.alpine_script,items.default_login",
|
||||
sort: "order",
|
||||
});
|
||||
|
||||
|
||||
@@ -18,9 +18,17 @@ import { Category, Script } from "@/lib/types";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { MostViewedScripts, LatestScripts } from "./ScriptInfoBlocks";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
|
||||
import { toast } from "sonner";
|
||||
import CodeCopyButton from "@/components/ui/code-copy-button";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
|
||||
|
||||
function ScriptItem({
|
||||
items,
|
||||
@@ -129,6 +137,7 @@ function ScriptItem({
|
||||
};
|
||||
|
||||
const hasAlpineScript = item?.expand?.alpine_script !== undefined;
|
||||
const hasDefaultLogin = item?.expand?.default_login !== undefined;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -277,40 +286,66 @@ function ScriptItem({
|
||||
<Separator className="mt-4" />
|
||||
<div>
|
||||
<div className="mt-4">
|
||||
<div className="p-2">
|
||||
<h2 className="mb-2 max-w-prose text-lg font-semibold">
|
||||
Description
|
||||
</h2>
|
||||
<p className="text-sm">
|
||||
{descriptionCodeBlock(item.description)}
|
||||
</p>
|
||||
</div>
|
||||
{item.alert1 && (
|
||||
<div className="mt-4 flex flex-col gap-2">
|
||||
<p className="inline-flex items-center gap-2 rounded-lg border border-red-500/25 bg-destructive/25 p-2 pl-4 text-sm">
|
||||
<Info className="h-4 min-h-4 w-4 min-w-4" />
|
||||
{descriptionCodeBlock(item.alert1)}
|
||||
<div className="flex p-2">
|
||||
<div>
|
||||
<h2 className="mb-2 max-w-prose text-lg font-semibold">
|
||||
Description
|
||||
</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{descriptionCodeBlock(item.description)}
|
||||
</p>
|
||||
{item.alert2 && (
|
||||
<p className="inline-flex items-center gap-2 rounded-lg border border-red-500/25 bg-destructive/25 p-2 pl-4 text-sm">
|
||||
<Info className="min-w-42 h-4 min-h-4 w-4" />
|
||||
{descriptionCodeBlock(item.alert2)}
|
||||
</p>
|
||||
)}
|
||||
{item.alert3 && (
|
||||
</div>
|
||||
</div>
|
||||
{item.expand?.alerts?.length > 0 &&
|
||||
item.expand.alerts.map((alert: any, index: number) => (
|
||||
<div key={index} className="mt-4 flex flex-col gap-2">
|
||||
<p className="inline-flex items-center gap-2 rounded-lg border border-red-500/25 bg-destructive/25 p-2 pl-4 text-sm">
|
||||
<Info className="h-4 min-h-4 w-4 min-w-4" />
|
||||
{descriptionCodeBlock(item.alert3)}
|
||||
{descriptionCodeBlock(alert.content)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-4 rounded-lg border bg-accent/50">
|
||||
<h2 className="px-4 py-2 text-lg font-semibold">
|
||||
How to {item.item_type ? "install" : "use"}
|
||||
</h2>
|
||||
<div className="flex gap-3 px-4 py-2">
|
||||
<h2 className=" text-lg font-semibold">
|
||||
How to {item.item_type ? "install" : "use"}
|
||||
</h2>
|
||||
<div className="flex items-center gap-2">
|
||||
{item.privileged && (
|
||||
<TooltipProvider>
|
||||
<Tooltip delayDuration={100}>
|
||||
<TooltipTrigger>
|
||||
<Badge variant={"success"}>
|
||||
Privileged
|
||||
</Badge>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
<p className="text-sm">
|
||||
This script will be run in a privileged LXC
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
{item.isUpdateable && (
|
||||
<TooltipProvider>
|
||||
<Tooltip delayDuration={100}>
|
||||
<TooltipTrigger>
|
||||
<Badge variant={"success"}>Updateable</Badge>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
<p className="text-sm">
|
||||
To Update {item.title}, run the command below
|
||||
(or type update) in the LXC Console.
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<Separator className="w-full"></Separator>
|
||||
<div className="p-4">
|
||||
{hasAlpineScript ? (
|
||||
@@ -332,12 +367,6 @@ function ScriptItem({
|
||||
{item.item_type}, run the command below in the
|
||||
Proxmox VE Shell.
|
||||
</p>
|
||||
{item.isUpdateable && (
|
||||
<p className="text-sm">
|
||||
To Update {item.title}, run the command
|
||||
below (or type update) in the LXC Console.
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<CodeCopyButton>{installCommand}</CodeCopyButton>
|
||||
@@ -374,12 +403,6 @@ function ScriptItem({
|
||||
{item.item_type}, run the command below in the
|
||||
Proxmox VE Shell.
|
||||
</p>
|
||||
{item.isUpdateable && (
|
||||
<p className="text-sm">
|
||||
To Update {item.title}, run the command below
|
||||
(or type update) in the LXC Console.
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{installCommand && (
|
||||
@@ -390,6 +413,55 @@ function ScriptItem({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{hasDefaultLogin && (
|
||||
<div className="mt-4 rounded-lg border bg-accent/50">
|
||||
<div className="flex gap-3 px-4 py-2">
|
||||
<h2 className=" text-lg font-semibold">
|
||||
Default Login Credentials
|
||||
</h2>
|
||||
</div>
|
||||
<Separator className="w-full"></Separator>
|
||||
<div className="flex flex-col gap-2 p-4">
|
||||
<p className="mb-2 text-sm">
|
||||
You can use the following credentials to login to the{" "}
|
||||
{""}
|
||||
{item.title} {item.item_type}.
|
||||
</p>
|
||||
<div className="text-sm">
|
||||
Username:{" "}
|
||||
<Button
|
||||
variant={"secondary"}
|
||||
size={"null"}
|
||||
onClick={() =>
|
||||
handleCopy(
|
||||
"username",
|
||||
item.expand.default_login.username,
|
||||
)
|
||||
}
|
||||
>
|
||||
{item.expand.default_login.username}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="text-sm">
|
||||
Password:{" "}
|
||||
<Button
|
||||
variant={"secondary"}
|
||||
size={"null"}
|
||||
onClick={() =>
|
||||
handleCopy(
|
||||
"password",
|
||||
item.expand.default_login.password,
|
||||
)
|
||||
}
|
||||
>
|
||||
{item.expand.default_login.password}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,6 +15,7 @@ const badgeVariants = cva(
|
||||
destructive:
|
||||
"border-transparent text-destructive-foreground border-destructive-foreground",
|
||||
outline: "text-foreground",
|
||||
success: "text-green-500 border-green-500"
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
30
src/components/ui/tooltip.tsx
Normal file
30
src/components/ui/tooltip.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const TooltipProvider = TooltipPrimitive.Provider
|
||||
|
||||
const Tooltip = TooltipPrimitive.Root
|
||||
|
||||
const TooltipTrigger = TooltipPrimitive.Trigger
|
||||
|
||||
const TooltipContent = React.forwardRef<
|
||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
<TooltipPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
||||
|
||||
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
||||
@@ -18,6 +18,8 @@ export type Script = {
|
||||
id: string;
|
||||
expand: {
|
||||
alpine_script: alpine_script;
|
||||
alerts: alerts[];
|
||||
default_login: default_login;
|
||||
}
|
||||
isUpdateable: boolean;
|
||||
item_type: string;
|
||||
@@ -30,6 +32,7 @@ export type Script = {
|
||||
isMostViewed: boolean;
|
||||
mostViewedPosition: number;
|
||||
interface: string;
|
||||
privileged: boolean;
|
||||
};
|
||||
|
||||
type alpine_script = {
|
||||
@@ -39,6 +42,15 @@ type alpine_script = {
|
||||
default_ram: string;
|
||||
};
|
||||
|
||||
type alerts = {
|
||||
content: string;
|
||||
}
|
||||
|
||||
type default_login = {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface Category {
|
||||
catagoryName: string;
|
||||
collectionId: string;
|
||||
@@ -49,11 +61,3 @@ export interface Category {
|
||||
items: Script[];
|
||||
};
|
||||
}
|
||||
|
||||
export type Scripts = {
|
||||
page: number;
|
||||
perPage: number;
|
||||
totalItems: number;
|
||||
totalPages: number;
|
||||
items: Category[];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user