Compare commits

..

1 Commits

Author SHA1 Message Date
0bcc97d569 Update CHANGELOG.md 2025-12-13 09:08:01 +00:00
128 changed files with 1718 additions and 2787 deletions

1
.github/pull_request_template.md generated vendored
View File

@ -3,7 +3,6 @@ PRs without prior testing will be closed. -->
## ✍️ Description
## 🔗 Related Issue
Fixes #

View File

@ -10,104 +10,12 @@
> [!CAUTION]
Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit the project's popularity for potentially malicious purposes.
## 2025-12-17
## 2025-12-16
### 🆕 New Scripts
- [REFACTOR]: NetVisor => Scanopy [@vhsdream](https://github.com/vhsdream) ([#10011](https://github.com/community-scripts/ProxmoxVE/pull/10011))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- zabbix: fix repo url after change [@MickLesk](https://github.com/MickLesk) ([#10042](https://github.com/community-scripts/ProxmoxVE/pull/10042))
- Fix: mariadb repo in update_scripts [@MickLesk](https://github.com/MickLesk) ([#10034](https://github.com/community-scripts/ProxmoxVE/pull/10034))
- 2fauth: update PHP version from 8.3 to 8.4 in update_script [@MickLesk](https://github.com/MickLesk) ([#10035](https://github.com/community-scripts/ProxmoxVE/pull/10035))
- pdm: add rsyslog to fix /dev/log Connection refused errors [@MickLesk](https://github.com/MickLesk) ([#10018](https://github.com/community-scripts/ProxmoxVE/pull/10018))
- 2fauth: bump to php8.4 [@MickLesk](https://github.com/MickLesk) ([#10019](https://github.com/community-scripts/ProxmoxVE/pull/10019))
- Miniflux: use correct systemctl to check service instead of file path [@MickLesk](https://github.com/MickLesk) ([#10024](https://github.com/community-scripts/ProxmoxVE/pull/10024))
- PhotoPrism: export env variables for CLI tools [@MickLesk](https://github.com/MickLesk) ([#10023](https://github.com/community-scripts/ProxmoxVE/pull/10023))
### 💾 Core
- #### ✨ New Features
- core: IP-Range-Scan Support (app.vars / default.vars) [@MickLesk](https://github.com/MickLesk) ([#10038](https://github.com/community-scripts/ProxmoxVE/pull/10038))
- tools.func: add optional enabled parameter to setup_deb822_repo [@MickLesk](https://github.com/MickLesk) ([#10017](https://github.com/community-scripts/ProxmoxVE/pull/10017))
- core: map Etc/* timezones to 'host' for pct compatibility [@MickLesk](https://github.com/MickLesk) ([#10020](https://github.com/community-scripts/ProxmoxVE/pull/10020))
### 🌐 Website
- website: bump deps & prevent security issues [@MickLesk](https://github.com/MickLesk) ([#10045](https://github.com/community-scripts/ProxmoxVE/pull/10045))
## 2025-12-15
### 🆕 New Scripts
- Koel ([#9972](https://github.com/community-scripts/ProxmoxVE/pull/9972))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Fix DiscoPanel build [@PouletteMC](https://github.com/PouletteMC) ([#10009](https://github.com/community-scripts/ProxmoxVE/pull/10009))
- fix:ct/openwebui.sh adding progressbar and minimize service downtime [@jobben-2025](https://github.com/jobben-2025) ([#9894](https://github.com/community-scripts/ProxmoxVE/pull/9894))
- homarr: add: temp note aboute deb13 requirement [@CrazyWolf13](https://github.com/CrazyWolf13) ([#9992](https://github.com/community-scripts/ProxmoxVE/pull/9992))
- paperless-ai: backup data and recreate venv during update [@MickLesk](https://github.com/MickLesk) ([#9987](https://github.com/community-scripts/ProxmoxVE/pull/9987))
- fix(booklore): add setup_yq to update script [@MickLesk](https://github.com/MickLesk) ([#9989](https://github.com/community-scripts/ProxmoxVE/pull/9989))
- fix(pangolin-install): add network-online dependency [@worried-networking](https://github.com/worried-networking) ([#9984](https://github.com/community-scripts/ProxmoxVE/pull/9984))
- #### ✨ New Features
- OPNsense: dynamic crawl latest stable FreeBSD [@austindsmith](https://github.com/austindsmith) ([#9831](https://github.com/community-scripts/ProxmoxVE/pull/9831))
- #### 🔧 Refactor
- Refactor: Heimdall Dashboard [@tremor021](https://github.com/tremor021) ([#9959](https://github.com/community-scripts/ProxmoxVE/pull/9959))
### 💾 Core
- #### 🐞 Bug Fixes
- tools: prevent awk errors in setup_rust on restricted containers [@MickLesk](https://github.com/MickLesk) ([#9985](https://github.com/community-scripts/ProxmoxVE/pull/9985))
- core: App Defaults force mode and prevent unbound variables [@MickLesk](https://github.com/MickLesk) ([#9971](https://github.com/community-scripts/ProxmoxVE/pull/9971))
- core: load app defaults before applying base_settings / fix composer cleanup after install/update [@MickLesk](https://github.com/MickLesk) ([#9965](https://github.com/community-scripts/ProxmoxVE/pull/9965))
- #### ✨ New Features
- tools: handle flat repositories in setup_deb822_repo [@MickLesk](https://github.com/MickLesk) ([#9994](https://github.com/community-scripts/ProxmoxVE/pull/9994))
### 📚 Documentation
- (github) remove old files and assets [@MickLesk](https://github.com/MickLesk) ([#9991](https://github.com/community-scripts/ProxmoxVE/pull/9991))
- README; add project statistics / formatting [@MickLesk](https://github.com/MickLesk) ([#9967](https://github.com/community-scripts/ProxmoxVE/pull/9967))
## 2025-12-14
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- SonarQube: Fix database variables [@tremor021](https://github.com/tremor021) ([#9946](https://github.com/community-scripts/ProxmoxVE/pull/9946))
- #### 💥 Breaking Changes
- refactor: homarr [@CrazyWolf13](https://github.com/CrazyWolf13) ([#9948](https://github.com/community-scripts/ProxmoxVE/pull/9948))
### 🌐 Website
- Update dependencies and remove unused files [@BramSuurdje](https://github.com/BramSuurdje) ([#9945](https://github.com/community-scripts/ProxmoxVE/pull/9945))
## 2025-12-13
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Umami: Use `pnpm` [@tremor021](https://github.com/tremor021) ([#9937](https://github.com/community-scripts/ProxmoxVE/pull/9937))
- Tunarr: Switch to prebuild archive [@tremor021](https://github.com/tremor021) ([#9920](https://github.com/community-scripts/ProxmoxVE/pull/9920))
- [HOTFIX] NetVisor: backup OIDC config before update [@vhsdream](https://github.com/vhsdream) ([#9895](https://github.com/community-scripts/ProxmoxVE/pull/9895))
- Update OPNsense download URL to version 14.3 [@jaredcarling42-design](https://github.com/jaredcarling42-design) ([#9899](https://github.com/community-scripts/ProxmoxVE/pull/9899))
@ -115,19 +23,6 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
- Add optional TLS setup to Valkey installer [@pshankinclarke](https://github.com/pshankinclarke) ([#9789](https://github.com/community-scripts/ProxmoxVE/pull/9789))
- #### 🔧 Refactor
- Refactor: Spoolman [@tremor021](https://github.com/tremor021) ([#9873](https://github.com/community-scripts/ProxmoxVE/pull/9873))
### 🧰 Tools
- AdGuardHome-Sync ([#9783](https://github.com/community-scripts/ProxmoxVE/pull/9783))
### ❔ Uncategorized
- Update category value in glance.json and adguard-home.json [@Bensonheimer992](https://github.com/Bensonheimer992) ([#9932](https://github.com/community-scripts/ProxmoxVE/pull/9932))
- Change category ID from 6 to 3 in coolify.json and dokploy.json [@Bensonheimer992](https://github.com/Bensonheimer992) ([#9930](https://github.com/community-scripts/ProxmoxVE/pull/9930))
## 2025-12-12
### 🆕 New Scripts

View File

@ -30,8 +30,8 @@
<br />
**Simplify your Proxmox VE setup with community-driven automation scripts**
Originally created by tteck, now maintained and expanded by the community
> **Simplify your Proxmox VE setup with community-driven automation scripts**
> Originally created by tteck, now maintained and expanded by the community
</div>
@ -239,34 +239,17 @@ This project is maintained by volunteers in memory of tteck. Your support helps
---
## 📈 Project Statistics
<p align="center">
<img
src="https://repobeats.axiom.co/api/embed/57edde03e00f88d739bdb5b844ff7d07dd079375.svg"
alt="Repobeats analytics"
width="650"
/>
</p>
## 📈 Project Growth
<p align="center">
<div align="center">
<a href="https://star-history.com/#community-scripts/ProxmoxVE&Date">
<picture>
<source
media="(prefers-color-scheme: dark)"
srcset="https://api.star-history.com/svg?repos=community-scripts/ProxmoxVE&type=Date&theme=dark"
/>
<source
media="(prefers-color-scheme: light)"
srcset="https://api.star-history.com/svg?repos=community-scripts/ProxmoxVE&type=Date"
/>
<img
alt="Star History Chart"
src="https://api.star-history.com/svg?repos=community-scripts/ProxmoxVE&type=Date"
width="650"
/>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=community-scripts/ProxmoxVE&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=community-scripts/ProxmoxVE&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=community-scripts/ProxmoxVE&type=Date" />
</picture>
</a>
</p>
</div>
---

View File

@ -28,21 +28,20 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
if check_for_gh_release "2fauth" "Bubka/2FAuth"; then
$STD apt update
$STD apt -y upgrade
msg_info "Creating Backup"
mv "/opt/2fauth" "/opt/2fauth-backup"
if ! dpkg -l | grep -q 'php8.4'; then
if ! dpkg -l | grep -q 'php8.3'; then
cp /etc/nginx/conf.d/2fauth.conf /etc/nginx/conf.d/2fauth.conf.bak
fi
msg_ok "Backup Created"
if ! dpkg -l | grep -q 'php8.4'; then
PHP_VERSION="8.4" PHP_MODULE="common,ctype,fileinfo,mysql,cli,tokenizer,dom,redis,session,openssl" PHP_FPM="YES" setup_php
sed -i 's/php8\.[0-9]/php8.4/g' /etc/nginx/conf.d/2fauth.conf
if ! dpkg -l | grep -q 'php8.3'; then
PHP_VERSION="8.3" PHP_MODULE="common,ctype,fileinfo,mysql,cli,tokenizer,dom,redis,session,openssl" PHP_FPM="YES" setup_php
sed -i 's/php8.2/php8.3/g' /etc/nginx/conf.d/2fauth.conf
fi
fetch_and_deploy_gh_release "2fauth" "Bubka/2FAuth"
setup_composer

View File

@ -13,7 +13,6 @@ var_disk="${var_disk:-8}"
var_os="${var_os:-ubuntu}"
var_version="${var_version:-24.04}"
var_unprivileged="${var_unprivileged:-0}"
var_gpu="${var_gpu:-yes}"
header_info "$APP"
variables

View File

@ -20,16 +20,15 @@ color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /opt/apache-guacamole ]]; then
msg_error "No ${APP} Installation Found!"
header_info
check_container_storage
check_container_resources
if [[ ! -d /opt/apache-guacamole ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
msg_error "Currently we don't provide an update function for this ${APP}."
exit
fi
setup_mariadb
msg_error "Currently we don't provide an update function for this ${APP}."
exit
}
start

View File

@ -28,7 +28,6 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
if check_for_gh_release "booklore" "booklore-app/BookLore"; then
msg_info "Stopping Service"
systemctl stop booklore
@ -47,7 +46,6 @@ function update_script() {
msg_ok "Built Frontend"
JAVA_VERSION="21" setup_java
setup_yq
msg_info "Building Backend"
cd /opt/booklore/booklore-api

View File

@ -28,7 +28,6 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
if check_for_gh_release "bookstack" "BookStackApp/BookStack"; then
msg_info "Stopping Apache2"
systemctl stop apache2

138
ct/calibre-web.sh Normal file
View File

@ -0,0 +1,138 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2025 tteck
# Author: tteck (tteckster) | Co-Author: remz1337
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/janeczku/calibre-web
APP="Calibre-Web"
var_tags="${var_tags:-eBook}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-2048}"
var_disk="${var_disk:-4}"
var_os="${var_os:-debian}"
var_version="${var_version:-12}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -f /etc/systemd/system/cps.service ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
msg_info "Stopping Service"
systemctl stop cps
msg_ok "Stopped Service"
msg_info "Updating ${APP}"
cd /opt/kepubify
rm -rf kepubify-linux-64bit
curl -fsSLO https://github.com/pgaskin/kepubify/releases/latest/download/kepubify-linux-64bit
chmod +x kepubify-linux-64bit
menu_array=("1" "Enables gdrive as storage backend for your ebooks" OFF
"2" "Enables sending emails via a googlemail account without enabling insecure apps" OFF
"3" "Enables displaying of additional author infos on the authors page" OFF
"4" "Enables login via LDAP server" OFF
"5" "Enables login via google or github oauth" OFF
"6" "Enables extracting of metadata from epub, fb2, pdf files, and also extraction of covers from cbr, cbz, cbt files" OFF
"7" "Enables extracting of metadata from cbr, cbz, cbt files" OFF
"8" "Enables syncing with your kobo reader" OFF)
if [ -f "/opt/calibre-web/options.txt" ]; then
cps_options="$(cat /opt/calibre-web/options.txt)"
IFS=',' read -ra ADDR <<<"$cps_options"
for i in "${ADDR[@]}"; do
if [ $i == "gdrive" ]; then
line=0
elif [ $i == "gmail" ]; then
line=1
elif [ $i == "goodreads" ]; then
line=2
elif [ $i == "ldap" ]; then
line=3
elif [ $i == "oauth" ]; then
line=4
elif [ $i == "metadata" ]; then
line=5
elif [ $i == "comics" ]; then
line=6
elif [ $i == "kobo" ]; then
line=7
fi
array_index=$((3 * line + 2))
menu_array[$array_index]=ON
done
fi
if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi
CHOICES=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CALIBRE-WEB OPTIONS" --separate-output --checklist "Choose Additional Options" 15 125 8 "${menu_array[@]}" 3>&1 1>&2 2>&3)
spinner &
SPINNER_PID=$!
options=()
if [ ! -z "$CHOICES" ]; then
for CHOICE in $CHOICES; do
case "$CHOICE" in
"1")
options+=(gdrive)
;;
"2")
options+=(gmail)
;;
"3")
options+=(goodreads)
;;
"4")
options+=(ldap)
apt-get install -qqy libldap2-dev libsasl2-dev
;;
"5")
options+=(oauth)
;;
"6")
options+=(metadata)
;;
"7")
options+=(comics)
;;
"8")
options+=(kobo)
;;
*)
echo "Unsupported item $CHOICE!" >&2
exit
;;
esac
done
fi
if [ ${#options[@]} -gt 0 ]; then
cps_options=$(
IFS=,
echo "${options[*]}"
)
echo $cps_options >/opt/calibre-web/options.txt
$STD pip install --upgrade calibreweb[$cps_options]
else
rm -rf /opt/calibre-web/options.txt
$STD pip install --upgrade calibreweb
fi
msg_info "Starting Service"
systemctl start cps
msg_ok "Started Service"
msg_ok "Updated successfully!"
exit
}
start
build_container
description
msg_ok "Completed Successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8083${CL}"

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2025 community-scripts ORG
# Author: jdacode
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
@ -13,7 +13,6 @@ var_disk="${var_disk:-25}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
var_gpu="${var_gpu:-yes}"
header_info "$APP"
variables

View File

@ -13,7 +13,6 @@ var_disk="${var_disk:-20}"
var_os="${var_os:-debian}"
var_version="${var_version:-12}"
var_unprivileged="${var_unprivileged:-1}"
var_gpu="${var_gpu:-yes}"
header_info "$APP"
variables

View File

@ -49,8 +49,6 @@ function update_script() {
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "discopanel" "nickheyer/discopanel" "tarball" "latest" "/opt/discopanel"
msg_info "Setting up DiscoPanel"
cd /opt/discopanel
$STD make gen
cd /opt/discopanel/web/discopanel
$STD npm install
$STD npm run build

View File

@ -13,7 +13,6 @@ var_disk="${var_disk:-8}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
var_gpu="${var_gpu:-yes}"
header_info "$APP"
variables
@ -89,8 +88,8 @@ function update_script() {
fi
if ! grep -q "DJANGO_SECRET_KEY" /opt/dispatcharr/.env; then
DJANGO_SECRET=$(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9' | cut -c1-50)
echo "DJANGO_SECRET_KEY=$DJANGO_SECRET" >>/opt/dispatcharr/.env
DJANGO_SECRET=$(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9' | cut -c1-50)
echo "DJANGO_SECRET_KEY=$DJANGO_SECRET" >> /opt/dispatcharr/.env
fi
cd /opt/dispatcharr

View File

@ -20,16 +20,15 @@ color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /usr/share/dolibarr ]]; then
msg_error "No ${APP} Installation Found!"
header_info
check_container_storage
check_container_resources
if [[ ! -d /usr/share/dolibarr ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
msg_error "To update ${APP}, use the applications web interface."
exit
fi
setup_mariadb
msg_error "To update ${APP}, use the applications web interface."
exit
}
start
@ -39,4 +38,4 @@ description
msg_ok "Completed Successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/dolibarr/install${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/dolibarr/install${CL}"

View File

@ -27,7 +27,6 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
if ! grep -Fq "root /usr/bin/php /opt/domain-monitor/cron/check_domains.php" /etc/crontab; then
echo "0 0 * * * root /usr/bin/php /opt/domain-monitor/cron/check_domains.php" >>/etc/crontab

View File

@ -28,7 +28,7 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
if check_for_gh_release "firefly" "firefly-iii/firefly-iii"; then
systemctl stop apache2
cp /opt/firefly/.env /opt/.env

View File

@ -23,7 +23,6 @@ function update_script() {
header_info
check_container_storage
check_container_resources
setup_mariadb
NODE_VERSION="22" setup_nodejs

View File

@ -28,7 +28,6 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
RELEASE=$(curl -fsSL https://api.github.com/repos/glpi-project/glpi/releases/latest | grep '"tag_name"' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/')
if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then
msg_error "Currently we don't provide an update function for this ${APP}."

View File

@ -13,7 +13,6 @@ var_disk="${var_disk:-4}"
var_os="${var_os:-debian}"
var_version="${var_version:-12}"
var_unprivileged="${var_unprivileged:-1}"
var_gpu="${var_gpu:-yes}"
header_info "$APP"
variables

6
ct/headers/calibre-web Normal file
View File

@ -0,0 +1,6 @@
______ ___ __ _ __ __
/ ____/___ _/ (_) /_ ________ | | / /__ / /_
/ / / __ `/ / / __ \/ ___/ _ \_____| | /| / / _ \/ __ \
/ /___/ /_/ / / / /_/ / / / __/_____/ |/ |/ / __/ /_/ /
\____/\__,_/_/_/_.___/_/ \___/ |__/|__/\___/_.___/

View File

@ -1,6 +0,0 @@
__ __ __
/ //_/___ ___ / /
/ ,< / __ \/ _ \/ /
/ /| / /_/ / __/ /
/_/ |_\____/\___/_/

View File

@ -1,6 +1,6 @@
_____
/ ___/_________ _____ ____ ____ __ __
\__ \/ ___/ __ `/ __ \/ __ \/ __ \/ / / /
___/ / /__/ /_/ / / / / /_/ / /_/ / /_/ /
/____/\___/\__,_/_/ /_/\____/ .___/\__, /
/_/ /____/
_ __ __ _ ___
/ | / /__ / /| | / (_)________ _____
/ |/ / _ \/ __/ | / / / ___/ __ \/ ___/
/ /| / __/ /_ | |/ / (__ ) /_/ / /
/_/ |_/\___/\__/ |___/_/____/\____/_/

View File

@ -1,6 +0,0 @@
_____
/ ___/_________ _____ ____ ____ __ __
\__ \/ ___/ __ `/ __ \/ __ \/ __ \/ / / /
___/ / /__/ /_/ / / / / /_/ / /_/ / /_/ /
/____/\___/\__,_/_/ /_/\____/ .___/\__, /
/_/ /____/

View File

@ -11,7 +11,7 @@ var_cpu="${var_cpu:-1}"
var_ram="${var_ram:-512}"
var_disk="${var_disk:-2}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_version="${var_version:-12}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
@ -27,45 +27,45 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "Heimdall" "linuxserver/Heimdall"; then
RELEASE=$(curl -fsSL "https://api.github.com/repos/linuxserver/Heimdall/releases/latest" | awk '/tag_name/{print $4;exit}' FS='[""]')
if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then
msg_info "Stopping Service"
systemctl stop heimdall
sleep 1
msg_ok "Stopped Service"
msg_info "Backing up Data"
cp -R /opt/Heimdall/database database-backup
cp -R /opt/Heimdall/public public-backup
sleep 1
msg_ok "Backed up Data"
setup_composer
fetch_and_deploy_gh_release "Heimdall" "linuxserver/Heimdall" "tarball"
msg_info "Updating Heimdall-Dashboard"
msg_info "Updating Heimdall Dashboard to ${RELEASE}"
curl -fsSL "https://github.com/linuxserver/Heimdall/archive/${RELEASE}.tar.gz" -o $(basename "https://github.com/linuxserver/Heimdall/archive/${RELEASE}.tar.gz")
tar xzf "${RELEASE}".tar.gz
VER=$(curl -fsSL https://api.github.com/repos/linuxserver/Heimdall/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }')
cp -R Heimdall-"${VER}"/* /opt/Heimdall
cd /opt/Heimdall
$STD apt-get install -y composer
export COMPOSER_ALLOW_SUPERUSER=1
$STD composer dump-autoload
msg_ok "Updated Heimdall-Dashboard"
echo "${RELEASE}" >/opt/${APP}_version.txt
msg_ok "Updated Heimdall Dashboard to ${RELEASE}"
msg_info "Restoring Data"
cd ~
cp -R database-backup/* /opt/Heimdall/database
cp -R public-backup/* /opt/Heimdall/public
sleep 1
msg_ok "Restored Data"
msg_info "Cleaning Up"
rm -rf {public-backup,database-backup}
msg_info "Cleanup"
rm -rf {"${RELEASE}".tar.gz,Heimdall-"${VER}",public-backup,database-backup,Heimdall}
sleep 1
msg_ok "Cleaned Up"
msg_ok "Cleaned"
msg_info "Starting Service"
systemctl start heimdall.service
sleep 2
msg_ok "Started Service"
msg_ok "Updated successfully!"
else
msg_ok "No update required. ${APP} is already at ${RELEASE}."
fi
exit
}

View File

@ -1,20 +1,21 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2025 community-scripts ORG
# Author: MickLesk (CanbiZ) | Co-Author: CrazyWolf13
# Author: CrazyWolf13
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://homarr.dev/
APP="homarr"
var_tags="${var_tags:-arr;dashboard}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-1024}"
var_cpu="${var_cpu:-3}"
var_ram="${var_ram:-6144}"
var_disk="${var_disk:-8}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
@ -27,43 +28,69 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
if [[ -f /opt/homarr/database/db.sqlite ]]; then
msg_error "Old Homarr detected due to existing database file (/opt/homarr/database/db.sqlite)."
msg_error "Update not supported. Refer to:"
msg_error " - https://github.com/community-scripts/ProxmoxVE/discussions/1551"
msg_error " - https://homarr.dev/docs/getting-started/after-the-installation/#importing-a-zip-from-version-before-100"
exit
fi
if [[ ! -f /opt/run_homarr.sh ]]; then
msg_info "Detected outdated and missing service files"
msg_error "Warning - The port of homarr changed from 3000 to 7575"
$STD apt-get install -y nginx gettext openssl gpg
sed -i '/^NODE_ENV=/d' /opt/homarr/.env && echo "NODE_ENV='production'" >>/opt/homarr/.env
sed -i '/^DB_DIALECT=/d' /opt/homarr/.env && echo "DB_DIALECT='sqlite'" >>/opt/homarr/.env
cat <<'EOF' >/opt/run_homarr.sh
#!/bin/bash
set -a
source /opt/homarr/.env
set +a
export DB_DIALECT='sqlite'
export AUTH_SECRET=$(openssl rand -base64 32)
export CRON_JOB_API_KEY=$(openssl rand -base64 32)
node /opt/homarr_db/migrations/$DB_DIALECT/migrate.cjs /opt/homarr_db/migrations/$DB_DIALECT
for dir in $(find /opt/homarr_db/migrations/migrations -mindepth 1 -maxdepth 1 -type d); do
dirname=$(basename "$dir")
mkdir -p "/opt/homarr_db/migrations/$dirname"
cp -r "$dir"/* "/opt/homarr_db/migrations/$dirname/" 2>/dev/null || true
done
export HOSTNAME=$(ip route get 1.1.1.1 | grep -oP 'src \K[^ ]+')
envsubst '${HOSTNAME}' < /etc/nginx/templates/nginx.conf > /etc/nginx/nginx.conf
nginx -g 'daemon off;' &
redis-server /opt/homarr/packages/redis/redis.conf &
node apps/tasks/tasks.cjs &
node apps/websocket/wssServer.cjs &
node apps/nextjs/server.js & PID=$!
wait $PID
EOF
chmod +x /opt/run_homarr.sh
rm /etc/systemd/system/homarr.service
cat <<EOF >/etc/systemd/system/homarr.service
[Unit]
Description=Homarr Service
After=network.target
[Service]
Type=exec
WorkingDirectory=/opt/homarr
EnvironmentFile=-/opt/homarr/.env
ExecStart=/opt/run_homarr.sh
[Install]
WantedBy=multi-user.target
EOF
msg_ok "Updated Services"
systemctl daemon-reload
fi
if check_for_gh_release "homarr" "homarr-labs/homarr"; then
msg_info "Stopping Services (Patience)"
systemctl stop homarr
systemctl stop redis-server
msg_ok "Services Stopped"
if ! { grep -q '^REDIS_IS_EXTERNAL=' /opt/homarr/.env 2>/dev/null || grep -q '^REDIS_IS_EXTERNAL=' /opt/homarr.env 2>/dev/null; }; then
DEBIAN_VERSION=$(cat /etc/debian_version 2>/dev/null | cut -d'.' -f1)
if [[ -n "$DEBIAN_VERSION" ]] && [[ "$DEBIAN_VERSION" -lt 13 ]]; then
msg_warn "⚠️ COMPATIBILITY WARNING ⚠️"
msg_warn "You are running Debian ${DEBIAN_VERSION}. Homarr's requires Debian 13"
msg_warn ""
msg_warn "Please Upgrade to Debian 13:"
msg_warn "See: https://github.com/community-scripts/ProxmoxVE/discussions/7489"
msg_warn ""
exit
fi
msg_info "Fixing old structure"
# fix musl issues because homarr compiles on alpine not debian soure: https://github.com/alexander-akhmetov/python-telegram/issues/3
$STD apt install -y musl-dev
ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1
cp /opt/homarr/.env /opt/homarr.env
echo "REDIS_IS_EXTERNAL='true'" >> /opt/homarr.env
sed -i 's|^ExecStart=.*|ExecStart=/opt/homarr/run.sh|' /etc/systemd/system/homarr.service
sed -i 's|^EnvironmentFile=.*|EnvironmentFile=-/opt/homarr.env|' /etc/systemd/system/homarr.service
chown -R redis:redis /appdata/redis
chmod 744 /appdata/redis
mkdir -p /etc/systemd/system/redis-server.service.d/
cat <<EOF >/etc/systemd/system/redis-server.service.d/override.conf
[Service]
ReadWritePaths=-/appdata/redis -/var/lib/redis -/var/log/redis -/var/run/redis -/etc/redis
EOF
systemctl daemon-reload
rm /opt/run_homarr.sh
msg_ok "Fixed old structure"
fi
msg_info "Backup Data"
mkdir -p /opt/homarr-data-backup
cp /opt/homarr/.env /opt/homarr-data-backup/.env
msg_ok "Backup Data"
msg_info "Updating Nodejs"
$STD apt update
@ -71,21 +98,69 @@ EOF
msg_ok "Updated Nodejs"
NODE_VERSION=$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.engines.node | split(">=")[1] | split(".")[0]')
NODE_MODULE="pnpm@$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.packageManager | split("@")[1]')"
setup_nodejs
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "homarr" "homarr-labs/homarr" "prebuild" "latest" "/opt/homarr" "build-amd64.tar.gz"
msg_info "Updating Homarr"
cp /opt/homarr/redis.conf /etc/redis/redis.conf
rm -rf /opt/homarr
fetch_and_deploy_gh_release "homarr" "homarr-labs/homarr"
msg_info "Updating and rebuilding ${APP} (Patience)"
rm /opt/run_homarr.sh
cat <<'EOF' >/opt/run_homarr.sh
#!/bin/bash
set -a
source /opt/homarr/.env
set +a
export DB_DIALECT='sqlite'
export AUTH_SECRET=$(openssl rand -base64 32)
export CRON_JOB_API_KEY=$(openssl rand -base64 32)
node /opt/homarr_db/migrations/$DB_DIALECT/migrate.cjs /opt/homarr_db/migrations/$DB_DIALECT
for dir in $(find /opt/homarr_db/migrations/migrations -mindepth 1 -maxdepth 1 -type d); do
dirname=$(basename "$dir")
mkdir -p "/opt/homarr_db/migrations/$dirname"
cp -r "$dir"/* "/opt/homarr_db/migrations/$dirname/" 2>/dev/null || true
done
export HOSTNAME=$(ip route get 1.1.1.1 | grep -oP 'src \K[^ ]+')
envsubst '${HOSTNAME}' < /etc/nginx/templates/nginx.conf > /etc/nginx/nginx.conf
nginx -g 'daemon off;' &
redis-server /opt/homarr/packages/redis/redis.conf &
node apps/tasks/tasks.cjs &
node apps/websocket/wssServer.cjs &
node apps/nextjs/server.js & PID=$!
wait $PID
EOF
chmod +x /opt/run_homarr.sh
mv /opt/homarr-data-backup/.env /opt/homarr/.env
cd /opt/homarr
$STD pnpm install --recursive --frozen-lockfile --shamefully-hoist
$STD pnpm build
cp /opt/homarr/apps/nextjs/next.config.ts .
cp /opt/homarr/apps/nextjs/package.json .
cp -r /opt/homarr/packages/db/migrations /opt/homarr_db/migrations
cp -r /opt/homarr/apps/nextjs/.next/standalone/* /opt/homarr
mkdir -p /appdata/redis
cp /opt/homarr/packages/redis/redis.conf /opt/homarr/redis.conf
rm /etc/nginx/nginx.conf
mkdir -p /etc/nginx/templates
cp /opt/homarr/nginx.conf /etc/nginx/templates/nginx.conf
msg_ok "Updated Homarr"
mkdir -p /opt/homarr/apps/cli
cp /opt/homarr/packages/cli/cli.cjs /opt/homarr/apps/cli/cli.cjs
echo $'#!/bin/bash\ncd /opt/homarr/apps/cli && node ./cli.cjs "$@"' >/usr/bin/homarr
chmod +x /usr/bin/homarr
mkdir /opt/homarr/build
cp ./node_modules/better-sqlite3/build/Release/better_sqlite3.node ./build/better_sqlite3.node
msg_ok "Updated ${APP}"
msg_info "Starting Services"
chmod +x /opt/homarr/run.sh
systemctl start homarr
systemctl start redis-server
msg_ok "Started Services"
msg_ok "Updated successfully!"
read -p "${TAB3}It's recommended to reboot the LXC after an update, would you like to reboot the LXC now ? (y/n): " choice
if [[ "$choice" =~ ^[Yy]$ ]]; then
reboot
fi
fi
exit
}

View File

@ -27,7 +27,6 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
if check_for_gh_release "hortusfox" "danielbrendel/hortusfox-web"; then
msg_info "Stopping Service"
systemctl stop apache2

View File

@ -13,7 +13,6 @@ var_disk="${var_disk:-4}"
var_os="${var_os:-debian}"
var_version="${var_version:-12}"
var_unprivileged="${var_unprivileged:-0}"
var_gpu="${var_gpu:-yes}"
header_info "$APP"
variables

View File

@ -13,7 +13,6 @@ var_disk="${var_disk:-2}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
var_gpu="${var_gpu:-yes}"
header_info "$APP"
variables

View File

@ -28,7 +28,7 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
if check_for_gh_release "invoiceninja" "invoiceninja/invoiceninja"; then
msg_info "Stopping Services"
systemctl stop supervisor nginx php8.4-fpm

View File

@ -28,7 +28,6 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit 1
fi
setup_mariadb
msg_info "Updating LXC"
$STD apt update

View File

@ -30,12 +30,11 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
PHP_VERSION="8.4" PHP_MODULE="mysql" PHP_APACHE="YES" setup_php
setup_composer
if check_for_gh_release "kimai" "kimai/kimai"; then
if check_for_gh_release "kimai" "kimai/kimai"; then
BACKUP_DIR="/opt/kimai_backup"
msg_info "Stopping Apache2"

View File

@ -1,81 +0,0 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2025 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://koel.dev/
APP="Koel"
var_tags="${var_tags:-music;streaming}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-2048}"
var_disk="${var_disk:-8}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /opt/koel ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "koel" "koel/koel"; then
msg_info "Stopping Services"
systemctl stop nginx php8.4-fpm
msg_ok "Stopped Services"
msg_info "Creating Backup"
mkdir -p /tmp/koel_backup
cp /opt/koel/.env /tmp/koel_backup/
cp -r /opt/koel/storage /tmp/koel_backup/ 2>/dev/null || true
cp -r /opt/koel/public/img /tmp/koel_backup/ 2>/dev/null || true
msg_ok "Created Backup"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "koel" "koel/koel" "prebuild" "latest" "/opt/koel" "koel-*.tar.gz"
msg_info "Restoring Data"
cp /tmp/koel_backup/.env /opt/koel/
cp -r /tmp/koel_backup/storage/* /opt/koel/storage/ 2>/dev/null || true
cp -r /tmp/koel_backup/img/* /opt/koel/public/img/ 2>/dev/null || true
rm -rf /tmp/koel_backup
msg_ok "Restored Data"
msg_info "Running Migrations"
cd /opt/koel
export COMPOSER_ALLOW_SUPERUSER=1
$STD composer install --no-interaction --no-dev --optimize-autoloader
$STD php artisan migrate --force
$STD php artisan config:clear
$STD php artisan cache:clear
$STD php artisan view:clear
$STD php artisan koel:init --no-assets --no-interaction
chown -R www-data:www-data /opt/koel
chmod -R 775 /opt/koel/storage
msg_ok "Ran Migrations"
msg_info "Starting Services"
systemctl start php8.4-fpm nginx
msg_ok "Started Services"
msg_ok "Updated Successfully"
fi
exit
}
start
build_container
description
msg_ok "Completed Successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}"

View File

@ -28,7 +28,7 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
if check_for_gh_release "leantime" "Leantime/leantime"; then
msg_info "Creating Backup"
mariadb-dump leantime >"/opt/${APP}_db_backup_$(date +%F).sql"

View File

@ -27,7 +27,6 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
msg_info "Updating LibreNMS"
su librenms
cd /opt/librenms

View File

@ -13,7 +13,6 @@ var_disk="${var_disk:-20}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
var_gpu="${var_gpu:-yes}"
header_info "$APP"
variables

View File

@ -20,17 +20,16 @@ color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /opt/limesurvey ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
header_info
check_container_storage
check_container_resources
if [[ ! -d /opt/limesurvey ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
msg_warn "Application is updated via Web Interface"
exit
msg_warn "Application is updated via Web Interface"
exit
}
start

View File

@ -28,7 +28,6 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
if check_for_gh_release "mmdl" "intri-in/manage-my-damn-life-nextjs"; then
msg_info "Stopping service"
systemctl stop mmdl

View File

@ -20,19 +20,18 @@ color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /etc/mysql/mariadb.conf.d ]]; then
msg_error "No ${APP} Installation Found!"
header_info
check_container_storage
check_container_resources
if [[ ! -d /etc/mysql/mariadb.conf.d ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
msg_info "Updating ${APP} LXC"
$STD apt update
$STD apt -y upgrade
msg_ok "Updated successfully!"
exit
fi
setup_mariadb
msg_info "Updating ${APP} LXC"
$STD apt update
$STD apt -y upgrade
msg_ok "Updated successfully!"
exit
}
start

View File

@ -13,7 +13,6 @@ var_disk="${var_disk:-4}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
var_gpu="${var_gpu:-yes}"
header_info "$APP"
variables

View File

@ -23,7 +23,7 @@ function update_script() {
header_info
check_container_storage
check_container_resources
if ! systemctl -q is-enabled miniflux 2>/dev/null; then
if [[ ! -f /etc/systemd/system/miniflux.service ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
@ -34,7 +34,7 @@ function update_script() {
msg_ok "Service Stopped"
fetch_and_deploy_gh_release "miniflux" "miniflux/v2" "binary" "latest"
msg_info "Updating Miniflux"
$STD miniflux -migrate -config-file /etc/miniflux.conf
msg_ok "Updated Miniflux"

View File

@ -27,7 +27,6 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs

View File

@ -13,7 +13,6 @@ var_disk="${var_disk:-8}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
var_gpu="${var_gpu:-yes}"
header_info "$APP"
variables
@ -21,17 +20,17 @@ color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -f /etc/systemd/system/motioneye.service ]]; then
msg_error "No ${APP} Installation Found!"
header_info
check_container_storage
check_container_resources
if [[ ! -f /etc/systemd/system/motioneye.service ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
msg_info "Updating ${APP} LXC"
$STD pip install motioneye --upgrade
msg_ok "Updated successfully!"
exit
fi
msg_info "Updating ${APP} LXC"
$STD pip install motioneye --upgrade
msg_ok "Updated successfully!"
exit
}
start

View File

@ -3,9 +3,9 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
# Copyright (c) 2021-2025 community-scripts ORG
# Author: vhsdream
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/scanopy/scanopy
# Source: https://github.com/netvisor-io/netvisor
APP="Scanopy"
APP="NetVisor"
var_tags="${var_tags:-analytics}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-3072}"
@ -29,74 +29,71 @@ function update_script() {
exit
fi
msg_info "Stopping services"
systemctl -q disable --now netvisor-daemon netvisor-server
msg_ok "Stopped services"
if check_for_gh_release "netvisor" "netvisor-io/netvisor"; then
msg_info "Stopping services"
systemctl stop netvisor-daemon netvisor-server
msg_ok "Stopped services"
NODE_VERSION="24" setup_nodejs
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "scanopy" "scanopy/scanopy" "tarball" "latest" "/opt/scanopy"
msg_info "Backing up configurations"
cp /opt/netvisor/.env /opt/netvisor.env.bak
if [[ -f /opt/netvisor/oidc.toml ]]; then
cp /opt/netvisor/oidc.toml /opt/netvisor.oidc.toml
fi
msg_ok "Backed up configurations"
if ! dpkg -l | grep -q "pkg-config"; then
$STD apt install -y pkg-config
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "netvisor" "netvisor-io/netvisor" "tarball" "latest" "/opt/netvisor"
if ! dpkg -l | grep -q "pkg-config"; then
$STD apt install -y pkg-config
fi
if ! dpkg -l | grep -q "libssl-dev"; then
$STD apt install -y libssl-dev
fi
TOOLCHAIN="$(grep "channel" /opt/netvisor/backend/rust-toolchain.toml | awk -F\" '{print $2}')"
RUST_TOOLCHAIN=$TOOLCHAIN setup_rust
mv /opt/netvisor.env.bak /opt/netvisor/.env
if [[ -f /opt/netvisor.oidc.toml ]]; then
mv /opt/netvisor.oidc.toml /opt/netvisor/oidc.toml
fi
LOCAL_IP="$(hostname -I | awk '{print $1}')"
if ! grep -q "PUBLIC_URL" /opt/netvisor/.env; then
sed -i "\|_PATH=|a\NETVISOR_PUBLIC_URL=http://${LOCAL_IP}:60072" /opt/netvisor/.env
fi
sed -i 's|_TARGET=.*$|_URL=http://127.0.0.1:60072|' /opt/netvisor/.env
msg_info "Creating frontend UI"
export PUBLIC_SERVER_HOSTNAME=default
export PUBLIC_SERVER_PORT=""
cd /opt/netvisor/ui
$STD npm ci --no-fund --no-audit
$STD npm run build
msg_ok "Created frontend UI"
msg_info "Building Netvisor-server (patience)"
cd /opt/netvisor/backend
$STD cargo build --release --bin server
mv ./target/release/server /usr/bin/netvisor-server
msg_ok "Built Netvisor-server"
msg_info "Building Netvisor-daemon"
$STD cargo build --release --bin daemon
cp ./target/release/daemon /usr/bin/netvisor-daemon
msg_ok "Built Netvisor-daemon"
sed -i -e 's|-target|-url|' \
-e 's| --server-port |:|' \
/etc/systemd/system/netvisor-daemon.service
sed -i '/^ \"server_target.*$/d' /root/.config/daemon/config.json
if ! grep -q "WorkingD" /etc/systemd/system/netvisor-server.service; then
sed -i '\|simple$|a\WorkingDirectory=/opt/netvisor/backend' /etc/systemd/system/netvisor-server.service
fi
systemctl daemon-reload
msg_info "Starting services"
systemctl start netvisor-server netvisor-daemon
msg_ok "Updated successfully!"
fi
if ! dpkg -l | grep -q "libssl-dev"; then
$STD apt install -y libssl-dev
fi
TOOLCHAIN="$(grep "channel" /opt/scanopy/backend/rust-toolchain.toml | awk -F\" '{print $2}')"
RUST_TOOLCHAIN=$TOOLCHAIN setup_rust
mv /opt/netvisor/.env /opt/scanopy/.env
if [[ -f /opt/netvisor/oidc.toml ]]; then
mv /opt/netvisor/oidc.toml /opt/scanopy/oidc.toml
fi
LOCAL_IP="$(hostname -I | awk '{print $1}')"
if ! grep -q "PUBLIC_URL" /opt/scanopy/.env; then
sed -i "\|_PATH=|a\NETVISOR_PUBLIC_URL=http://${LOCAL_IP}:60072" /opt/scanopy/.env
fi
sed -i 's|_TARGET=.*$|_URL=http://127.0.0.1:60072|' /opt/scanopy/.env
sed -i 's/NETVISOR/SCANOPY/g; s|netvisor/|scanopy/|' /opt/scanopy/.env
msg_info "Creating frontend UI"
export PUBLIC_SERVER_HOSTNAME=default
export PUBLIC_SERVER_PORT=""
cd /opt/scanopy/ui
$STD npm ci --no-fund --no-audit
$STD npm run build
msg_ok "Created frontend UI"
msg_info "Building Scanopy-server (patience)"
cd /opt/scanopy/backend
$STD cargo build --release --bin server
mv ./target/release/server /usr/bin/scanopy-server
msg_ok "Built Scanopy-server"
msg_info "Building Scanopy-daemon"
$STD cargo build --release --bin daemon
cp ./target/release/daemon /usr/bin/scanopy-daemon
msg_ok "Built Scanopy-daemon"
sed -i '/^ \"server_target.*$/d' /root/.config/daemon/config.json
sed -i -e 's|-target|-url|' \
-e 's| --server-port |:|' \
-e 's/NetVisor/Scanopy/' \
-e 's/netvisor/scanopy/' \
/etc/systemd/system/netvisor-daemon.service
mv /etc/systemd/system/netvisor-daemon.service /etc/systemd/system/scanopy-daemon.service
sed -i -e 's/NetVisor/Scanopy/' \
-e 's/netvisor/scanopy/g' \
/etc/systemd/system/netvisor-server.service
mv /etc/systemd/system/netvisor-server.service /etc/systemd/system/scanopy-server.service
systemctl daemon-reload
msg_info "Starting services"
systemctl -q enable --now scanopy-server scanopy-daemon
msg_ok "Updated successfully!"
sed -i 's/netvisor/scanopy/' /usr/bin/update
mv ~/NetVisor.creds ~/scanopy.creds
rm ~/.netvisor
rm -rf /opt/netvisor
exit
}

View File

@ -14,7 +14,6 @@ var_disk="${var_disk:-5}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
var_gpu="${var_gpu:-yes}"
header_info "$APP"
variables

View File

@ -13,7 +13,6 @@ var_disk="${var_disk:-8}"
var_os="${var_os:-ubuntu}"
var_version="${var_version:-24.04}"
var_unprivileged="${var_unprivileged:-0}"
var_gpu="${var_gpu:-yes}"
header_info "$APP"
variables

View File

@ -88,36 +88,22 @@ EOF
fi
if [ -x "/usr/bin/ollama" ]; then
msg_info "Checking for Ollama Update"
msg_info "Updating Ollama"
OLLAMA_VERSION=$(ollama -v | awk '{print $NF}')
RELEASE=$(curl -s https://api.github.com/repos/ollama/ollama/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}')
if [ "$OLLAMA_VERSION" != "$RELEASE" ]; then
msg_info "Ollama update available: v$OLLAMA_VERSION -> v$RELEASE"
msg_info "Downloading Ollama v$RELEASE \n"
curl -fS#LO https://ollama.com/download/ollama-linux-amd64.tgz
msg_ok "Download Complete"
if [ -f "ollama-linux-amd64.tgz" ]; then
msg_info "Stopping Ollama Service"
systemctl stop ollama
msg_ok "Stopped Service"
msg_info "Installing Ollama"
rm -rf /usr/lib/ollama
rm -rf /usr/bin/ollama
tar -C /usr -xzf ollama-linux-amd64.tgz
rm -rf ollama-linux-amd64.tgz
msg_ok "Installed Ollama"
msg_info "Starting Ollama Service"
systemctl start ollama
msg_ok "Started Service"
msg_ok "Ollama updated to version $RELEASE"
else
msg_error "Ollama download failed. Aborting update."
fi
msg_info "Stopping Service"
systemctl stop ollama
msg_ok "Stopped Service"
curl -fsSLO -C - https://ollama.com/download/ollama-linux-amd64.tgz
rm -rf /usr/lib/ollama
rm -rf /usr/bin/ollama
tar -C /usr -xzf ollama-linux-amd64.tgz
rm -rf ollama-linux-amd64.tgz
msg_info "Starting Service"
systemctl start ollama
msg_info "Started Service"
msg_ok "Ollama updated to version $RELEASE"
else
msg_ok "Ollama is already up to date."
fi

View File

@ -13,7 +13,6 @@ var_disk="${var_disk:-2}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
var_gpu="${var_gpu:-yes}"
header_info "$APP"
variables

View File

@ -33,23 +33,10 @@ function update_script() {
systemctl stop paperless-ai paperless-rag
msg_ok "Stopped Service"
msg_info "Backing up data"
cp -r /opt/paperless-ai/data /opt/paperless-ai-data-backup
msg_ok "Backed up data"
fetch_and_deploy_gh_release "paperless-ai" "clusterzx/paperless-ai"
msg_info "Restoring data"
cp -r /opt/paperless-ai-data-backup/* /opt/paperless-ai/data/
rm -rf /opt/paperless-ai-data-backup
msg_ok "Restored data"
msg_info "Updating Paperless-AI"
cd /opt/paperless-ai
if [[ ! -d /opt/paperless-ai/venv ]]; then
msg_info "Recreating Python venv"
$STD python3 -m venv /opt/paperless-ai/venv
fi
source /opt/paperless-ai/venv/bin/activate
$STD pip install --upgrade pip
$STD pip install --no-cache-dir -r requirements.txt

View File

@ -27,7 +27,6 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
msg_info "Updating $APP LXC"
$STD apt update
$STD apt upgrade -y

View File

@ -27,7 +27,6 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2)
if [[ "$CURRENT_PHP" != "8.3" ]]; then

View File

@ -27,7 +27,6 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2)
if [[ "$CURRENT_PHP" != "8.4" ]]; then

View File

@ -13,7 +13,6 @@ var_disk="${var_disk:-8}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
var_gpu="${var_gpu:-yes}"
header_info "$APP"
variables
@ -33,13 +32,6 @@ function update_script() {
systemctl stop photoprism
msg_ok "Stopped PhotoPrism"
if ! grep -q "photoprism/config/.env" ~/.bashrc 2>/dev/null; then
msg_info "Adding environment export for CLI tools"
echo '# Load PhotoPrism environment variables for CLI tools' >>~/.bashrc
echo 'export $(grep -v "^#" /opt/photoprism/config/.env | xargs)' >>~/.bashrc
msg_ok "Added environment export"
fi
fetch_and_deploy_gh_release "photoprism" "photoprism/photoprism" "prebuild" "latest" "/opt/photoprism" "*linux-amd64.tar.gz"
LIBHEIF_URL=$(curl -fsSL "https://dl.photoprism.app/dist/libheif/" | grep -oP "libheif-bookworm-amd64-v[0-9\.]+\.tar\.gz" | sort -V | tail -n 1)

View File

@ -27,7 +27,7 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
if check_for_gh_release "phpipam" "phpipam/phpipam"; then
msg_info "Stopping Service"
systemctl stop apache2

View File

@ -27,7 +27,6 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
if check_for_gh_release "plant-it" "MDeLuise/plant-it"; then
msg_info "Stopping Service"
systemctl stop plant-it

View File

@ -27,7 +27,6 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
if check_for_gh_release "projectsend" "projectsend/projectsend"; then
msg_info "Stopping Service"

View File

@ -27,7 +27,6 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2)
if [[ "$CURRENT_PHP" != "8.4" ]]; then

View File

@ -1,99 +0,0 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2025 community-scripts ORG
# Author: vhsdream
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/scanopy/scanopy
APP="Scanopy"
var_tags="${var_tags:-analytics}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-3072}"
var_disk="${var_disk:-6}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /opt/scanopy ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "scanopy" "scanopy-io/scanopy"; then
msg_info "Stopping services"
systemctl stop scanopy-daemon scanopy-server
msg_ok "Stopped services"
msg_info "Backing up configurations"
cp /opt/scanopy/.env /opt/scanopy.env.bak
if [[ -f /opt/scanopy/oidc.toml ]]; then
cp /opt/scanopy/oidc.toml /opt/scanopy.oidc.toml
fi
msg_ok "Backed up configurations"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "scanopy" "scanopy/scanopy" "tarball" "latest" "/opt/scanopy"
if ! dpkg -l | grep -q "pkg-config"; then
$STD apt install -y pkg-config
fi
if ! dpkg -l | grep -q "libssl-dev"; then
$STD apt install -y libssl-dev
fi
TOOLCHAIN="$(grep "channel" /opt/scanopy/backend/rust-toolchain.toml | awk -F\" '{print $2}')"
RUST_TOOLCHAIN=$TOOLCHAIN setup_rust
mv /opt/scanopy.env.bak /opt/scanopy/.env
if [[ -f /opt/scanopy.oidc.toml ]]; then
mv /opt/scanopy.oidc.toml /opt/scanopy/oidc.toml
fi
LOCAL_IP="$(hostname -I | awk '{print $1}')"
if ! grep -q "PUBLIC_URL" /opt/scanopy/.env; then
sed -i "\|_PATH=|a\scanopy_PUBLIC_URL=http://${LOCAL_IP}:60072" /opt/scanopy/.env
fi
sed -i 's|_TARGET=.*$|_URL=http://127.0.0.1:60072|' /opt/scanopy/.env
msg_info "Creating frontend UI"
export PUBLIC_SERVER_HOSTNAME=default
export PUBLIC_SERVER_PORT=""
cd /opt/scanopy/ui
$STD npm ci --no-fund --no-audit
$STD npm run build
msg_ok "Created frontend UI"
msg_info "Building scanopy-server (patience)"
cd /opt/scanopy/backend
$STD cargo build --release --bin server
mv ./target/release/server /usr/bin/scanopy-server
msg_ok "Built scanopy-server"
msg_info "Building scanopy-daemon"
$STD cargo build --release --bin daemon
cp ./target/release/daemon /usr/bin/scanopy-daemon
msg_ok "Built scanopy-daemon"
msg_info "Starting services"
systemctl start scanopy-server scanopy-daemon
msg_ok "Updated successfully!"
fi
exit
}
start
build_container
description
msg_ok "Completed Successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:60072${CL}"
echo -e "${INFO}${YW} Then create your account, and run the 'configure_daemon.sh' script to setup the daemon.${CL}"

View File

@ -13,7 +13,6 @@ var_disk="${var_disk:-8}"
var_os="${var_os:-ubuntu}"
var_version="${var_version:-24.04}"
var_unprivileged="${var_unprivileged:-0}"
var_gpu="${var_gpu:-yes}"
header_info "$APP"
variables
@ -28,7 +27,6 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
msg_info "Updating Shinobi"
cd /opt/Shinobi
$STD sh UPDATE.sh

View File

@ -27,7 +27,6 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
if ! grep -q "client_max_body_size[[:space:]]\+100M;" /etc/nginx/conf.d/snipeit.conf; then
sed -i '/index index.php;/i \ client_max_body_size 100M;' /etc/nginx/conf.d/snipeit.conf
fi

View File

@ -27,29 +27,32 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
RELEASE=$(curl -fsSL https://github.com/Donkie/Spoolman/releases/latest | grep "title>Release" | cut -d " " -f 4)
if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then
if check_for_gh_release "spoolman" "Donkie/Spoolman"; then
msg_info "Stopping Service"
systemctl stop spoolman
msg_ok "Stopped Service"
msg_info "Creating Backup"
[ -d /opt/spoolman_bak ] && rm -rf /opt/spoolman_bak
mv /opt/spoolman /opt/spoolman_bak
msg_ok "Created Backup"
fetch_and_deploy_gh_release "spoolman" "Donkie/Spoolman" "prebuild" "latest" "/opt/spoolman" "spoolman.zip"
msg_info "Updating Spoolman"
cd /opt/spoolman
msg_info "Updating ${APP} to ${RELEASE}"
cd /opt
rm -rf spoolman_bak
mv spoolman spoolman_bak
curl -fsSL "https://github.com/Donkie/Spoolman/releases/download/${RELEASE}/spoolman.zip" -o $(basename "https://github.com/Donkie/Spoolman/releases/download/${RELEASE}/spoolman.zip")
$STD unzip spoolman.zip -d spoolman
cd spoolman
$STD pip3 install -r requirements.txt
cp /opt/spoolman_bak/.env /opt/spoolman
msg_ok "Updated Spoolman"
curl -fsSL "https://raw.githubusercontent.com/Donkie/Spoolman/master/.env.example" -o ".env"
rm -rf /opt/spoolman.zip
echo "${RELEASE}" >/opt/${APP}_version.txt
msg_ok "Updated ${APP} to ${RELEASE}"
msg_info "Starting Service"
systemctl start spoolman
msg_ok "Started Service"
msg_ok "Updated successfully!"
else
msg_ok "No update required. ${APP} is already at ${RELEASE}"
fi
exit
}

View File

@ -13,7 +13,6 @@ var_disk="${var_disk:-4}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
var_gpu="${var_gpu:-yes}"
header_info "$APP"
variables

View File

@ -40,9 +40,7 @@ function update_script() {
msg_error "Backup failed: /usr/local/share/tunarr does not exist"
fi
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "prebuild" "latest" "/opt/tunarr" "*linux-x64.tar.gz"
cd /opt/tunarr
mv tunarr* tunarr
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "singlefile" "latest" "/opt/tunarr" "*linux-x64"
msg_info "Starting Service"
systemctl start tunarr

View File

@ -13,7 +13,6 @@ var_disk="${var_disk:-8}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
var_gpu="${var_gpu:-yes}"
header_info "$APP"
variables

View File

@ -37,8 +37,8 @@ function update_script() {
msg_info "Updating Umami"
cd /opt/umami
$STD pnpm install
$STD pnpm run build
$STD yarn install
$STD yarn run build
msg_ok "Updated Umami"
msg_info "Starting Service"

View File

@ -28,7 +28,7 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
if check_for_gh_release "wallabag" "wallabag/wallabag"; then
msg_info "Stopping Services"
systemctl stop nginx php8.3-fpm

View File

@ -27,7 +27,7 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
if check_for_gh_release "wavelog" "wavelog/wavelog"; then
msg_info "Stopping Services"
systemctl stop apache2

View File

@ -27,7 +27,6 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
setup_mariadb
msg_error "Wordpress should be updated via the user interface."
exit
}

View File

@ -67,7 +67,7 @@ function update_script() {
rm -Rf /etc/apt/sources.list.d/zabbix.list
cd /tmp
ZABBIX_DEB_URL="https://repo.zabbix.com/zabbix/${ZABBIX_VERSION}/debian/pool/main/z/zabbix-release/zabbix-release_latest+debian13_all.deb"
ZABBIX_DEB_URL="https://repo.zabbix.com/zabbix/${ZABBIX_VERSION}/release/debian/pool/main/z/zabbix-release/zabbix-release_latest+debian13_all.deb"
curl -fsSL "$ZABBIX_DEB_URL" -o /tmp/zabbix-release_latest+debian13_all.deb
$STD dpkg -i zabbix-release_latest+debian13_all.deb
rm -rf /tmp/zabbix-release_latest+debian13_all.deb

View File

@ -434,24 +434,12 @@ var_unprivileged=1 # 0=privileged, 1=unprivileged
#### Network
```bash
var_brg=vmbr0 # Bridge interface
var_net=dhcp # dhcp, static IP/CIDR, or IP range (see below)
var_gateway=192.168.1.1 # Default gateway (required for static IP)
var_net=veth # Network driver
var_gateway=192.168.1.1 # Default gateway
var_mtu=1500 # MTU size
var_vlan=100 # VLAN ID
```
#### IP Range Scanning
You can specify an IP range instead of a static IP. The system will ping each IP in the range and automatically assign the first free IP:
```bash
# Format: START_IP/CIDR-END_IP/CIDR
var_net=192.168.1.100/24-192.168.1.200/24
var_gateway=192.168.1.1
```
This is useful for automated deployments where you want static IPs but don't want to track which IPs are already in use.
#### System
```bash
var_hostname=pihole # Container name

View File

@ -122,31 +122,6 @@ var_verbose=no \
echo "✓ Container deployed successfully"
```
### Using IP Range Scan for Automatic IP Assignment
Instead of manually specifying static IPs, you can define an IP range. The system will automatically ping each IP and assign the first free one:
```bash
#!/bin/bash
# deploy-with-ip-scan.sh - Auto-assign first free IP from range
var_unprivileged=1 \
var_cpu=4 \
var_ram=4096 \
var_hostname=web-server \
var_net=192.168.1.100/24-192.168.1.150/24 \
var_gateway=192.168.1.1 \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/debian.sh)"
# The script will:
# 1. Ping 192.168.1.100 - if responds, skip
# 2. Ping 192.168.1.101 - if responds, skip
# 3. Continue until first IP that doesn't respond
# 4. Assign that IP to the container
```
> **Note**: IP range format is `START_IP/CIDR-END_IP/CIDR`. Both sides must include the same CIDR notation.
### Using App Defaults
**Step 1: Create defaults once (interactive)**

5
frontend/.prettierignore Normal file
View File

@ -0,0 +1,5 @@
dist
node_modules
.next
build
.contentlayer

3
frontend/.prettierrc Normal file
View File

@ -0,0 +1,3 @@
{
"plugins": ["prettier-plugin-tailwindcss", "prettier-plugin-organize-imports"]
}

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2024-Present Bram Suurd
Copyright (c) 2024 Bram Suurd
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

663
frontend/bun.lock generated

File diff suppressed because it is too large Load Diff

104
frontend/package.json generated
View File

@ -16,70 +16,80 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-accordion": "^1.2.3",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-dropdown-menu": "^2.1.6",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-label": "^2.1.8",
"@radix-ui/react-navigation-menu": "^1.2.14",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-navigation-menu": "^1.2.5",
"@radix-ui/react-popover": "^1.1.6",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-tooltip": "^1.2.8",
"@tanstack/react-query": "^5.90.12",
"chart.js": "^4.5.1",
"@radix-ui/react-select": "^2.1.6",
"@radix-ui/react-separator": "^1.1.2",
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-switch": "^1.1.3",
"@radix-ui/react-tabs": "^1.1.3",
"@radix-ui/react-tooltip": "^1.2.0",
"@tanstack/react-query": "^5.71.1",
"chart.js": "^4.4.8",
"chartjs-plugin-datalabels": "^2.2.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"date-fns": "^4.1.0",
"framer-motion": "^12.23.26",
"framer-motion": "^11.18.2",
"fuse.js": "^7.1.0",
"lucide-react": "^0.561.0",
"lucide-react": "^0.554.0",
"mini-svg-data-uri": "^1.4.4",
"motion": "^12.23.26",
"next": "15.5.8",
"next-themes": "^0.4.6",
"nuqs": "^2.8.5",
"react": "19.2.3",
"react-chartjs-2": "^5.3.1",
"react-code-blocks": "^0.1.6",
"react-datepicker": "^9.0.0",
"react-day-picker": "^9.12.0",
"react-dom": "19.2.3",
"motion": "^12.23.12",
"next": "15.5.7",
"next-themes": "^0.4.4",
"nuqs": "^2.4.1",
"prettier-plugin-organize-imports": "^4.1.0",
"react": "19.0.0",
"react-chartjs-2": "^5.3.0",
"react-day-picker": "^9.4.3",
"react-dom": "19.0.0",
"react-icons": "^5.5.0",
"react-use-measure": "^2.1.7",
"recharts": "3.6.0",
"sharp": "^0.34.5",
"sonner": "^2.0.7",
"tailwind-merge": "^3.4.0",
"zod": "^4.2.1"
"recharts": "2.15.4",
"sharp": "^0.33.5",
"sonner": "^1.7.4",
"tailwind-merge": "^2.6.0",
"zod": "^3.24.2"
},
"devDependencies": {
"@antfu/eslint-config": "^6.7.1",
"@eslint-react/eslint-plugin": "^2.3.13",
"@next/eslint-plugin-next": "^15.5.8",
"@tanstack/eslint-plugin-query": "^5.91.2",
"@types/node": "^25.0.2",
"@antfu/eslint-config": "^4.16.1",
"@eslint-react/eslint-plugin": "^1.52.2",
"@next/eslint-plugin-next": "^15.3.4",
"@tanstack/eslint-plugin-query": "^5.68.0",
"@types/node": "^22.13.16",
"@types/react": "npm:types-react@19.0.0-rc.1",
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
"@typescript-eslint/eslint-plugin": "^8.50.0",
"@typescript-eslint/parser": "^8.50.0",
"@vitejs/plugin-react": "^5.1.2",
"eslint": "^9.39.2",
"eslint-config-next": "15.5.8",
"eslint-plugin-format": "^1.1.0",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.25",
"jsdom": "^27.3.0",
"postcss": "^8.5.6",
"@typescript-eslint/eslint-plugin": "^8.29.0",
"@typescript-eslint/parser": "^8.29.0",
"@vitejs/plugin-react": "^4.3.4",
"eslint": "^9.23.0",
"eslint-config-next": "15.0.2",
"eslint-plugin-format": "^1.0.1",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20",
"jsdom": "^25.0.1",
"postcss": "^8.5.3",
"prettier": "^3.5.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"tailwindcss": "^3.4.17",
"tailwindcss-animate": "^1.0.7",
"tailwindcss-animated": "^1.1.2",
"typescript": "^5.9.3"
"typescript": "^5.8.2",
"vite-tsconfig-paths": "^5.1.4"
},
"overrides": {
"@types/react": "npm:types-react@19.0.0-rc.1",
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
"date-fns": "^4.1.0",
"react": "19.0.0",
"react-dom": "19.0.0"
}
}

View File

@ -1,59 +0,0 @@
{
"name": "AdGuardHome-Sync",
"slug": "adguardhome-sync",
"categories": [
5
],
"date_created": "2025-12-13",
"type": "addon",
"updateable": true,
"privileged": false,
"interface_port": 8080,
"documentation": "https://github.com/bakito/adguardhome-sync",
"website": "https://github.com/bakito/adguardhome-sync",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/adguardhome-sync.webp",
"config_path": "/opt/adguardhome-sync/adguardhome-sync.yaml",
"description": "Synchronize AdGuardHome config to one or multiple replica instances. Syncs General Settings, Filters, Rewrites, Services, Clients, DNS Config, DHCP Config and Theme.",
"install_methods": [
{
"type": "default",
"script": "tools/addon/adguardhome-sync.sh",
"resources": {
"cpu": null,
"ram": null,
"hdd": null,
"os": null,
"version": null
}
},
{
"type": "alpine",
"script": "tools/addon/adguardhome-sync.sh",
"resources": {
"cpu": null,
"ram": null,
"hdd": null,
"os": null,
"version": null
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"notes": [
{
"text": "Edit /opt/adguardhome-sync/adguardhome-sync.yaml to configure your AdGuardHome instances",
"type": "info"
},
{
"text": "Origin = Primary instance, Replicas = Instances to sync to",
"type": "info"
},
{
"text": "Update with: update_adguardhome-sync",
"type": "info"
}
]
}

View File

@ -2,7 +2,7 @@
"name": "Coolify",
"slug": "coolify",
"categories": [
3
6
],
"date_created": "2025-12-09",
"type": "ct",

View File

@ -2,7 +2,7 @@
"name": "Dokploy",
"slug": "dokploy",
"categories": [
3
6
],
"date_created": "2025-12-09",
"type": "ct",

View File

@ -2,7 +2,7 @@
"name": "Glance",
"slug": "glance",
"categories": [
10
9
],
"date_created": "2024-12-02",
"type": "ct",

View File

@ -23,7 +23,7 @@
"ram": 512,
"hdd": 2,
"os": "debian",
"version": "13"
"version": "12"
}
}
],

View File

@ -19,8 +19,8 @@
"type": "default",
"script": "ct/homarr.sh",
"resources": {
"cpu": 2,
"ram": 1024,
"cpu": 3,
"ram": 6144,
"hdd": 8,
"os": "debian",
"version": "13"

View File

@ -1,48 +0,0 @@
{
"name": "Koel",
"slug": "koel",
"categories": [
13
],
"date_created": "2025-12-15",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 80,
"documentation": "https://docs.koel.dev/",
"config_path": "/opt/koel/.env",
"website": "https://koel.dev/",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/koel-light.webp",
"description": "Koel is a simple web-based personal audio streaming service written in Vue and Laravel. It supports multiple users, audio visualization, smart playlists, YouTube integration, and Last.fm scrobbling.",
"install_methods": [
{
"type": "default",
"script": "ct/koel.sh",
"resources": {
"cpu": 2,
"ram": 2048,
"hdd": 8,
"os": "Debian",
"version": "13"
}
}
],
"default_credentials": {
"username": "admin@koel.dev",
"password": "KoelIsCool"
},
"notes": [
{
"text": "Media files should be placed in /opt/koel_media",
"type": "info"
},
{
"text": "Database credentials are stored in ~/koel.creds",
"type": "info"
},
{
"text": "Music library is scanned hourly via cron job",
"type": "info"
}
]
}

View File

@ -1,6 +1,6 @@
{
"name": "Scanopy",
"slug": "scanopy",
"name": "NetVisor",
"slug": "netvisor",
"categories": [
9
],
@ -9,15 +9,15 @@
"updateable": true,
"privileged": false,
"interface_port": 60072,
"documentation": "https://github.com/scanopy/scanopy",
"config_path": "/opt/scanopy/.env, OIDC: /opt/scanopy/oidc.toml",
"website": "https://scanopy.net",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/scanopy.webp",
"documentation": "https://github.com/netvisor-io/netvisor",
"config_path": "/opt/netvisor/.env, OIDC: /opt/netvisor/oidc.toml",
"website": "https://netvisor.io",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/png/netvisor.png",
"description": "Automatically discover and visually document network infrastructure",
"install_methods": [
{
"type": "default",
"script": "ct/scanopy.sh",
"script": "ct/netvisor.sh",
"resources": {
"cpu": 2,
"ram": 3072,

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@ import { z } from "zod";
export const InstallMethodSchema = z.object({
type: z.enum(["default", "alpine"], {
message: "Type must be either 'default' or 'alpine'",
errorMap: () => ({ message: "Type must be either 'default' or 'alpine'" }),
}),
script: z.string().min(1, "Script content cannot be empty"),
resources: z.object({
@ -25,7 +25,7 @@ export const ScriptSchema = z.object({
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"),
type: z.enum(["vm", "ct", "pve", "addon", "turnkey"], {
message: "Type must be either 'vm', 'ct', 'pve', 'addon' or 'turnkey'",
errorMap: () => ({ message: "Type must be either 'vm', 'ct', 'pve', 'addon' or 'turnkey'" }),
}),
updateable: z.boolean(),
privileged: z.boolean(),

View File

@ -62,7 +62,7 @@ export default function JSONGenerator() {
useEffect(() => {
fetchCategories()
.then(setCategories)
.catch((error) => console.error("Error fetching categories:", error));
.catch(error => console.error("Error fetching categories:", error));
}, []);
const updateScript = useCallback((key: keyof Script, value: Script[keyof Script]) => {
@ -75,11 +75,14 @@ export default function JSONGenerator() {
if (updated.type === "pve") {
scriptPath = `tools/pve/${updated.slug}.sh`;
} else if (updated.type === "addon") {
}
else if (updated.type === "addon") {
scriptPath = `tools/addon/${updated.slug}.sh`;
} else if (method.type === "alpine") {
}
else if (method.type === "alpine") {
scriptPath = `${updated.type}/alpine-${updated.slug}.sh`;
} else {
}
else {
scriptPath = `${updated.type}/${updated.slug}.sh`;
}
@ -142,9 +145,12 @@ export default function JSONGenerator() {
</AlertDescription>
{zodErrors && (
<div className="mt-2 space-y-1">
{zodErrors.issues.map((error, index) => (
{zodErrors.errors.map((error, index) => (
<AlertDescription key={index} className="p-1 text-red-500">
{error.path.join(".")} -{error.message}
{error.path.join(".")}
{" "}
-
{error.message}
</AlertDescription>
))}
</div>
@ -162,25 +168,31 @@ export default function JSONGenerator() {
<div className="grid grid-cols-2 gap-4">
<div>
<Label>
Name <span className="text-red-500">*</span>
Name
{" "}
<span className="text-red-500">*</span>
</Label>
<Input placeholder="Example" value={script.name} onChange={(e) => updateScript("name", e.target.value)} />
<Input placeholder="Example" value={script.name} onChange={e => updateScript("name", e.target.value)} />
</div>
<div>
<Label>
Slug <span className="text-red-500">*</span>
Slug
{" "}
<span className="text-red-500">*</span>
</Label>
<Input placeholder="example" value={script.slug} onChange={(e) => updateScript("slug", e.target.value)} />
<Input placeholder="example" value={script.slug} onChange={e => updateScript("slug", e.target.value)} />
</div>
</div>
<div>
<Label>
Logo <span className="text-red-500">*</span>
Logo
{" "}
<span className="text-red-500">*</span>
</Label>
<Input
placeholder="Full logo URL"
value={script.logo || ""}
onChange={(e) => updateScript("logo", e.target.value || null)}
onChange={e => updateScript("logo", e.target.value || null)}
/>
</div>
<div>
@ -188,17 +200,19 @@ export default function JSONGenerator() {
<Input
placeholder="Path to config file"
value={script.config_path || ""}
onChange={(e) => updateScript("config_path", e.target.value || null)}
onChange={e => updateScript("config_path", e.target.value || null)}
/>
</div>
<div>
<Label>
Description <span className="text-red-500">*</span>
Description
{" "}
<span className="text-red-500">*</span>
</Label>
<Textarea
placeholder="Example"
value={script.description}
onChange={(e) => updateScript("description", e.target.value)}
onChange={e => updateScript("description", e.target.value)}
/>
</div>
<Categories script={script} setScript={setScript} categories={categories} />
@ -227,7 +241,7 @@ export default function JSONGenerator() {
</div>
<div className="flex flex-col gap-2 w-full">
<Label>Type</Label>
<Select value={script.type} onValueChange={(value) => updateScript("type", value)}>
<Select value={script.type} onValueChange={value => updateScript("type", value)}>
<SelectTrigger className="flex-1">
<SelectValue placeholder="Type" />
</SelectTrigger>
@ -242,30 +256,29 @@ export default function JSONGenerator() {
</div>
<div className="w-full flex gap-5">
<div className="flex items-center space-x-2">
<Switch checked={script.updateable} onCheckedChange={(checked) => updateScript("updateable", checked)} />
<Switch checked={script.updateable} onCheckedChange={checked => updateScript("updateable", checked)} />
<label>Updateable</label>
</div>
<div className="flex items-center space-x-2">
<Switch checked={script.privileged} onCheckedChange={(checked) => updateScript("privileged", checked)} />
<Switch checked={script.privileged} onCheckedChange={checked => updateScript("privileged", checked)} />
<label>Privileged</label>
</div>
<div className="flex items-center space-x-2">
<Switch
checked={script.disable || false}
onCheckedChange={(checked) => updateScript("disable", checked)}
/>
<Switch checked={script.disable || false} onCheckedChange={checked => updateScript("disable", checked)} />
<label>Disabled</label>
</div>
</div>
{script.disable && (
<div>
<Label>
Disable Description <span className="text-red-500">*</span>
Disable Description
{" "}
<span className="text-red-500">*</span>
</Label>
<Textarea
placeholder="Explain why this script is disabled..."
value={script.disable_description || ""}
onChange={(e) => updateScript("disable_description", e.target.value)}
onChange={e => updateScript("disable_description", e.target.value)}
/>
</div>
)}
@ -273,18 +286,18 @@ export default function JSONGenerator() {
placeholder="Interface Port"
type="number"
value={script.interface_port || ""}
onChange={(e) => updateScript("interface_port", e.target.value ? Number(e.target.value) : null)}
onChange={e => updateScript("interface_port", e.target.value ? Number(e.target.value) : null)}
/>
<div className="flex gap-2">
<Input
placeholder="Website URL"
value={script.website || ""}
onChange={(e) => updateScript("website", e.target.value || null)}
onChange={e => updateScript("website", e.target.value || null)}
/>
<Input
placeholder="Documentation URL"
value={script.documentation || ""}
onChange={(e) => updateScript("documentation", e.target.value || null)}
onChange={e => updateScript("documentation", e.target.value || null)}
/>
</div>
<InstallMethod script={script} setScript={setScript} setIsValid={setIsValid} setZodErrors={setZodErrors} />
@ -292,22 +305,20 @@ export default function JSONGenerator() {
<Input
placeholder="Username"
value={script.default_credentials.username || ""}
onChange={(e) =>
onChange={e =>
updateScript("default_credentials", {
...script.default_credentials,
username: e.target.value || null,
})
}
})}
/>
<Input
placeholder="Password"
value={script.default_credentials.password || ""}
onChange={(e) =>
onChange={e =>
updateScript("default_credentials", {
...script.default_credentials,
password: e.target.value || null,
})
}
})}
/>
<Note script={script} setScript={setScript} setIsValid={setIsValid} setZodErrors={setZodErrors} />
</form>

View File

@ -1,45 +1,50 @@
"use client";
"use client"
import * as React from "react";
import * as RechartsPrimitive from "recharts";
import * as React from "react"
import * as RechartsPrimitive from "recharts"
import { cn } from "@/lib/utils";
import { cn } from "@/lib/utils"
// Format: { THEME_NAME: CSS_SELECTOR }
const THEMES = { light: "", dark: ".dark" } as const;
const THEMES = { light: "", dark: ".dark" } as const
export type ChartConfig = {
[k in string]: {
label?: React.ReactNode;
icon?: React.ComponentType;
} & ({ color?: string; theme?: never } | { color?: never; theme: Record<keyof typeof THEMES, string> });
};
label?: React.ReactNode
icon?: React.ComponentType
} & (
| { color?: string; theme?: never }
| { color?: never; theme: Record<keyof typeof THEMES, string> }
)
}
type ChartContextProps = {
config: ChartConfig;
};
config: ChartConfig
}
const ChartContext = React.createContext<ChartContextProps | null>(null);
const ChartContext = React.createContext<ChartContextProps | null>(null)
function useChart() {
const context = React.useContext(ChartContext);
const context = React.useContext(ChartContext)
if (!context) {
throw new Error("useChart must be used within a <ChartContainer />");
throw new Error("useChart must be used within a <ChartContainer />")
}
return context;
return context
}
const ChartContainer = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div"> & {
config: ChartConfig;
children: React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>["children"];
config: ChartConfig
children: React.ComponentProps<
typeof RechartsPrimitive.ResponsiveContainer
>["children"]
}
>(({ id, className, children, config, ...props }, ref) => {
const uniqueId = React.useId();
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
const uniqueId = React.useId()
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
return (
<ChartContext.Provider value={{ config }}>
@ -53,18 +58,22 @@ const ChartContainer = React.forwardRef<
{...props}
>
<ChartStyle id={chartId} config={config} />
<RechartsPrimitive.ResponsiveContainer>{children}</RechartsPrimitive.ResponsiveContainer>
<RechartsPrimitive.ResponsiveContainer>
{children}
</RechartsPrimitive.ResponsiveContainer>
</div>
</ChartContext.Provider>
);
});
ChartContainer.displayName = "Chart";
)
})
ChartContainer.displayName = "Chart"
const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
const colorConfig = Object.entries(config).filter(([, config]) => config.theme || config.color);
const colorConfig = Object.entries(config).filter(
([, config]) => config.theme || config.color
)
if (!colorConfig.length) {
return null;
return null
}
return (
@ -76,8 +85,10 @@ const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
${prefix} [data-chart=${id}] {
${colorConfig
.map(([key, itemConfig]) => {
const color = itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.color;
return color ? ` --color-${key}: ${color};` : null;
const color =
itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
itemConfig.color
return color ? ` --color-${key}: ${color};` : null
})
.join("\n")}
}
@ -86,37 +97,20 @@ ${colorConfig
.join("\n"),
}}
/>
);
};
)
}
const ChartTooltip = RechartsPrimitive.Tooltip;
type TooltipPayloadItem = {
value?: string | number;
name?: string;
dataKey?: string | number;
payload?: Record<string, unknown>;
color?: string;
fill?: string;
type?: string;
};
const ChartTooltip = RechartsPrimitive.Tooltip
const ChartTooltipContent = React.forwardRef<
HTMLDivElement,
Omit<React.ComponentProps<typeof RechartsPrimitive.Tooltip>, "content"> &
React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
React.ComponentProps<"div"> & {
active?: boolean;
payload?: TooltipPayloadItem[];
hideLabel?: boolean;
hideIndicator?: boolean;
indicator?: "line" | "dot" | "dashed";
nameKey?: string;
labelKey?: string;
label?: string;
labelFormatter?: (value: any, payload: TooltipPayloadItem[]) => React.ReactNode;
labelClassName?: string;
formatter?: (value: any, name: string, item: TooltipPayloadItem, index: number, payload: any) => React.ReactNode;
color?: string;
hideLabel?: boolean
hideIndicator?: boolean
indicator?: "line" | "dot" | "dashed"
nameKey?: string
labelKey?: string
}
>(
(
@ -137,37 +131,49 @@ const ChartTooltipContent = React.forwardRef<
},
ref
) => {
const { config } = useChart();
const { config } = useChart()
const tooltipLabel = React.useMemo(() => {
if (hideLabel || !payload?.length) {
return null;
return null
}
const [item] = payload;
const key = `${labelKey || item?.dataKey || item?.name || "value"}`;
const itemConfig = getPayloadConfigFromPayload(config, item, key);
const [item] = payload
const key = `${labelKey || item?.dataKey || item?.name || "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
const value =
!labelKey && typeof label === "string"
? config[label as keyof typeof config]?.label || label
: itemConfig?.label;
: itemConfig?.label
if (labelFormatter) {
return <div className={cn("font-medium", labelClassName)}>{labelFormatter(value, payload)}</div>;
return (
<div className={cn("font-medium", labelClassName)}>
{labelFormatter(value, payload)}
</div>
)
}
if (!value) {
return null;
return null
}
return <div className={cn("font-medium", labelClassName)}>{value}</div>;
}, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey]);
return <div className={cn("font-medium", labelClassName)}>{value}</div>
}, [
label,
labelFormatter,
payload,
hideLabel,
labelClassName,
config,
labelKey,
])
if (!active || !payload?.length) {
return null;
return null
}
const nestLabel = payload.length === 1 && indicator !== "dot";
const nestLabel = payload.length === 1 && indicator !== "dot"
return (
<div
@ -182,9 +188,9 @@ const ChartTooltipContent = React.forwardRef<
{payload
.filter((item) => item.type !== "none")
.map((item, index) => {
const key = `${nameKey || item.name || item.dataKey || "value"}`;
const itemConfig = getPayloadConfigFromPayload(config, item, key);
const indicatorColor = color || item.payload?.fill || item.color;
const key = `${nameKey || item.name || item.dataKey || "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
const indicatorColor = color || item.payload.fill || item.color
return (
<div
@ -203,12 +209,16 @@ const ChartTooltipContent = React.forwardRef<
) : (
!hideIndicator && (
<div
className={cn("shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]", {
"h-2.5 w-2.5": indicator === "dot",
"w-1": indicator === "line",
"w-0 border-[1.5px] border-dashed bg-transparent": indicator === "dashed",
"my-0.5": nestLabel && indicator === "dashed",
})}
className={cn(
"shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]",
{
"h-2.5 w-2.5": indicator === "dot",
"w-1": indicator === "line",
"w-0 border-[1.5px] border-dashed bg-transparent":
indicator === "dashed",
"my-0.5": nestLabel && indicator === "dashed",
}
)}
style={
{
"--color-bg": indicatorColor,
@ -226,7 +236,9 @@ const ChartTooltipContent = React.forwardRef<
>
<div className="grid gap-1.5">
{nestLabel ? tooltipLabel : null}
<span className="text-muted-foreground">{itemConfig?.label || item.name}</span>
<span className="text-muted-foreground">
{itemConfig?.label || item.name}
</span>
</div>
{item.value && (
<span className="font-mono font-medium tabular-nums text-foreground">
@ -237,98 +249,121 @@ const ChartTooltipContent = React.forwardRef<
</>
)}
</div>
);
)
})}
</div>
</div>
);
)
}
);
ChartTooltipContent.displayName = "ChartTooltip";
)
ChartTooltipContent.displayName = "ChartTooltip"
const ChartLegend = RechartsPrimitive.Legend;
type LegendPayloadItem = {
value?: string;
type?: string;
dataKey?: string | number;
color?: string;
};
const ChartLegend = RechartsPrimitive.Legend
const ChartLegendContent = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div"> & {
payload?: LegendPayloadItem[];
verticalAlign?: "top" | "bottom";
hideIcon?: boolean;
nameKey?: string;
React.ComponentProps<"div"> &
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
hideIcon?: boolean
nameKey?: string
}
>(
(
{ className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },
ref
) => {
const { config } = useChart()
if (!payload?.length) {
return null
}
return (
<div
ref={ref}
className={cn(
"flex items-center justify-center gap-4",
verticalAlign === "top" ? "pb-3" : "pt-3",
className
)}
>
{payload
.filter((item) => item.type !== "none")
.map((item) => {
const key = `${nameKey || item.dataKey || "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
return (
<div
key={item.value}
className={cn(
"flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
)}
>
{itemConfig?.icon && !hideIcon ? (
<itemConfig.icon />
) : (
<div
className="h-2 w-2 shrink-0 rounded-[2px]"
style={{
backgroundColor: item.color,
}}
/>
)}
{itemConfig?.label}
</div>
)
})}
</div>
)
}
>(({ className, hideIcon = false, payload, verticalAlign = "bottom", nameKey }, ref) => {
const { config } = useChart();
if (!payload?.length) {
return null;
}
return (
<div
ref={ref}
className={cn("flex items-center justify-center gap-4", verticalAlign === "top" ? "pb-3" : "pt-3", className)}
>
{payload
.filter((item) => item.type !== "none")
.map((item) => {
const key = `${nameKey || item.dataKey || "value"}`;
const itemConfig = getPayloadConfigFromPayload(config, item, key);
return (
<div
key={item.value}
className={cn("flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground")}
>
{itemConfig?.icon && !hideIcon ? (
<itemConfig.icon />
) : (
<div
className="h-2 w-2 shrink-0 rounded-[2px]"
style={{
backgroundColor: item.color,
}}
/>
)}
{itemConfig?.label}
</div>
);
})}
</div>
);
});
ChartLegendContent.displayName = "ChartLegend";
)
ChartLegendContent.displayName = "ChartLegend"
// Helper to extract item config from a payload.
function getPayloadConfigFromPayload(config: ChartConfig, payload: unknown, key: string) {
function getPayloadConfigFromPayload(
config: ChartConfig,
payload: unknown,
key: string
) {
if (typeof payload !== "object" || payload === null) {
return undefined;
return undefined
}
const payloadPayload =
"payload" in payload && typeof payload.payload === "object" && payload.payload !== null
"payload" in payload &&
typeof payload.payload === "object" &&
payload.payload !== null
? payload.payload
: undefined;
: undefined
let configLabelKey: string = key;
let configLabelKey: string = key
if (key in payload && typeof payload[key as keyof typeof payload] === "string") {
configLabelKey = payload[key as keyof typeof payload] as string;
if (
key in payload &&
typeof payload[key as keyof typeof payload] === "string"
) {
configLabelKey = payload[key as keyof typeof payload] as string
} else if (
payloadPayload &&
key in payloadPayload &&
typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
) {
configLabelKey = payloadPayload[key as keyof typeof payloadPayload] as string;
configLabelKey = payloadPayload[
key as keyof typeof payloadPayload
] as string
}
return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config];
return configLabelKey in config
? config[configLabelKey]
: config[key as keyof typeof config]
}
export { ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent, ChartStyle };
export {
ChartContainer,
ChartTooltip,
ChartTooltipContent,
ChartLegend,
ChartLegendContent,
ChartStyle,
}

View File

@ -0,0 +1,11 @@
import tsconfigPaths from "vite-tsconfig-paths";
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [tsconfigPaths(), react()],
test: {
environment: "jsdom",
setupFiles: ["src/__tests__/setupTests.ts"],
},
});

View File

@ -17,8 +17,7 @@ msg_info "Installing Dependencies"
$STD apt install -y nginx
msg_ok "Installed Dependencies"
PHP_VERSION="8.4"
PHP_MODULE="common,ctype,fileinfo,mysql,tokenizer,dom,redis" PHP_FPM="YES" setup_php
PHP_VERSION="8.3" PHP_MODULE="common,ctype,fileinfo,mysql,cli,tokenizer,dom,redis,session,openssl" PHP_FPM="YES" setup_php
setup_composer
setup_mariadb
MARIADB_DB_NAME="2fauth_db" MARIADB_DB_USER="2fauth" setup_mariadb_db
@ -66,7 +65,7 @@ server {
error_page 404 /index.php;
location ~ \.php\$ {
fastcgi_pass unix:/var/run/php/php${PHP_VERSION}-fpm.sock;
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name;
include fastcgi_params;
}

View File

@ -12,7 +12,6 @@ catch_errors
setting_up_container
network_check
update_os
setup_hwaccel
msg_info "Installing Dependencies"
$STD apt install -y \

View File

@ -12,7 +12,6 @@ catch_errors
setting_up_container
network_check
update_os
setup_hwaccel
echo
echo "${TAB3}Choose the GPU type for ComfyUI:"

View File

@ -12,7 +12,6 @@ catch_errors
setting_up_container
network_check
update_os
setup_hwaccel
setup_imagemagick

View File

@ -22,8 +22,6 @@ fetch_and_deploy_gh_release "discopanel" "nickheyer/discopanel" "tarball" "lates
setup_docker
msg_info "Setting up DiscoPanel"
cd /opt/discopanel
$STD make gen
cd /opt/discopanel/web/discopanel
$STD npm install
$STD npm run build

View File

@ -12,18 +12,17 @@ catch_errors
setting_up_container
network_check
update_os
setup_hwaccel
msg_info "Installing Dependencies"
$STD apt install -y \
build-essential \
python3-dev \
libpq-dev \
nginx \
redis-server \
ffmpeg \
procps \
streamlink
build-essential \
python3-dev \
libpq-dev \
nginx \
redis-server \
ffmpeg \
procps \
streamlink
msg_ok "Installed Dependencies"
setup_uv
@ -40,11 +39,11 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8'
$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';"
$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';"
{
echo "Dispatcharr Credentials"
echo "Database Name: $DB_NAME"
echo "Database User: $DB_USER"
echo "Database Password: $DB_PASS"
echo ""
echo "Dispatcharr Credentials"
echo "Database Name: $DB_NAME"
echo "Database User: $DB_USER"
echo "Database Password: $DB_PASS"
echo ""
} >>~/dispatcharr.creds
msg_ok "Created PostgreSQL Database"
@ -59,9 +58,9 @@ msg_ok "Installed Python Dependencies"
msg_info "Configuring Dispatcharr"
install -d -m 755 \
/data/{logos,recordings,plugins,db} \
/data/uploads/{m3us,epgs} \
/data/{m3us,epgs}
/data/{logos,recordings,plugins,db} \
/data/uploads/{m3us,epgs} \
/data/{m3us,epgs}
chown -R root:root /data
DJANGO_SECRET=$(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9' | cut -c1-50)
export DATABASE_URL="postgresql://${DB_USER}:${DB_PASS}@localhost:5432/${DB_NAME}"

View File

@ -12,7 +12,6 @@ catch_errors
setting_up_container
network_check
update_os
setup_hwaccel
USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "go2rtc" "AlexxIT/go2rtc" "singlefile" "latest" "/opt/go2rtc" "go2rtc_linux_amd64"

View File

@ -14,22 +14,27 @@ network_check
update_os
msg_info "Installing Dependencies"
$STD apt install -y apt-transport-https
$STD apt-get install -y apt-transport-https
$STD apt-get install -y composer
$STD apt-get install -y php8.2-{bz2,curl,sqlite3,zip,xml}
msg_ok "Installed Dependencies"
PHP_VERSION="8.4" PHP_MODULE="bz2,sqlite3" PHP_FPM="YES" setup_php
setup_composer
fetch_and_deploy_gh_release "Heimdall" "linuxserver/Heimdall" "tarball"
msg_info "Setting up Heimdall-Dashboard"
RELEASE=$(curl -fsSL "https://api.github.com/repos/linuxserver/Heimdall/releases/latest" | awk '/tag_name/{print $4;exit}' FS='[""]')
echo "${RELEASE}" >/opt/"${APPLICATION}"_version.txt
msg_info "Installing Heimdall Dashboard ${RELEASE}"
curl -fsSL "https://github.com/linuxserver/Heimdall/archive/${RELEASE}.tar.gz" -o "${RELEASE}".tar.gz
tar xzf "${RELEASE}".tar.gz
VER=$(curl -fsSL https://api.github.com/repos/linuxserver/Heimdall/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }')
rm -rf "${RELEASE}".tar.gz
mv Heimdall-"${VER}" /opt/Heimdall
cd /opt/Heimdall
cp .env.example .env
$STD php artisan key:generate
msg_ok "Setup Heimdall-Dashboard"
msg_ok "Installed Heimdall Dashboard ${RELEASE}"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/heimdall.service
[Unit]
service_path="/etc/systemd/system/heimdall.service"
echo "[Unit]
Description=Heimdall
After=network.target
@ -39,16 +44,14 @@ RestartSec=5
Type=simple
User=root
WorkingDirectory=/opt/Heimdall
ExecStart=/usr/bin/php artisan serve --port 7990 --host 0.0.0.0
ExecStart="/usr/bin/php" artisan serve --port 7990 --host 0.0.0.0
TimeoutStopSec=30
[Install]
WantedBy=multi-user.target"
EOF
WantedBy=multi-user.target" >$service_path
systemctl enable -q --now heimdall
cd /opt/Heimdall
export COMPOSER_ALLOW_SUPERUSER=1
$STD composer dump-autoload
COMPOSER_ALLOW_SUPERUSER=1 composer dump-autoload &>/dev/null
systemctl restart heimdall.service
msg_ok "Created Service"

View File

@ -1,7 +1,7 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2025 community-scripts ORG
# Author: MickLesk (CanbiZ) | Co-Author: CrazyWolf13
# Author: MickLesk (Canbiz)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/homarr-labs/homarr
@ -16,24 +16,28 @@ update_os
msg_info "Installing Dependencies"
$STD apt install -y \
redis-server \
ca-certificates \
make \
g++ \
build-essential \
nginx \
gettext \
openssl \
musl-dev
jq \
openssl
msg_ok "Installed Dependencies"
NODE_VERSION=$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.engines.node | split(">=")[1] | split(".")[0]')
NODE_MODULE="pnpm@$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.packageManager | split("@")[1]')"
setup_nodejs
fetch_and_deploy_gh_release "homarr" "homarr-labs/homarr" "prebuild" "latest" "/opt/homarr" "build-amd64.tar.gz"
fetch_and_deploy_gh_release "homarr" "homarr-labs/homarr"
msg_info "Installing Homarr"
# fix musl issues because homarr compiles on alpine not debian soure: https://github.com/alexander-akhmetov/python-telegram/issues/3
ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1
msg_info "Installing Homarr (Patience)"
cd /opt
mkdir -p /opt/homarr_db
touch /opt/homarr_db/db.sqlite
SECRET_ENCRYPTION_KEY="$(openssl rand -hex 32)"
cd /opt/homarr
cat <<EOF >/opt/homarr.env
cat <<EOF >/opt/homarr/.env
DB_DRIVER='better-sqlite3'
DB_DIALECT='sqlite'
SECRET_ENCRYPTION_KEY='${SECRET_ENCRYPTION_KEY}'
@ -41,28 +45,54 @@ DB_URL='/opt/homarr_db/db.sqlite'
TURBO_TELEMETRY_DISABLED=1
AUTH_PROVIDERS='credentials'
NODE_ENV='production'
REDIS_IS_EXTERNAL='true'
EOF
$STD pnpm install --recursive --frozen-lockfile --shamefully-hoist
$STD pnpm build
msg_ok "Installed Homarr"
msg_info "Copying config files"
msg_info "Copying build and config files"
cp /opt/homarr/apps/nextjs/next.config.ts .
cp /opt/homarr/apps/nextjs/package.json .
cp -r /opt/homarr/packages/db/migrations /opt/homarr_db/migrations
cp -r /opt/homarr/apps/nextjs/.next/standalone/* /opt/homarr
mkdir -p /appdata/redis
chown -R redis:redis /appdata/redis
chmod 744 /appdata/redis
cp /opt/homarr/redis.conf /etc/redis/redis.conf
rm /etc/nginx/nginx.conf
cp /opt/homarr/packages/redis/redis.conf /opt/homarr/redis.conf
mkdir -p /etc/nginx/templates
rm /etc/nginx/nginx.conf
cp /opt/homarr/nginx.conf /etc/nginx/templates/nginx.conf
mkdir -p /opt/homarr/apps/cli
cp /opt/homarr/packages/cli/cli.cjs /opt/homarr/apps/cli/cli.cjs
echo $'#!/bin/bash\ncd /opt/homarr/apps/cli && node ./cli.cjs "$@"' >/usr/bin/homarr
chmod +x /usr/bin/homarr
msg_ok "Copied config files"
mkdir /opt/homarr/build
cp ./node_modules/better-sqlite3/build/Release/better_sqlite3.node ./build/better_sqlite3.node
msg_ok "Finished copying"
msg_info "Creating Services"
mkdir -p /etc/systemd/system/redis-server.service.d/
cat <<EOF >/etc/systemd/system/redis-server.service.d/override.conf
[Service]
ReadWritePaths=-/appdata/redis -/var/lib/redis -/var/log/redis -/var/run/redis -/etc/redis
cat <<'EOF' >/opt/run_homarr.sh
#!/bin/bash
set -a
source /opt/homarr/.env
set +a
export DB_DIALECT='sqlite'
export AUTH_SECRET=$(openssl rand -base64 32)
export CRON_JOB_API_KEY=$(openssl rand -base64 32)
node /opt/homarr_db/migrations/$DB_DIALECT/migrate.cjs /opt/homarr_db/migrations/$DB_DIALECT
for dir in $(find /opt/homarr_db/migrations/migrations -mindepth 1 -maxdepth 1 -type d); do
dirname=$(basename "$dir")
mkdir -p "/opt/homarr_db/migrations/$dirname"
cp -r "$dir"/* "/opt/homarr_db/migrations/$dirname/" 2>/dev/null || true
done
export HOSTNAME=$(ip route get 1.1.1.1 | grep -oP 'src \K[^ ]+')
envsubst '${HOSTNAME}' < /etc/nginx/templates/nginx.conf > /etc/nginx/nginx.conf
nginx -g 'daemon off;' &
redis-server /opt/homarr/packages/redis/redis.conf &
node apps/tasks/tasks.cjs &
node apps/websocket/wssServer.cjs &
node apps/nextjs/server.js & PID=$!
wait $PID
EOF
chmod +x /opt/run_homarr.sh
cat <<EOF >/etc/systemd/system/homarr.service
[Unit]
Description=Homarr Service
@ -71,17 +101,14 @@ After=network.target
[Service]
Type=exec
WorkingDirectory=/opt/homarr
EnvironmentFile=-/opt/homarr.env
ExecStart=/opt/homarr/run.sh
EnvironmentFile=-/opt/homarr/.env
ExecStart=/opt/run_homarr.sh
[Install]
WantedBy=multi-user.target
EOF
chmod +x /opt/homarr/run.sh
systemctl daemon-reload
systemctl enable -q --now redis-server && sleep 5
systemctl enable -q --now homarr
msg_ok "Created Services"
msg_ok "Created Service"
motd_ssh
customize

View File

@ -12,7 +12,6 @@ catch_errors
setting_up_container
network_check
update_os
setup_hwaccel
msg_info "Installing HyperHDR"
setup_deb822_repo \

View File

@ -12,7 +12,6 @@ catch_errors
setting_up_container
network_check
update_os
setup_hwaccel
msg_info "Setting up Hyperion repository"
setup_deb822_repo \

View File

@ -1,189 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2025 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://koel.dev/
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
msg_info "Installing Dependencies"
$STD apt install -y \
nginx \
ffmpeg \
cron \
locales
msg_ok "Installed Dependencies"
import_local_ip
PG_VERSION="16" setup_postgresql
PG_DB_NAME="koel" PG_DB_USER="koel" setup_postgresql_db
PHP_VERSION="8.4" PHP_FPM="YES" PHP_MODULE="bz2,exif,imagick,pgsql,sqlite3" setup_php
NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs
setup_composer
fetch_and_deploy_gh_release "koel" "koel/koel" "prebuild" "latest" "/opt/koel" "koel-*.tar.gz"
msg_info "Configuring Koel"
mkdir -p /opt/koel_media /opt/koel_sync
cd /opt/koel
cat <<EOF >/opt/koel/.env
APP_NAME=Koel
APP_ENV=production
APP_DEBUG=false
APP_URL=http://${LOCAL_IP}
APP_KEY=
TRUSTED_HOSTS=
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=${PG_DB_NAME}
DB_USERNAME=${PG_DB_USER}
DB_PASSWORD=${PG_DB_PASS}
STORAGE_DRIVER=local
MEDIA_PATH=/opt/koel_media
ARTIFACTS_PATH=
IGNORE_DOT_FILES=true
APP_MAX_SCAN_TIME=600
MEMORY_LIMIT=
STREAMING_METHOD=php
SCOUT_DRIVER=tntsearch
USE_MUSICBRAINZ=true
MUSICBRAINZ_USER_AGENT=
LASTFM_API_KEY=
LASTFM_API_SECRET=
SPOTIFY_CLIENT_ID=
SPOTIFY_CLIENT_SECRET=
YOUTUBE_API_KEY=
CDN_URL=
TRANSCODE_FLAC=false
FFMPEG_PATH=/usr/bin/ffmpeg
TRANSCODE_BIT_RATE=128
ALLOW_DOWNLOAD=true
BACKUP_ON_DELETE=true
MEDIA_BROWSER_ENABLED=false
PROXY_AUTH_ENABLED=false
SYNC_LOG_LEVEL=error
FORCE_HTTPS=
MAIL_FROM_ADDRESS="noreply@localhost"
MAIL_FROM_NAME="Koel"
MAIL_MAILER=log
MAIL_HOST=null
MAIL_PORT=null
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
BROADCAST_CONNECTION=log
CACHE_DRIVER=file
FILESYSTEM_DISK=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
EOF
mkdir -p /opt/koel/storage/{app/public,framework/{cache/data,sessions,views},logs}
chown -R www-data:www-data /opt/koel /opt/koel_media /opt/koel_sync
chmod -R 775 /opt/koel/storage /opt/koel/bootstrap/cache
msg_ok "Configured Koel"
msg_info "Installing Koel (Patience)"
export COMPOSER_ALLOW_SUPERUSER=1
cd /opt/koel
$STD composer install --no-interaction --no-dev --optimize-autoloader
$STD php artisan key:generate --force
$STD php artisan config:clear
$STD php artisan cache:clear
$STD php artisan koel:init --no-assets --no-interaction
chown -R www-data:www-data /opt/koel
msg_ok "Installed Koel"
msg_info "Tuning PHP-FPM"
PHP_FPM_CONF="/etc/php/8.4/fpm/pool.d/www.conf"
sed -i 's/^pm.max_children = .*/pm.max_children = 15/' "$PHP_FPM_CONF"
sed -i 's/^pm.start_servers = .*/pm.start_servers = 4/' "$PHP_FPM_CONF"
sed -i 's/^pm.min_spare_servers = .*/pm.min_spare_servers = 2/' "$PHP_FPM_CONF"
sed -i 's/^pm.max_spare_servers = .*/pm.max_spare_servers = 8/' "$PHP_FPM_CONF"
$STD systemctl restart php8.4-fpm
msg_ok "Tuned PHP-FPM"
msg_info "Configuring Nginx"
cat <<'EOF' >/etc/nginx/sites-available/koel
server {
listen 80;
server_name _;
root /opt/koel/public;
index index.php;
client_max_body_size 50M;
charset utf-8;
gzip on;
gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/json;
gzip_comp_level 9;
send_timeout 3600;
location / {
try_files $uri $uri/ /index.php?$args;
}
location /media/ {
internal;
alias $upstream_http_x_media_root;
}
location ~ \.php$ {
try_files $uri $uri/ /index.php?$args;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_intercept_errors on;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
EOF
rm -f /etc/nginx/sites-enabled/default
ln -sf /etc/nginx/sites-available/koel /etc/nginx/sites-enabled/koel
$STD systemctl reload nginx
msg_ok "Configured Nginx"
msg_info "Setting up Cron Job"
cat <<'EOF' >/etc/cron.d/koel
0 * * * * www-data cd /opt/koel && /usr/bin/php artisan koel:scan >/dev/null 2>&1
EOF
chmod 644 /etc/cron.d/koel
msg_ok "Set up Cron Job"
motd_ssh
customize
cleanup_lxc

Some files were not shown because too many files have changed in this diff Show More