1
0
mirror of https://github.com/community-scripts/ProxmoxVE.git synced 2025-04-19 16:28:07 +00:00

Move Scripts to Tools / Add-Ons

This commit is contained in:
CanbiZ 2025-04-09 09:57:56 +02:00
parent 3ab2ecc3ef
commit 3dbc7f80d2
77 changed files with 1262 additions and 1413 deletions

View File

@ -1,9 +1,7 @@
{ {
"name": "Proxmox VE LXC IP-Tag", "name": "Proxmox VE LXC IP-Tag",
"slug": "add-lxc-iptag", "slug": "add-lxc-iptag",
"categories": [ "categories": [1],
1
],
"date_created": "2024-12-16", "date_created": "2024-12-16",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/add-lxc-iptag.sh", "script": "tools/pve/add-lxc-iptag.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "NetBird", "name": "NetBird",
"slug": "add-netbird-lxc", "slug": "add-netbird-lxc",
"categories": [ "categories": [1],
1
],
"date_created": "2024-05-19", "date_created": "2024-05-19",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/add-netbird-lxc.sh", "script": "tools/addon/add-netbird-lxc.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Tailscale", "name": "Tailscale",
"slug": "add-tailscale-lxc", "slug": "add-tailscale-lxc",
"categories": [ "categories": [1],
1
],
"date_created": "2024-05-02", "date_created": "2024-05-02",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/add-tailscale-lxc.sh", "script": "tools/addon/add-tailscale-lxc.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "All Templates", "name": "All Templates",
"slug": "all-templates", "slug": "all-templates",
"categories": [ "categories": [1],
1
],
"date_created": "2024-05-02", "date_created": "2024-05-02",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/all-templates.sh", "script": "tools/addon/all-templates.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Proxmox VE LXC Cleaner", "name": "Proxmox VE LXC Cleaner",
"slug": "clean-lxcs", "slug": "clean-lxcs",
"categories": [ "categories": [1],
1
],
"date_created": "2024-04-29", "date_created": "2024-04-29",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/clean-lxcs.sh", "script": "tools/pve/clean-lxcs.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Proxmox Clean Orphaned LVM", "name": "Proxmox Clean Orphaned LVM",
"slug": "clean-orphaned-lvm", "slug": "clean-orphaned-lvm",
"categories": [ "categories": [1],
1
],
"date_created": "2025-01-29", "date_created": "2025-01-29",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/clean-orphaned-lvm.sh", "script": "tools/pve/clean-orphaned-lvm.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,11 +1,7 @@
{ {
"name": "VS Code Server", "name": "VS Code Server",
"slug": "code-server", "slug": "code-server",
"categories": [ "categories": [1, 20, 11],
1,
20,
11
],
"date_created": "2024-05-02", "date_created": "2024-05-02",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -18,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/code-server.sh", "script": "tools/addon/code-server.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Proxmox VE Cron LXC Updater", "name": "Proxmox VE Cron LXC Updater",
"slug": "cron-update-lxcs", "slug": "cron-update-lxcs",
"categories": [ "categories": [1],
1
],
"date_created": "2024-04-29", "date_created": "2024-04-29",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/cron-update-lxcs.sh", "script": "tools/pve/cron-update-lxcs.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,
@ -39,6 +37,5 @@
"text": "To exclude LXCs from updating, edit the crontab using `crontab -e` and add CTID as shown in the example below:\n\n\n\n`0 0 * * 0 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin /bin/bash -c '$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/update-lxcs-cron.sh)' -s 103 111 >>/var/log/update-lxcs-cron.log 2>/dev/null`", "text": "To exclude LXCs from updating, edit the crontab using `crontab -e` and add CTID as shown in the example below:\n\n\n\n`0 0 * * 0 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin /bin/bash -c '$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/update-lxcs-cron.sh)' -s 103 111 >>/var/log/update-lxcs-cron.log 2>/dev/null`",
"type": "info" "type": "info"
} }
] ]
} }

View File

@ -1,9 +1,7 @@
{ {
"name": "CrowdSec", "name": "CrowdSec",
"slug": "crowdsec", "slug": "crowdsec",
"categories": [ "categories": [6],
6
],
"date_created": "2024-05-02", "date_created": "2024-05-02",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/crowdsec.sh", "script": "tools/addon/crowdsec.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "File Browser", "name": "File Browser",
"slug": "filebrowser", "slug": "filebrowser",
"categories": [ "categories": [1],
1
],
"date_created": "2024-05-02", "date_created": "2024-05-02",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/filebrowser.sh", "script": "tools/addon/filebrowser.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,
@ -27,7 +25,7 @@
}, },
{ {
"type": "alpine", "type": "alpine",
"script": "misc/filebrowser.sh", "script": "tools/addon/filebrowser.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Proxmox VE LXC Filesystem Trim", "name": "Proxmox VE LXC Filesystem Trim",
"slug": "fstrim", "slug": "fstrim",
"categories": [ "categories": [1],
1
],
"date_created": "2024-04-29", "date_created": "2024-04-29",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/fstrim.sh", "script": "tools/pve/fstrim.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Glances", "name": "Glances",
"slug": "glances", "slug": "glances",
"categories": [ "categories": [9],
9
],
"date_created": "2024-05-02", "date_created": "2024-05-02",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/glances.sh", "script": "tools/addon/glances.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Proxmox VE Host Backup", "name": "Proxmox VE Host Backup",
"slug": "host-backup", "slug": "host-backup",
"categories": [ "categories": [1],
1
],
"date_created": "2024-04-29", "date_created": "2024-04-29",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/host-backup.sh", "script": "tools/pve/host-backup.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Proxmox VE Kernel Clean", "name": "Proxmox VE Kernel Clean",
"slug": "kernel-clean", "slug": "kernel-clean",
"categories": [ "categories": [1],
1
],
"date_created": "2024-04-29", "date_created": "2024-04-29",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/kernel-clean.sh", "script": "tools/pve/kernel-clean.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Proxmox VE Kernel Pin", "name": "Proxmox VE Kernel Pin",
"slug": "kernel-pin", "slug": "kernel-pin",
"categories": [ "categories": [1],
1
],
"date_created": "2024-05-08", "date_created": "2024-05-08",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/kernel-pin.sh", "script": "tools/pve/kernel-pin.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Container LXC Deletion", "name": "Container LXC Deletion",
"slug": "lxc-delete", "slug": "lxc-delete",
"categories": [ "categories": [1],
1
],
"date_created": "2025-01-21", "date_created": "2025-01-21",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/lxc-delete.sh", "script": "tools/pve/lxc-delete.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Proxmox VE Processor Microcode", "name": "Proxmox VE Processor Microcode",
"slug": "microcode", "slug": "microcode",
"categories": [ "categories": [1],
1
],
"date_created": "2024-04-29", "date_created": "2024-04-29",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/microcode.sh", "script": "tools/pve/microcode.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Proxmox VE Monitor-All", "name": "Proxmox VE Monitor-All",
"slug": "monitor-all", "slug": "monitor-all",
"categories": [ "categories": [1],
1
],
"date_created": "2024-04-29", "date_created": "2024-04-29",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/monitor-all.sh", "script": "tools/pve/monitor-all.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Proxmox VE Netdata", "name": "Proxmox VE Netdata",
"slug": "netdata", "slug": "netdata",
"categories": [ "categories": [1],
1
],
"date_created": "2024-04-29", "date_created": "2024-04-29",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/netdata.sh", "script": "tools/addon/netdata.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "OliveTin", "name": "OliveTin",
"slug": "olivetin", "slug": "olivetin",
"categories": [ "categories": [10],
10
],
"date_created": "2024-05-02", "date_created": "2024-05-02",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/olivetin.sh", "script": "tools/addon/olivetin.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Proxmox Backup Server Processor Microcode", "name": "Proxmox Backup Server Processor Microcode",
"slug": "pbs-microcode", "slug": "pbs-microcode",
"categories": [ "categories": [1],
1
],
"date_created": "2025-02-07", "date_created": "2025-02-07",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/pbs_microcode.sh", "script": "tools/pve/pbs_microcode.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Proxmox Backup Server Post Install", "name": "Proxmox Backup Server Post Install",
"slug": "post-pbs-install", "slug": "post-pbs-install",
"categories": [ "categories": [1],
1
],
"date_created": "2024-04-29", "date_created": "2024-04-29",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/post-pbs-install.sh", "script": "tools/pve/post-pbs-install.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Proxmox Mail Gateway Post Install", "name": "Proxmox Mail Gateway Post Install",
"slug": "post-pmg-install", "slug": "post-pmg-install",
"categories": [ "categories": [1],
1
],
"date_created": "2025-01-20", "date_created": "2025-01-20",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/post-pmg-install.sh", "script": "tools/pve/post-pmg-install.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Proxmox VE Post Install", "name": "Proxmox VE Post Install",
"slug": "post-pve-install", "slug": "post-pve-install",
"categories": [ "categories": [1],
1
],
"date_created": "2024-04-28", "date_created": "2024-04-28",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/post-pve-install.sh", "script": "tools/pve/post-pve-install.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Proxmox VE CPU Scaling Governor", "name": "Proxmox VE CPU Scaling Governor",
"slug": "scaling-governor", "slug": "scaling-governor",
"categories": [ "categories": [1],
1
],
"date_created": "2024-04-29", "date_created": "2024-04-29",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/scaling-governor.sh", "script": "tools/pve/scaling-governor.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Proxmox VE LXC Updater", "name": "Proxmox VE LXC Updater",
"slug": "update-lxcs", "slug": "update-lxcs",
"categories": [ "categories": [1],
1
],
"date_created": "2024-04-29", "date_created": "2024-04-29",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/update-lxcs.sh", "script": "tools/pve/update-lxcs.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Proxmox Update Repositories", "name": "Proxmox Update Repositories",
"slug": "update-repo", "slug": "update-repo",
"categories": [ "categories": [1],
1
],
"date_created": "2024-11-04", "date_created": "2024-11-04",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/update-repo.sh", "script": "tools/pve/update-repo.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,9 +1,7 @@
{ {
"name": "Webmin System Administration", "name": "Webmin System Administration",
"slug": "webmin", "slug": "webmin",
"categories": [ "categories": [1],
1
],
"date_created": "2024-05-02", "date_created": "2024-05-02",
"type": "misc", "type": "misc",
"updateable": false, "updateable": false,
@ -16,7 +14,7 @@
"install_methods": [ "install_methods": [
{ {
"type": "default", "type": "default",
"script": "misc/webmin.sh", "script": "tools/addon/webmin.sh",
"resources": { "resources": {
"cpu": null, "cpu": null,
"ram": null, "ram": null,

View File

@ -1,12 +1,12 @@
"use client"; "use client";
import React, { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { ChevronLeft, ChevronRight } from "lucide-react"; import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Category } from "@/lib/types"; import { Category } from "@/lib/types";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { useRouter } from "next/navigation";
import React, { useEffect, useState } from "react";
const defaultLogo = "/default-logo.png"; // Fallback logo path const defaultLogo = "/default-logo.png"; // Fallback logo path
const MAX_DESCRIPTION_LENGTH = 100; // Set max length for description const MAX_DESCRIPTION_LENGTH = 100; // Set max length for description
@ -17,11 +17,11 @@ const formattedBadge = (type: string) => {
case "vm": case "vm":
return <Badge className="text-blue-500/75 border-blue-500/75 badge">VM</Badge>; return <Badge className="text-blue-500/75 border-blue-500/75 badge">VM</Badge>;
case "ct": case "ct":
return ( return <Badge className="text-yellow-500/75 border-yellow-500/75 badge">LXC</Badge>;
<Badge className="text-yellow-500/75 border-yellow-500/75 badge">LXC</Badge> case "pve":
); return <Badge className="text-orange-500/75 border-orange-500/75 badge">PVE</Badge>;
case "misc": case "addon":
return <Badge className="text-green-500/75 border-green-500/75 badge">MISC</Badge>; return <Badge className="text-green-500/75 border-green-500/75 badge">ADDON</Badge>;
} }
return null; return null;
}; };
@ -100,9 +100,7 @@ const CategoryView = () => {
}; };
const truncateDescription = (text: string) => { const truncateDescription = (text: string) => {
return text.length > MAX_DESCRIPTION_LENGTH return text.length > MAX_DESCRIPTION_LENGTH ? `${text.slice(0, MAX_DESCRIPTION_LENGTH)}...` : text;
? `${text.slice(0, MAX_DESCRIPTION_LENGTH)}...`
: text;
}; };
const renderResources = (script: any) => { const renderResources = (script: any) => {
@ -111,9 +109,24 @@ const CategoryView = () => {
const hdd = script.install_methods[0]?.resources.hdd; const hdd = script.install_methods[0]?.resources.hdd;
const resourceParts = []; const resourceParts = [];
if (cpu) resourceParts.push(<span key="cpu"><b>CPU:</b> {cpu}vCPU</span>); if (cpu)
if (ram) resourceParts.push(<span key="ram"><b>RAM:</b> {ram}MB</span>); resourceParts.push(
if (hdd) resourceParts.push(<span key="hdd"><b>HDD:</b> {hdd}GB</span>); <span key="cpu">
<b>CPU:</b> {cpu}vCPU
</span>,
);
if (ram)
resourceParts.push(
<span key="ram">
<b>RAM:</b> {ram}MB
</span>,
);
if (hdd)
resourceParts.push(
<span key="hdd">
<b>HDD:</b> {hdd}GB
</span>,
);
return resourceParts.length > 0 ? ( return resourceParts.length > 0 ? (
<div className="text-sm text-gray-400"> <div className="text-sm text-gray-400">

View File

@ -24,8 +24,8 @@ export const ScriptSchema = z.object({
slug: z.string().min(1, "Slug is required"), slug: z.string().min(1, "Slug is required"),
categories: z.array(z.number()), categories: z.array(z.number()),
date_created: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").min(1, "Date is required"), date_created: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").min(1, "Date is required"),
type: z.enum(["vm", "ct", "misc", "turnkey"], { type: z.enum(["vm", "ct", "pve", "addon", "turnkey"], {
errorMap: () => ({ message: "Type must be either 'vm', 'ct', 'misc' or 'turnkey'" }) errorMap: () => ({ message: "Type must be either 'vm', 'ct', 'pve', 'addon' or 'turnkey'" })
}), }),
updateable: z.boolean(), updateable: z.boolean(),
privileged: z.boolean(), privileged: z.boolean(),

View File

@ -5,18 +5,8 @@ import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar"; import { Calendar } from "@/components/ui/calendar";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
Popover, import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { fetchCategories } from "@/lib/data"; import { fetchCategories } from "@/lib/data";
@ -66,8 +56,7 @@ export default function JSONGenerator() {
.catch((error) => console.error("Error fetching categories:", error)); .catch((error) => console.error("Error fetching categories:", error));
}, []); }, []);
const updateScript = useCallback( const updateScript = useCallback((key: keyof Script, value: Script[keyof Script]) => {
(key: keyof Script, value: Script[keyof Script]) => {
setScript((prev) => { setScript((prev) => {
const updated = { ...prev, [key]: value }; const updated = { ...prev, [key]: value };
@ -86,9 +75,7 @@ export default function JSONGenerator() {
setZodErrors(result.success ? null : result.error); setZodErrors(result.success ? null : result.error);
return updated; return updated;
}); });
}, }, []);
[],
);
const handleCopy = useCallback(() => { const handleCopy = useCallback(() => {
navigator.clipboard.writeText(JSON.stringify(script, null, 2)); navigator.clipboard.writeText(JSON.stringify(script, null, 2));
@ -120,16 +107,13 @@ export default function JSONGenerator() {
); );
const formattedDate = useMemo( const formattedDate = useMemo(
() => () => (script.date_created ? format(script.date_created, "PPP") : undefined),
script.date_created ? format(script.date_created, "PPP") : undefined,
[script.date_created], [script.date_created],
); );
const validationAlert = useMemo( const validationAlert = useMemo(
() => ( () => (
<Alert <Alert className={cn("text-black", isValid ? "bg-green-100" : "bg-red-100")}>
className={cn("text-black", isValid ? "bg-green-100" : "bg-red-100")}
>
<AlertTitle>{isValid ? "Valid JSON" : "Invalid JSON"}</AlertTitle> <AlertTitle>{isValid ? "Valid JSON" : "Invalid JSON"}</AlertTitle>
<AlertDescription> <AlertDescription>
{isValid {isValid
@ -160,21 +144,13 @@ export default function JSONGenerator() {
<Label> <Label>
Name <span className="text-red-500">*</span> Name <span className="text-red-500">*</span>
</Label> </Label>
<Input <Input placeholder="Example" value={script.name} onChange={(e) => updateScript("name", e.target.value)} />
placeholder="Example"
value={script.name}
onChange={(e) => updateScript("name", e.target.value)}
/>
</div> </div>
<div> <div>
<Label> <Label>
Slug <span className="text-red-500">*</span> Slug <span className="text-red-500">*</span>
</Label> </Label>
<Input <Input placeholder="example" value={script.slug} onChange={(e) => updateScript("slug", e.target.value)} />
placeholder="example"
value={script.slug}
onChange={(e) => updateScript("slug", e.target.value)}
/>
</div> </div>
</div> </div>
<div> <div>
@ -197,11 +173,7 @@ export default function JSONGenerator() {
onChange={(e) => updateScript("description", e.target.value)} onChange={(e) => updateScript("description", e.target.value)}
/> />
</div> </div>
<Categories <Categories script={script} setScript={setScript} categories={categories} />
script={script}
setScript={setScript}
categories={categories}
/>
<div className="flex gap-2"> <div className="flex gap-2">
<div className="flex flex-col gap-2 w-full"> <div className="flex flex-col gap-2 w-full">
<Label>Date Created</Label> <Label>Date Created</Label>
@ -209,10 +181,7 @@ export default function JSONGenerator() {
<PopoverTrigger asChild className="flex-1"> <PopoverTrigger asChild className="flex-1">
<Button <Button
variant={"outline"} variant={"outline"}
className={cn( className={cn("pl-3 text-left font-normal w-full", !script.date_created && "text-muted-foreground")}
"pl-3 text-left font-normal w-full",
!script.date_created && "text-muted-foreground",
)}
> >
{formattedDate || <span>Pick a date</span>} {formattedDate || <span>Pick a date</span>}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" /> <CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
@ -230,38 +199,26 @@ export default function JSONGenerator() {
</div> </div>
<div className="flex flex-col gap-2 w-full"> <div className="flex flex-col gap-2 w-full">
<Label>Type</Label> <Label>Type</Label>
<Select <Select value={script.type} onValueChange={(value) => updateScript("type", value)}>
value={script.type}
onValueChange={(value) => updateScript("type", value)}
>
<SelectTrigger className="flex-1"> <SelectTrigger className="flex-1">
<SelectValue placeholder="Type" /> <SelectValue placeholder="Type" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="ct">LXC Container</SelectItem> <SelectItem value="ct">LXC Container</SelectItem>
<SelectItem value="vm">Virtual Machine</SelectItem> <SelectItem value="vm">Virtual Machine</SelectItem>
<SelectItem value="misc">Miscellaneous</SelectItem> <SelectItem value="pve">PVE-Tool</SelectItem>
<SelectItem value="addon">Add-On</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
</div> </div>
<div className="w-full flex gap-5"> <div className="w-full flex gap-5">
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Switch <Switch checked={script.updateable} onCheckedChange={(checked) => updateScript("updateable", checked)} />
checked={script.updateable}
onCheckedChange={(checked) =>
updateScript("updateable", checked)
}
/>
<label>Updateable</label> <label>Updateable</label>
</div> </div>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Switch <Switch checked={script.privileged} onCheckedChange={(checked) => updateScript("privileged", checked)} />
checked={script.privileged}
onCheckedChange={(checked) =>
updateScript("privileged", checked)
}
/>
<label>Privileged</label> <label>Privileged</label>
</div> </div>
</div> </div>
@ -269,12 +226,7 @@ export default function JSONGenerator() {
placeholder="Interface Port" placeholder="Interface Port"
type="number" type="number"
value={script.interface_port || ""} value={script.interface_port || ""}
onChange={(e) => onChange={(e) => updateScript("interface_port", e.target.value ? Number(e.target.value) : null)}
updateScript(
"interface_port",
e.target.value ? Number(e.target.value) : null,
)
}
/> />
<div className="flex gap-2"> <div className="flex gap-2">
<Input <Input
@ -285,17 +237,10 @@ export default function JSONGenerator() {
<Input <Input
placeholder="Documentation URL" placeholder="Documentation URL"
value={script.documentation || ""} value={script.documentation || ""}
onChange={(e) => onChange={(e) => updateScript("documentation", e.target.value || null)}
updateScript("documentation", e.target.value || null)
}
/> />
</div> </div>
<InstallMethod <InstallMethod script={script} setScript={setScript} setIsValid={setIsValid} setZodErrors={setZodErrors} />
script={script}
setScript={setScript}
setIsValid={setIsValid}
setZodErrors={setZodErrors}
/>
<h3 className="text-xl font-semibold">Default Credentials</h3> <h3 className="text-xl font-semibold">Default Credentials</h3>
<Input <Input
placeholder="Username" placeholder="Username"
@ -317,30 +262,17 @@ export default function JSONGenerator() {
}) })
} }
/> />
<Note <Note script={script} setScript={setScript} setIsValid={setIsValid} setZodErrors={setZodErrors} />
script={script}
setScript={setScript}
setIsValid={setIsValid}
setZodErrors={setZodErrors}
/>
</form> </form>
</div> </div>
<div className="w-1/2 p-4 bg-background overflow-y-auto"> <div className="w-1/2 p-4 bg-background overflow-y-auto">
{validationAlert} {validationAlert}
<div className="relative"> <div className="relative">
<div className="absolute right-2 top-2 flex gap-1"> <div className="absolute right-2 top-2 flex gap-1">
<Button <Button size="icon" variant="outline" onClick={handleCopy}>
size="icon"
variant="outline"
onClick={handleCopy}
>
{isCopied ? <Check className="h-4 w-4" /> : <Clipboard className="h-4 w-4" />} {isCopied ? <Check className="h-4 w-4" /> : <Clipboard className="h-4 w-4" />}
</Button> </Button>
<Button <Button size="icon" variant="outline" onClick={handleDownload}>
size="icon"
variant="outline"
onClick={handleDownload}
>
<Download className="h-4 w-4" /> <Download className="h-4 w-4" />
</Button> </Button>
</div> </div>

View File

@ -1,12 +1,5 @@
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { basePath, mostPopularScripts } from "@/config/siteConfig"; import { basePath, mostPopularScripts } from "@/config/siteConfig";
import { extractDate } from "@/lib/time"; import { extractDate } from "@/lib/time";
import { Category, Script } from "@/lib/types"; import { Category, Script } from "@/lib/types";
@ -23,7 +16,8 @@ export const getDisplayValueFromType = (type: string) => {
return "LXC"; return "LXC";
case "vm": case "vm":
return "VM"; return "VM";
case "misc": case "pve":
case "addon":
return ""; return "";
default: default:
return ""; return "";
@ -47,8 +41,7 @@ export function LatestScripts({ items }: { items: Category[] }) {
}); });
return Array.from(uniqueScriptsMap.values()).sort( return Array.from(uniqueScriptsMap.values()).sort(
(a, b) => (a, b) => new Date(b.date_created).getTime() - new Date(a.date_created).getTime(),
new Date(b.date_created).getTime() - new Date(a.date_created).getTime(),
); );
}, [items]); }, [items]);
@ -74,18 +67,12 @@ export function LatestScripts({ items }: { items: Category[] }) {
<h2 className="text-lg font-semibold">Newest Scripts</h2> <h2 className="text-lg font-semibold">Newest Scripts</h2>
<div className="flex items-center justify-end gap-1"> <div className="flex items-center justify-end gap-1">
{page > 1 && ( {page > 1 && (
<div <div className="cursor-pointer select-none p-2 text-sm font-semibold" onClick={goToPreviousPage}>
className="cursor-pointer select-none p-2 text-sm font-semibold"
onClick={goToPreviousPage}
>
Previous Previous
</div> </div>
)} )}
{endIndex < latestScripts.length && ( {endIndex < latestScripts.length && (
<div <div onClick={goToNextPage} className="cursor-pointer select-none p-2 text-sm font-semibold">
onClick={goToNextPage}
className="cursor-pointer select-none p-2 text-sm font-semibold"
>
{page === 1 ? "More.." : "Next"} {page === 1 ? "More.." : "Next"}
</div> </div>
)} )}
@ -94,10 +81,7 @@ export function LatestScripts({ items }: { items: Category[] }) {
)} )}
<div className="min-w flex w-full flex-row flex-wrap gap-4"> <div className="min-w flex w-full flex-row flex-wrap gap-4">
{latestScripts.slice(startIndex, endIndex).map((script) => ( {latestScripts.slice(startIndex, endIndex).map((script) => (
<Card <Card key={script.slug} className="min-w-[250px] flex-1 flex-grow bg-accent/30">
key={script.slug}
className="min-w-[250px] flex-1 flex-grow bg-accent/30"
>
<CardHeader> <CardHeader>
<CardTitle className="flex items-center gap-3"> <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"> <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} height={64}
width={64} width={64}
alt="" alt=""
onError={(e) => onError={(e) => ((e.currentTarget as HTMLImageElement).src = `/${basePath}/logo.png`)}
((e.currentTarget as HTMLImageElement).src =
`/${basePath}/logo.png`)
}
className="h-11 w-11 object-contain" className="h-11 w-11 object-contain"
/> />
</div> </div>
@ -126,9 +107,7 @@ export function LatestScripts({ items }: { items: Category[] }) {
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<CardDescription className="line-clamp-3 text-card-foreground"> <CardDescription className="line-clamp-3 text-card-foreground">{script.description}</CardDescription>
{script.description}
</CardDescription>
</CardContent> </CardContent>
<CardFooter className=""> <CardFooter className="">
<Button asChild variant="outline"> <Button asChild variant="outline">
@ -151,9 +130,7 @@ export function LatestScripts({ items }: { items: Category[] }) {
export function MostViewedScripts({ items }: { items: Category[] }) { export function MostViewedScripts({ items }: { items: Category[] }) {
const mostViewedScripts = items.reduce((acc: Script[], category) => { const mostViewedScripts = items.reduce((acc: Script[], category) => {
const foundScripts = category.scripts.filter((script) => const foundScripts = category.scripts.filter((script) => mostPopularScripts.includes(script.slug));
mostPopularScripts.includes(script.slug),
);
return acc.concat(foundScripts); 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"> <div className="min-w flex w-full flex-row flex-wrap gap-4">
{mostViewedScripts.map((script) => ( {mostViewedScripts.map((script) => (
<Card <Card key={script.slug} className="min-w-[250px] flex-1 flex-grow bg-accent/30">
key={script.slug}
className="min-w-[250px] flex-1 flex-grow bg-accent/30"
>
<CardHeader> <CardHeader>
<CardTitle className="flex items-center gap-3"> <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"> <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} height={64}
width={64} width={64}
alt="" alt=""
onError={(e) => onError={(e) => ((e.currentTarget as HTMLImageElement).src = `/${basePath}/logo.png`)}
((e.currentTarget as HTMLImageElement).src =
`/${basePath}/logo.png`)
}
className="h-11 w-11 object-contain" className="h-11 w-11 object-contain"
/> />
</div> </div>

View File

@ -14,12 +14,12 @@ import { Suspense } from "react";
import { ResourceDisplay } from "./ResourceDisplay"; import { ResourceDisplay } from "./ResourceDisplay";
import { getDisplayValueFromType } from "./ScriptInfoBlocks"; import { getDisplayValueFromType } from "./ScriptInfoBlocks";
import Alerts from "./ScriptItems/Alerts"; import Alerts from "./ScriptItems/Alerts";
import Buttons from "./ScriptItems/Buttons";
import DefaultPassword from "./ScriptItems/DefaultPassword"; import DefaultPassword from "./ScriptItems/DefaultPassword";
import Description from "./ScriptItems/Description"; import Description from "./ScriptItems/Description";
import InstallCommand from "./ScriptItems/InstallCommand"; import InstallCommand from "./ScriptItems/InstallCommand";
import Tooltips from "./ScriptItems/Tooltips";
import InterFaces from "./ScriptItems/InterFaces"; import InterFaces from "./ScriptItems/InterFaces";
import Buttons from "./ScriptItems/Buttons"; import Tooltips from "./ScriptItems/Tooltips";
interface ScriptItemProps { interface ScriptItemProps {
item: Script; item: Script;
@ -142,7 +142,9 @@ export function ScriptItem({ item, setSelectedScript }: ScriptItemProps) {
<div className="mt-4 rounded-lg border shadow-sm"> <div className="mt-4 rounded-lg border shadow-sm">
<div className="flex gap-3 px-4 py-2 bg-accent/25"> <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} /> <Tooltips item={item} />
</div> </div>
<Separator /> <Separator />

View File

@ -16,7 +16,17 @@ const generateInstallSourceUrl = (slug: string) => {
const generateSourceUrl = (slug: string, type: string) => { const generateSourceUrl = (slug: string, type: string) => {
const baseUrl = `https://raw.githubusercontent.com/community-scripts/${basePath}/main`; 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) => { const generateUpdateUrl = (slug: string) => {

View File

@ -6,50 +6,44 @@ import { getDisplayValueFromType } from "../ScriptInfoBlocks";
const getInstallCommand = (scriptPath = "", isAlpine = false) => { const getInstallCommand = (scriptPath = "", isAlpine = false) => {
const url = `https://raw.githubusercontent.com/community-scripts/${basePath}/main/${scriptPath}`; const url = `https://raw.githubusercontent.com/community-scripts/${basePath}/main/${scriptPath}`;
return isAlpine return isAlpine ? `bash -c "$(curl -fsSL ${url})"` : `bash -c "$(curl -fsSL ${url})"`;
? `bash -c "$(curl -fsSL ${url})"`
: `bash -c "$(curl -fsSL ${url})"`;
}; };
export default function InstallCommand({ item }: { item: Script }) { export default function InstallCommand({ item }: { item: Script }) {
const alpineScript = item.install_methods.find( const alpineScript = item.install_methods.find((method) => method.type === "alpine");
(method) => method.type === "alpine",
);
const defaultScript = item.install_methods.find( const defaultScript = item.install_methods.find((method) => method.type === "default");
(method) => method.type === "default",
);
const renderInstructions = (isAlpine = false) => ( const renderInstructions = (isAlpine = false) => (
<> <>
<p className="text-sm mt-2"> <p className="text-sm mt-2">
{isAlpine ? ( {isAlpine ? (
<> <>
As an alternative option, you can use Alpine Linux and the{" "} As an alternative option, you can use Alpine Linux and the {item.name} package to create a {item.name}{" "}
{item.name} package to create a {item.name}{" "} {getDisplayValueFromType(item.type)} container with faster creation time and minimal system resource usage.
{getDisplayValueFromType(item.type)} container with faster creation You are also obliged to adhere to updates provided by the package maintainer.
time and minimal system resource usage. You are also obliged to
adhere to updates provided by the package maintainer.
</> </>
) : item.type == "misc" ? ( ) : item.type === "pve" ? (
<> <>
To use the {item.name} script, run the command below in the shell. 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
To create a new Proxmox VE {item.name}{" "}
{getDisplayValueFromType(item.type)}, run the command below in the
Proxmox VE Shell. Proxmox VE Shell.
</> </>
)} )}
</p> </p>
{isAlpine && ( {isAlpine && (
<p className="mt-2 text-sm"> <p className="mt-2 text-sm">
To create a new Proxmox VE Alpine-{item.name}{" "} To create a new Proxmox VE Alpine-{item.name} {getDisplayValueFromType(item.type)}, run the command below in
{getDisplayValueFromType(item.type)}, run the command below in the the Proxmox VE Shell.
Proxmox VE Shell
</p> </p>
)} )}
</> </>
@ -65,23 +59,17 @@ export default function InstallCommand({ item }: { item: Script }) {
</TabsList> </TabsList>
<TabsContent value="default"> <TabsContent value="default">
{renderInstructions()} {renderInstructions()}
<CodeCopyButton> <CodeCopyButton>{getInstallCommand(defaultScript?.script)}</CodeCopyButton>
{getInstallCommand(defaultScript?.script)}
</CodeCopyButton>
</TabsContent> </TabsContent>
<TabsContent value="alpine"> <TabsContent value="alpine">
{renderInstructions(true)} {renderInstructions(true)}
<CodeCopyButton> <CodeCopyButton>{getInstallCommand(alpineScript.script, true)}</CodeCopyButton>
{getInstallCommand(alpineScript.script, true)}
</CodeCopyButton>
</TabsContent> </TabsContent>
</Tabs> </Tabs>
) : defaultScript?.script ? ( ) : defaultScript?.script ? (
<> <>
{renderInstructions()} {renderInstructions()}
<CodeCopyButton> <CodeCopyButton>{getInstallCommand(defaultScript.script)}</CodeCopyButton>
{getInstallCommand(defaultScript.script)}
</CodeCopyButton>
</> </>
) : null} ) : null}
</div> </div>

View File

@ -6,6 +6,7 @@ import {
CommandItem, CommandItem,
CommandList, CommandList,
} from "@/components/ui/command"; } from "@/components/ui/command";
import { basePath } from "@/config/siteConfig";
import { fetchCategories } from "@/lib/data"; import { fetchCategories } from "@/lib/data";
import { Category } from "@/lib/types"; import { Category } from "@/lib/types";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@ -15,18 +16,17 @@ import React from "react";
import { Badge } from "./ui/badge"; import { Badge } from "./ui/badge";
import { Button } from "./ui/button"; import { Button } from "./ui/button";
import { DialogTitle } from "./ui/dialog"; import { DialogTitle } from "./ui/dialog";
import { basePath } from "@/config/siteConfig";
export const formattedBadge = (type: string) => { export const formattedBadge = (type: string) => {
switch (type) { switch (type) {
case "vm": case "vm":
return <Badge className="text-blue-500/75 border-blue-500/75">VM</Badge>; return <Badge className="text-blue-500/75 border-blue-500/75">VM</Badge>;
case "ct": case "ct":
return ( return <Badge className="text-yellow-500/75 border-yellow-500/75">LXC</Badge>;
<Badge className="text-yellow-500/75 border-yellow-500/75">LXC</Badge> case "pve":
); return <Badge className="text-orange-500/75 border-orange-500/75">PVE</Badge>;
case "misc": case "addon":
return <Badge className="text-green-500/75 border-green-500/75">MISC</Badge>; return <Badge className="text-green-500/75 border-green-500/75">ADDON</Badge>;
} }
return null; return null;
}; };
@ -84,14 +84,9 @@ export default function CommandMenu() {
<DialogTitle className="sr-only">Search scripts</DialogTitle> <DialogTitle className="sr-only">Search scripts</DialogTitle>
<CommandInput placeholder="Search for a script..." /> <CommandInput placeholder="Search for a script..." />
<CommandList> <CommandList>
<CommandEmpty> <CommandEmpty>{isLoading ? "Loading..." : "No scripts found."}</CommandEmpty>
{isLoading ? "Loading..." : "No scripts found."}
</CommandEmpty>
{links.map((category) => ( {links.map((category) => (
<CommandGroup <CommandGroup key={`category:${category.name}`} heading={category.name}>
key={`category:${category.name}`}
heading={category.name}
>
{category.scripts.map((script) => ( {category.scripts.map((script) => (
<CommandItem <CommandItem
key={`script:${script.slug}`} key={`script:${script.slug}`}
@ -104,10 +99,7 @@ export default function CommandMenu() {
<div className="flex gap-2" onClick={() => setOpen(false)}> <div className="flex gap-2" onClick={() => setOpen(false)}>
<Image <Image
src={script.logo || `/${basePath}/logo.png`} src={script.logo || `/${basePath}/logo.png`}
onError={(e) => onError={(e) => ((e.currentTarget as HTMLImageElement).src = `/${basePath}/logo.png`)}
((e.currentTarget as HTMLImageElement).src =
`/${basePath}/logo.png`)
}
unoptimized unoptimized
width={16} width={16}
height={16} height={16}

View File

@ -5,7 +5,7 @@ export type Script = {
slug: string; slug: string;
categories: number[]; categories: number[];
date_created: string; date_created: string;
type: "vm" | "ct" | "misc"; type: "vm" | "ct" | "pve" | "addon";
updateable: boolean; updateable: boolean;
privileged: boolean; privileged: boolean;
interface_port: number | null; interface_port: number | null;

View File

@ -18,7 +18,7 @@ $STD apt-get install -y g++-multilib
msg_ok "Installed Dependencies" msg_ok "Installed Dependencies"
msg_info "Installing Daemon Sync Server" msg_info "Installing Daemon Sync Server"
curl -fsSL "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/daemonsync_2.2.0.0059_amd64.deb" -o "daemonsync_2.2.0.0059_amd64.deb" curl -fsSL "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/daemonsync_2.2.0.0059_amd64.deb" -o "daemonsync_2.2.0.0059_amd64.deb"
$STD dpkg -i daemonsync_2.2.0.0059_amd64.deb $STD dpkg -i daemonsync_2.2.0.0059_amd64.deb
msg_ok "Installed Daemon Sync Server" msg_ok "Installed Daemon Sync Server"

Binary file not shown.