mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2025-12-17 12:43:27 +01:00
Compare commits
103 Commits
05a66e3729
...
update_ver
| Author | SHA1 | Date | |
|---|---|---|---|
| 273cd6a647 | |||
| 9135cc3f31 | |||
| 93a4ce5e33 | |||
| 0271dddfd5 | |||
| 5a263b5036 | |||
| 60635feeab | |||
| 7261ffe8a2 | |||
| 44ea7cecb8 | |||
| 5f6af94ca6 | |||
| 059aaac914 | |||
| 951fb3d53a | |||
| 000c8b5fc9 | |||
| baaf6a9f58 | |||
| 4ff31f3dcf | |||
| 0dd4a9e6ed | |||
| 3dc2ea7d50 | |||
| 1333624879 | |||
| c1831f9bc8 | |||
| 4b09cb3d00 | |||
| 01b147d28a | |||
| e95ae11f88 | |||
| f2a2ec30ba | |||
| 798015df6f | |||
| 2773faee08 | |||
| b524cbe232 | |||
| 7b55077a00 | |||
| 7ab0327376 | |||
| 51ba8ca7c8 | |||
| 6d78e4da6b | |||
| a3fb28105d | |||
| cc7eb7fd35 | |||
| b605e463d1 | |||
| 71c2f4e7f8 | |||
| 648ca5e4d0 | |||
| 0917921225 | |||
| c269dc3b13 | |||
| d0a09ccd5d | |||
| 494ab1cee3 | |||
| 568bbb7f32 | |||
| 3026a405c8 | |||
| 7d98f80eb7 | |||
| f4c1b68881 | |||
| a949a1cc97 | |||
| 1e6b9a7001 | |||
| 8672aa075a | |||
| aaa20f7845 | |||
| 6da27a7112 | |||
| 8e07685aab | |||
| 5ba55d1f5a | |||
| 126b0c339a | |||
| c2034039e8 | |||
| 9374e5d183 | |||
| 6311584fef | |||
| 5bbf0861e9 | |||
| 7cb02a0cc3 | |||
| 010e8d5f27 | |||
| 2b322bf182 | |||
| d12215dea8 | |||
| f258ab7ae5 | |||
| 33f167f278 | |||
| 3568cf52f9 | |||
| 5f5144c661 | |||
| 09134d94dd | |||
| 33103ad256 | |||
| fd75da89c4 | |||
| ee49576f3a | |||
| e651c14f76 | |||
| 1fc5c031cc | |||
| 35793051a1 | |||
| 9a6a0379a6 | |||
| 4fab369a5b | |||
| f977f6c987 | |||
| 2524dd1af7 | |||
| dfcaa70647 | |||
| 7b911403a3 | |||
| 1a96ed2221 | |||
| b180694d46 | |||
| f807ec748d | |||
| b9f29f2bd3 | |||
| 66aa0f163a | |||
| e22284635a | |||
| c978c49cc9 | |||
| 882babeaf3 | |||
| a411d780ea | |||
| 86a3099452 | |||
| 26a8caa2eb | |||
| 415a2c1215 | |||
| 308a1cb91e | |||
| 1e97124c0c | |||
| 3f8685067f | |||
| 5adf98fada | |||
| 4cd81a9769 | |||
| d83af67c62 | |||
| e9d14b3a15 | |||
| 13cbe0fdbf | |||
| 52db0d94ed | |||
| e38436340b | |||
| 139e86b945 | |||
| a91151549d | |||
| 5ec7d0fd4d | |||
| 02911dc07c | |||
| 1bb3837646 | |||
| 8e6f911963 |
1
.github/pull_request_template.md
generated
vendored
1
.github/pull_request_template.md
generated
vendored
@ -3,6 +3,7 @@ PRs without prior testing will be closed. -->
|
|||||||
|
|
||||||
## ✍️ Description
|
## ✍️ Description
|
||||||
|
|
||||||
|
|
||||||
## 🔗 Related Issue
|
## 🔗 Related Issue
|
||||||
|
|
||||||
Fixes #
|
Fixes #
|
||||||
|
|||||||
114
CHANGELOG.md
114
CHANGELOG.md
@ -10,8 +10,122 @@
|
|||||||
> [!CAUTION]
|
> [!CAUTION]
|
||||||
Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit the project's popularity for potentially malicious purposes.
|
Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit the project's popularity for potentially malicious purposes.
|
||||||
|
|
||||||
|
## 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
|
## 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))
|
||||||
|
|
||||||
|
- #### ✨ New Features
|
||||||
|
|
||||||
|
- 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
|
## 2025-12-12
|
||||||
|
|
||||||
### 🆕 New Scripts
|
### 🆕 New Scripts
|
||||||
|
|||||||
33
README.md
33
README.md
@ -30,8 +30,8 @@
|
|||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
> **Simplify your Proxmox VE setup with community-driven automation scripts**
|
**Simplify your Proxmox VE setup with community-driven automation scripts**
|
||||||
> Originally created by tteck, now maintained and expanded by the community
|
Originally created by tteck, now maintained and expanded by the community
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -239,17 +239,34 @@ This project is maintained by volunteers in memory of tteck. Your support helps
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📈 Project Growth
|
## 📈 Project Statistics
|
||||||
|
<p align="center">
|
||||||
|
<img
|
||||||
|
src="https://repobeats.axiom.co/api/embed/57edde03e00f88d739bdb5b844ff7d07dd079375.svg"
|
||||||
|
alt="Repobeats analytics"
|
||||||
|
width="650"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
|
||||||
<div align="center">
|
<p align="center">
|
||||||
<a href="https://star-history.com/#community-scripts/ProxmoxVE&Date">
|
<a href="https://star-history.com/#community-scripts/ProxmoxVE&Date">
|
||||||
<picture>
|
<picture>
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=community-scripts/ProxmoxVE&type=Date&theme=dark" />
|
<source
|
||||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=community-scripts/ProxmoxVE&type=Date" />
|
media="(prefers-color-scheme: dark)"
|
||||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=community-scripts/ProxmoxVE&type=Date" />
|
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"
|
||||||
|
/>
|
||||||
</picture>
|
</picture>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</p>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -28,20 +28,21 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
if check_for_gh_release "2fauth" "Bubka/2FAuth"; then
|
if check_for_gh_release "2fauth" "Bubka/2FAuth"; then
|
||||||
$STD apt update
|
$STD apt update
|
||||||
$STD apt -y upgrade
|
$STD apt -y upgrade
|
||||||
|
|
||||||
msg_info "Creating Backup"
|
msg_info "Creating Backup"
|
||||||
mv "/opt/2fauth" "/opt/2fauth-backup"
|
mv "/opt/2fauth" "/opt/2fauth-backup"
|
||||||
if ! dpkg -l | grep -q 'php8.3'; then
|
if ! dpkg -l | grep -q 'php8.4'; then
|
||||||
cp /etc/nginx/conf.d/2fauth.conf /etc/nginx/conf.d/2fauth.conf.bak
|
cp /etc/nginx/conf.d/2fauth.conf /etc/nginx/conf.d/2fauth.conf.bak
|
||||||
fi
|
fi
|
||||||
msg_ok "Backup Created"
|
msg_ok "Backup Created"
|
||||||
|
|
||||||
if ! dpkg -l | grep -q 'php8.3'; then
|
if ! dpkg -l | grep -q 'php8.4'; then
|
||||||
PHP_VERSION="8.3" PHP_MODULE="common,ctype,fileinfo,mysql,cli,tokenizer,dom,redis,session,openssl" PHP_FPM="YES" setup_php
|
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.2/php8.3/g' /etc/nginx/conf.d/2fauth.conf
|
sed -i 's/php8\.[0-9]/php8.4/g' /etc/nginx/conf.d/2fauth.conf
|
||||||
fi
|
fi
|
||||||
fetch_and_deploy_gh_release "2fauth" "Bubka/2FAuth"
|
fetch_and_deploy_gh_release "2fauth" "Bubka/2FAuth"
|
||||||
setup_composer
|
setup_composer
|
||||||
|
|||||||
@ -27,6 +27,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
msg_error "Currently we don't provide an update function for this ${APP}."
|
msg_error "Currently we don't provide an update function for this ${APP}."
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,6 +28,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
if check_for_gh_release "booklore" "booklore-app/BookLore"; then
|
if check_for_gh_release "booklore" "booklore-app/BookLore"; then
|
||||||
msg_info "Stopping Service"
|
msg_info "Stopping Service"
|
||||||
systemctl stop booklore
|
systemctl stop booklore
|
||||||
@ -46,6 +47,7 @@ function update_script() {
|
|||||||
msg_ok "Built Frontend"
|
msg_ok "Built Frontend"
|
||||||
|
|
||||||
JAVA_VERSION="21" setup_java
|
JAVA_VERSION="21" setup_java
|
||||||
|
setup_yq
|
||||||
|
|
||||||
msg_info "Building Backend"
|
msg_info "Building Backend"
|
||||||
cd /opt/booklore/booklore-api
|
cd /opt/booklore/booklore-api
|
||||||
|
|||||||
@ -28,6 +28,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
if check_for_gh_release "bookstack" "BookStackApp/BookStack"; then
|
if check_for_gh_release "bookstack" "BookStackApp/BookStack"; then
|
||||||
msg_info "Stopping Apache2"
|
msg_info "Stopping Apache2"
|
||||||
systemctl stop apache2
|
systemctl stop apache2
|
||||||
|
|||||||
@ -1,138 +0,0 @@
|
|||||||
#!/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}"
|
|
||||||
@ -49,6 +49,8 @@ function update_script() {
|
|||||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "discopanel" "nickheyer/discopanel" "tarball" "latest" "/opt/discopanel"
|
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "discopanel" "nickheyer/discopanel" "tarball" "latest" "/opt/discopanel"
|
||||||
|
|
||||||
msg_info "Setting up DiscoPanel"
|
msg_info "Setting up DiscoPanel"
|
||||||
|
cd /opt/discopanel
|
||||||
|
$STD make gen
|
||||||
cd /opt/discopanel/web/discopanel
|
cd /opt/discopanel/web/discopanel
|
||||||
$STD npm install
|
$STD npm install
|
||||||
$STD npm run build
|
$STD npm run build
|
||||||
|
|||||||
@ -27,6 +27,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
msg_error "To update ${APP}, use the applications web interface."
|
msg_error "To update ${APP}, use the applications web interface."
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
|
|
||||||
if ! grep -Fq "root /usr/bin/php /opt/domain-monitor/cron/check_domains.php" /etc/crontab; then
|
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
|
echo "0 0 * * * root /usr/bin/php /opt/domain-monitor/cron/check_domains.php" >>/etc/crontab
|
||||||
|
|||||||
@ -28,7 +28,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
if check_for_gh_release "firefly" "firefly-iii/firefly-iii"; then
|
if check_for_gh_release "firefly" "firefly-iii/firefly-iii"; then
|
||||||
systemctl stop apache2
|
systemctl stop apache2
|
||||||
cp /opt/firefly/.env /opt/.env
|
cp /opt/firefly/.env /opt/.env
|
||||||
|
|||||||
@ -23,6 +23,7 @@ function update_script() {
|
|||||||
header_info
|
header_info
|
||||||
check_container_storage
|
check_container_storage
|
||||||
check_container_resources
|
check_container_resources
|
||||||
|
setup_mariadb
|
||||||
|
|
||||||
NODE_VERSION="22" setup_nodejs
|
NODE_VERSION="22" setup_nodejs
|
||||||
|
|
||||||
|
|||||||
@ -28,6 +28,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
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/')
|
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
|
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}."
|
msg_error "Currently we don't provide an update function for this ${APP}."
|
||||||
|
|||||||
@ -1,6 +0,0 @@
|
|||||||
______ ___ __ _ __ __
|
|
||||||
/ ____/___ _/ (_) /_ ________ | | / /__ / /_
|
|
||||||
/ / / __ `/ / / __ \/ ___/ _ \_____| | /| / / _ \/ __ \
|
|
||||||
/ /___/ /_/ / / / /_/ / / / __/_____/ |/ |/ / __/ /_/ /
|
|
||||||
\____/\__,_/_/_/_.___/_/ \___/ |__/|__/\___/_.___/
|
|
||||||
|
|
||||||
6
ct/headers/koel
Normal file
6
ct/headers/koel
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
__ __ __
|
||||||
|
/ //_/___ ___ / /
|
||||||
|
/ ,< / __ \/ _ \/ /
|
||||||
|
/ /| / /_/ / __/ /
|
||||||
|
/_/ |_\____/\___/_/
|
||||||
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
_ __ __ _ ___
|
_____
|
||||||
/ | / /__ / /| | / (_)________ _____
|
/ ___/_________ _____ ____ ____ __ __
|
||||||
/ |/ / _ \/ __/ | / / / ___/ __ \/ ___/
|
\__ \/ ___/ __ `/ __ \/ __ \/ __ \/ / / /
|
||||||
/ /| / __/ /_ | |/ / (__ ) /_/ / /
|
___/ / /__/ /_/ / / / / /_/ / /_/ / /_/ /
|
||||||
/_/ |_/\___/\__/ |___/_/____/\____/_/
|
/____/\___/\__,_/_/ /_/\____/ .___/\__, /
|
||||||
|
/_/ /____/
|
||||||
|
|||||||
6
ct/headers/scanopy
Normal file
6
ct/headers/scanopy
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
_____
|
||||||
|
/ ___/_________ _____ ____ ____ __ __
|
||||||
|
\__ \/ ___/ __ `/ __ \/ __ \/ __ \/ / / /
|
||||||
|
___/ / /__/ /_/ / / / / /_/ / /_/ / /_/ /
|
||||||
|
/____/\___/\__,_/_/ /_/\____/ .___/\__, /
|
||||||
|
/_/ /____/
|
||||||
@ -11,7 +11,7 @@ var_cpu="${var_cpu:-1}"
|
|||||||
var_ram="${var_ram:-512}"
|
var_ram="${var_ram:-512}"
|
||||||
var_disk="${var_disk:-2}"
|
var_disk="${var_disk:-2}"
|
||||||
var_os="${var_os:-debian}"
|
var_os="${var_os:-debian}"
|
||||||
var_version="${var_version:-12}"
|
var_version="${var_version:-13}"
|
||||||
var_unprivileged="${var_unprivileged:-1}"
|
var_unprivileged="${var_unprivileged:-1}"
|
||||||
|
|
||||||
header_info "$APP"
|
header_info "$APP"
|
||||||
@ -27,45 +27,45 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
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
|
if check_for_gh_release "Heimdall" "linuxserver/Heimdall"; then
|
||||||
msg_info "Stopping Service"
|
msg_info "Stopping Service"
|
||||||
systemctl stop heimdall
|
systemctl stop heimdall
|
||||||
sleep 1
|
sleep 1
|
||||||
msg_ok "Stopped Service"
|
msg_ok "Stopped Service"
|
||||||
|
|
||||||
msg_info "Backing up Data"
|
msg_info "Backing up Data"
|
||||||
cp -R /opt/Heimdall/database database-backup
|
cp -R /opt/Heimdall/database database-backup
|
||||||
cp -R /opt/Heimdall/public public-backup
|
cp -R /opt/Heimdall/public public-backup
|
||||||
sleep 1
|
sleep 1
|
||||||
msg_ok "Backed up Data"
|
msg_ok "Backed up Data"
|
||||||
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")
|
setup_composer
|
||||||
tar xzf "${RELEASE}".tar.gz
|
fetch_and_deploy_gh_release "Heimdall" "linuxserver/Heimdall" "tarball"
|
||||||
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
|
msg_info "Updating Heimdall-Dashboard"
|
||||||
cd /opt/Heimdall
|
cd /opt/Heimdall
|
||||||
$STD apt-get install -y composer
|
|
||||||
export COMPOSER_ALLOW_SUPERUSER=1
|
export COMPOSER_ALLOW_SUPERUSER=1
|
||||||
$STD composer dump-autoload
|
$STD composer dump-autoload
|
||||||
echo "${RELEASE}" >/opt/${APP}_version.txt
|
msg_ok "Updated Heimdall-Dashboard"
|
||||||
msg_ok "Updated Heimdall Dashboard to ${RELEASE}"
|
|
||||||
msg_info "Restoring Data"
|
msg_info "Restoring Data"
|
||||||
cd ~
|
cd ~
|
||||||
cp -R database-backup/* /opt/Heimdall/database
|
cp -R database-backup/* /opt/Heimdall/database
|
||||||
cp -R public-backup/* /opt/Heimdall/public
|
cp -R public-backup/* /opt/Heimdall/public
|
||||||
sleep 1
|
sleep 1
|
||||||
msg_ok "Restored Data"
|
msg_ok "Restored Data"
|
||||||
msg_info "Cleanup"
|
|
||||||
rm -rf {"${RELEASE}".tar.gz,Heimdall-"${VER}",public-backup,database-backup,Heimdall}
|
msg_info "Cleaning Up"
|
||||||
|
rm -rf {public-backup,database-backup}
|
||||||
sleep 1
|
sleep 1
|
||||||
msg_ok "Cleaned"
|
msg_ok "Cleaned Up"
|
||||||
|
|
||||||
msg_info "Starting Service"
|
msg_info "Starting Service"
|
||||||
systemctl start heimdall.service
|
systemctl start heimdall.service
|
||||||
sleep 2
|
sleep 2
|
||||||
msg_ok "Started Service"
|
msg_ok "Started Service"
|
||||||
msg_ok "Updated successfully!"
|
msg_ok "Updated successfully!"
|
||||||
else
|
|
||||||
msg_ok "No update required. ${APP} is already at ${RELEASE}."
|
|
||||||
fi
|
fi
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
|
|||||||
155
ct/homarr.sh
155
ct/homarr.sh
@ -1,21 +1,20 @@
|
|||||||
#!/usr/bin/env bash
|
#!/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
|
# Copyright (c) 2021-2025 community-scripts ORG
|
||||||
# Author: CrazyWolf13
|
# Author: MickLesk (CanbiZ) | Co-Author: CrazyWolf13
|
||||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||||
# Source: https://homarr.dev/
|
# Source: https://homarr.dev/
|
||||||
|
|
||||||
APP="homarr"
|
APP="homarr"
|
||||||
var_tags="${var_tags:-arr;dashboard}"
|
var_tags="${var_tags:-arr;dashboard}"
|
||||||
var_cpu="${var_cpu:-3}"
|
var_cpu="${var_cpu:-2}"
|
||||||
var_ram="${var_ram:-6144}"
|
var_ram="${var_ram:-1024}"
|
||||||
var_disk="${var_disk:-8}"
|
var_disk="${var_disk:-8}"
|
||||||
var_os="${var_os:-debian}"
|
var_os="${var_os:-debian}"
|
||||||
var_version="${var_version:-13}"
|
var_version="${var_version:-13}"
|
||||||
var_unprivileged="${var_unprivileged:-1}"
|
var_unprivileged="${var_unprivileged:-1}"
|
||||||
|
|
||||||
header_info "$APP"
|
header_info "$APP"
|
||||||
|
|
||||||
variables
|
variables
|
||||||
color
|
color
|
||||||
catch_errors
|
catch_errors
|
||||||
@ -28,69 +27,43 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
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
|
if check_for_gh_release "homarr" "homarr-labs/homarr"; then
|
||||||
msg_info "Stopping Services (Patience)"
|
msg_info "Stopping Services (Patience)"
|
||||||
systemctl stop homarr
|
systemctl stop homarr
|
||||||
|
systemctl stop redis-server
|
||||||
msg_ok "Services Stopped"
|
msg_ok "Services Stopped"
|
||||||
|
|
||||||
msg_info "Backup Data"
|
if ! { grep -q '^REDIS_IS_EXTERNAL=' /opt/homarr/.env 2>/dev/null || grep -q '^REDIS_IS_EXTERNAL=' /opt/homarr.env 2>/dev/null; }; then
|
||||||
mkdir -p /opt/homarr-data-backup
|
DEBIAN_VERSION=$(cat /etc/debian_version 2>/dev/null | cut -d'.' -f1)
|
||||||
cp /opt/homarr/.env /opt/homarr-data-backup/.env
|
if [[ -n "$DEBIAN_VERSION" ]] && [[ "$DEBIAN_VERSION" -lt 13 ]]; then
|
||||||
msg_ok "Backup Data"
|
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 "Updating Nodejs"
|
msg_info "Updating Nodejs"
|
||||||
$STD apt update
|
$STD apt update
|
||||||
@ -98,69 +71,21 @@ EOF
|
|||||||
msg_ok "Updated Nodejs"
|
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_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
|
setup_nodejs
|
||||||
|
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "homarr" "homarr-labs/homarr" "prebuild" "latest" "/opt/homarr" "build-amd64.tar.gz"
|
||||||
|
|
||||||
rm -rf /opt/homarr
|
msg_info "Updating Homarr"
|
||||||
fetch_and_deploy_gh_release "homarr" "homarr-labs/homarr"
|
cp /opt/homarr/redis.conf /etc/redis/redis.conf
|
||||||
|
|
||||||
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
|
rm /etc/nginx/nginx.conf
|
||||||
mkdir -p /etc/nginx/templates
|
|
||||||
cp /opt/homarr/nginx.conf /etc/nginx/templates/nginx.conf
|
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"
|
msg_info "Starting Services"
|
||||||
|
chmod +x /opt/homarr/run.sh
|
||||||
systemctl start homarr
|
systemctl start homarr
|
||||||
|
systemctl start redis-server
|
||||||
msg_ok "Started Services"
|
msg_ok "Started Services"
|
||||||
msg_ok "Updated successfully!"
|
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
|
fi
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
if check_for_gh_release "hortusfox" "danielbrendel/hortusfox-web"; then
|
if check_for_gh_release "hortusfox" "danielbrendel/hortusfox-web"; then
|
||||||
msg_info "Stopping Service"
|
msg_info "Stopping Service"
|
||||||
systemctl stop apache2
|
systemctl stop apache2
|
||||||
|
|||||||
@ -28,7 +28,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
if check_for_gh_release "invoiceninja" "invoiceninja/invoiceninja"; then
|
if check_for_gh_release "invoiceninja" "invoiceninja/invoiceninja"; then
|
||||||
msg_info "Stopping Services"
|
msg_info "Stopping Services"
|
||||||
systemctl stop supervisor nginx php8.4-fpm
|
systemctl stop supervisor nginx php8.4-fpm
|
||||||
|
|||||||
@ -28,6 +28,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
|
|
||||||
msg_info "Updating LXC"
|
msg_info "Updating LXC"
|
||||||
$STD apt update
|
$STD apt update
|
||||||
|
|||||||
@ -30,6 +30,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
|
|
||||||
PHP_VERSION="8.4" PHP_MODULE="mysql" PHP_APACHE="YES" setup_php
|
PHP_VERSION="8.4" PHP_MODULE="mysql" PHP_APACHE="YES" setup_php
|
||||||
setup_composer
|
setup_composer
|
||||||
|
|||||||
81
ct/koel.sh
Normal file
81
ct/koel.sh
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#!/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}"
|
||||||
@ -28,7 +28,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
if check_for_gh_release "leantime" "Leantime/leantime"; then
|
if check_for_gh_release "leantime" "Leantime/leantime"; then
|
||||||
msg_info "Creating Backup"
|
msg_info "Creating Backup"
|
||||||
mariadb-dump leantime >"/opt/${APP}_db_backup_$(date +%F).sql"
|
mariadb-dump leantime >"/opt/${APP}_db_backup_$(date +%F).sql"
|
||||||
|
|||||||
@ -27,6 +27,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
msg_info "Updating LibreNMS"
|
msg_info "Updating LibreNMS"
|
||||||
su librenms
|
su librenms
|
||||||
cd /opt/librenms
|
cd /opt/librenms
|
||||||
|
|||||||
@ -27,6 +27,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
|
|
||||||
msg_warn "Application is updated via Web Interface"
|
msg_warn "Application is updated via Web Interface"
|
||||||
exit
|
exit
|
||||||
|
|||||||
@ -28,6 +28,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
if check_for_gh_release "mmdl" "intri-in/manage-my-damn-life-nextjs"; then
|
if check_for_gh_release "mmdl" "intri-in/manage-my-damn-life-nextjs"; then
|
||||||
msg_info "Stopping service"
|
msg_info "Stopping service"
|
||||||
systemctl stop mmdl
|
systemctl stop mmdl
|
||||||
|
|||||||
@ -27,6 +27,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
msg_info "Updating ${APP} LXC"
|
msg_info "Updating ${APP} LXC"
|
||||||
$STD apt update
|
$STD apt update
|
||||||
$STD apt -y upgrade
|
$STD apt -y upgrade
|
||||||
|
|||||||
@ -23,7 +23,7 @@ function update_script() {
|
|||||||
header_info
|
header_info
|
||||||
check_container_storage
|
check_container_storage
|
||||||
check_container_resources
|
check_container_resources
|
||||||
if [[ ! -f /etc/systemd/system/miniflux.service ]]; then
|
if ! systemctl -q is-enabled miniflux 2>/dev/null; then
|
||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -27,6 +27,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
|
|
||||||
NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs
|
NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs
|
||||||
|
|
||||||
|
|||||||
@ -3,9 +3,9 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
|
|||||||
# Copyright (c) 2021-2025 community-scripts ORG
|
# Copyright (c) 2021-2025 community-scripts ORG
|
||||||
# Author: vhsdream
|
# Author: vhsdream
|
||||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||||
# Source: https://github.com/netvisor-io/netvisor
|
# Source: https://github.com/scanopy/scanopy
|
||||||
|
|
||||||
APP="NetVisor"
|
APP="Scanopy"
|
||||||
var_tags="${var_tags:-analytics}"
|
var_tags="${var_tags:-analytics}"
|
||||||
var_cpu="${var_cpu:-2}"
|
var_cpu="${var_cpu:-2}"
|
||||||
var_ram="${var_ram:-3072}"
|
var_ram="${var_ram:-3072}"
|
||||||
@ -29,16 +29,12 @@ function update_script() {
|
|||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if check_for_gh_release "netvisor" "netvisor-io/netvisor"; then
|
|
||||||
msg_info "Stopping services"
|
msg_info "Stopping services"
|
||||||
systemctl stop netvisor-daemon netvisor-server
|
systemctl -q disable --now netvisor-daemon netvisor-server
|
||||||
msg_ok "Stopped services"
|
msg_ok "Stopped services"
|
||||||
|
|
||||||
msg_info "Backing up configurations"
|
NODE_VERSION="24" setup_nodejs
|
||||||
cp /opt/netvisor/.env /opt/netvisor.env.bak
|
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "scanopy" "scanopy/scanopy" "tarball" "latest" "/opt/scanopy"
|
||||||
msg_ok "Backed up configurations"
|
|
||||||
|
|
||||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "netvisor" "netvisor-io/netvisor" "tarball" "latest" "/opt/netvisor"
|
|
||||||
|
|
||||||
if ! dpkg -l | grep -q "pkg-config"; then
|
if ! dpkg -l | grep -q "pkg-config"; then
|
||||||
$STD apt install -y pkg-config
|
$STD apt install -y pkg-config
|
||||||
@ -46,48 +42,61 @@ function update_script() {
|
|||||||
if ! dpkg -l | grep -q "libssl-dev"; then
|
if ! dpkg -l | grep -q "libssl-dev"; then
|
||||||
$STD apt install -y libssl-dev
|
$STD apt install -y libssl-dev
|
||||||
fi
|
fi
|
||||||
TOOLCHAIN="$(grep "channel" /opt/netvisor/backend/rust-toolchain.toml | awk -F\" '{print $2}')"
|
TOOLCHAIN="$(grep "channel" /opt/scanopy/backend/rust-toolchain.toml | awk -F\" '{print $2}')"
|
||||||
RUST_TOOLCHAIN=$TOOLCHAIN setup_rust
|
RUST_TOOLCHAIN=$TOOLCHAIN setup_rust
|
||||||
|
|
||||||
cp /opt/netvisor.env.bak /opt/netvisor/.env
|
mv /opt/netvisor/.env /opt/scanopy/.env
|
||||||
LOCAL_IP="$(hostname -I | awk '{print $1}')"
|
if [[ -f /opt/netvisor/oidc.toml ]]; then
|
||||||
if ! grep -q "PUBLIC_URL" /opt/netvisor/.env; then
|
mv /opt/netvisor/oidc.toml /opt/scanopy/oidc.toml
|
||||||
sed -i "\|_PATH=|a\NETVISOR_PUBLIC_URL=http://${LOCAL_IP}:60072" /opt/netvisor/.env
|
|
||||||
fi
|
fi
|
||||||
sed -i 's|_TARGET=.*$|_URL=http://127.0.0.1:60072|' /opt/netvisor/.env
|
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"
|
msg_info "Creating frontend UI"
|
||||||
export PUBLIC_SERVER_HOSTNAME=default
|
export PUBLIC_SERVER_HOSTNAME=default
|
||||||
export PUBLIC_SERVER_PORT=""
|
export PUBLIC_SERVER_PORT=""
|
||||||
cd /opt/netvisor/ui
|
cd /opt/scanopy/ui
|
||||||
$STD npm ci --no-fund --no-audit
|
$STD npm ci --no-fund --no-audit
|
||||||
$STD npm run build
|
$STD npm run build
|
||||||
msg_ok "Created frontend UI"
|
msg_ok "Created frontend UI"
|
||||||
|
|
||||||
msg_info "Building Netvisor-server (patience)"
|
msg_info "Building Scanopy-server (patience)"
|
||||||
cd /opt/netvisor/backend
|
cd /opt/scanopy/backend
|
||||||
$STD cargo build --release --bin server
|
$STD cargo build --release --bin server
|
||||||
mv ./target/release/server /usr/bin/netvisor-server
|
mv ./target/release/server /usr/bin/scanopy-server
|
||||||
msg_ok "Built Netvisor-server"
|
msg_ok "Built Scanopy-server"
|
||||||
|
|
||||||
msg_info "Building Netvisor-daemon"
|
msg_info "Building Scanopy-daemon"
|
||||||
$STD cargo build --release --bin daemon
|
$STD cargo build --release --bin daemon
|
||||||
cp ./target/release/daemon /usr/bin/netvisor-daemon
|
cp ./target/release/daemon /usr/bin/scanopy-daemon
|
||||||
msg_ok "Built Netvisor-daemon"
|
msg_ok "Built Scanopy-daemon"
|
||||||
|
|
||||||
|
sed -i '/^ \"server_target.*$/d' /root/.config/daemon/config.json
|
||||||
sed -i -e 's|-target|-url|' \
|
sed -i -e 's|-target|-url|' \
|
||||||
-e 's| --server-port |:|' \
|
-e 's| --server-port |:|' \
|
||||||
|
-e 's/NetVisor/Scanopy/' \
|
||||||
|
-e 's/netvisor/scanopy/' \
|
||||||
/etc/systemd/system/netvisor-daemon.service
|
/etc/systemd/system/netvisor-daemon.service
|
||||||
sed -i '/^ \"server_target.*$/d' /root/.config/daemon/config.json
|
mv /etc/systemd/system/netvisor-daemon.service /etc/systemd/system/scanopy-daemon.service
|
||||||
if ! grep -q "WorkingD" /etc/systemd/system/netvisor-server.service; then
|
sed -i -e 's/NetVisor/Scanopy/' \
|
||||||
sed -i '\|simple$|a\WorkingDirectory=/opt/netvisor/backend' /etc/systemd/system/netvisor-server.service
|
-e 's/netvisor/scanopy/g' \
|
||||||
fi
|
/etc/systemd/system/netvisor-server.service
|
||||||
|
mv /etc/systemd/system/netvisor-server.service /etc/systemd/system/scanopy-server.service
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
|
|
||||||
msg_info "Starting services"
|
msg_info "Starting services"
|
||||||
systemctl start netvisor-server netvisor-daemon
|
systemctl -q enable --now scanopy-server scanopy-daemon
|
||||||
msg_ok "Updated successfully!"
|
msg_ok "Updated successfully!"
|
||||||
fi
|
|
||||||
|
sed -i 's/netvisor/scanopy/' /usr/bin/update
|
||||||
|
mv ~/NetVisor.creds ~/scanopy.creds
|
||||||
|
rm ~/.netvisor
|
||||||
|
rm -rf /opt/netvisor
|
||||||
|
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -88,22 +88,36 @@ EOF
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -x "/usr/bin/ollama" ]; then
|
if [ -x "/usr/bin/ollama" ]; then
|
||||||
msg_info "Updating Ollama"
|
msg_info "Checking for Ollama Update"
|
||||||
OLLAMA_VERSION=$(ollama -v | awk '{print $NF}')
|
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)}')
|
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
|
if [ "$OLLAMA_VERSION" != "$RELEASE" ]; then
|
||||||
msg_info "Stopping Service"
|
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
|
systemctl stop ollama
|
||||||
msg_ok "Stopped Service"
|
msg_ok "Stopped Service"
|
||||||
curl -fsSLO -C - https://ollama.com/download/ollama-linux-amd64.tgz
|
|
||||||
|
msg_info "Installing Ollama"
|
||||||
rm -rf /usr/lib/ollama
|
rm -rf /usr/lib/ollama
|
||||||
rm -rf /usr/bin/ollama
|
rm -rf /usr/bin/ollama
|
||||||
tar -C /usr -xzf ollama-linux-amd64.tgz
|
tar -C /usr -xzf ollama-linux-amd64.tgz
|
||||||
rm -rf ollama-linux-amd64.tgz
|
rm -rf ollama-linux-amd64.tgz
|
||||||
msg_info "Starting Service"
|
msg_ok "Installed Ollama"
|
||||||
|
|
||||||
|
msg_info "Starting Ollama Service"
|
||||||
systemctl start ollama
|
systemctl start ollama
|
||||||
msg_info "Started Service"
|
msg_ok "Started Service"
|
||||||
|
|
||||||
msg_ok "Ollama updated to version $RELEASE"
|
msg_ok "Ollama updated to version $RELEASE"
|
||||||
|
else
|
||||||
|
msg_error "Ollama download failed. Aborting update."
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
msg_ok "Ollama is already up to date."
|
msg_ok "Ollama is already up to date."
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -33,10 +33,23 @@ function update_script() {
|
|||||||
systemctl stop paperless-ai paperless-rag
|
systemctl stop paperless-ai paperless-rag
|
||||||
msg_ok "Stopped Service"
|
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"
|
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"
|
msg_info "Updating Paperless-AI"
|
||||||
cd /opt/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
|
source /opt/paperless-ai/venv/bin/activate
|
||||||
$STD pip install --upgrade pip
|
$STD pip install --upgrade pip
|
||||||
$STD pip install --no-cache-dir -r requirements.txt
|
$STD pip install --no-cache-dir -r requirements.txt
|
||||||
|
|||||||
@ -27,6 +27,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
msg_info "Updating $APP LXC"
|
msg_info "Updating $APP LXC"
|
||||||
$STD apt update
|
$STD apt update
|
||||||
$STD apt upgrade -y
|
$STD apt upgrade -y
|
||||||
|
|||||||
@ -27,6 +27,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
|
|
||||||
CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2)
|
CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2)
|
||||||
if [[ "$CURRENT_PHP" != "8.3" ]]; then
|
if [[ "$CURRENT_PHP" != "8.3" ]]; then
|
||||||
|
|||||||
@ -27,6 +27,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2)
|
CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2)
|
||||||
|
|
||||||
if [[ "$CURRENT_PHP" != "8.4" ]]; then
|
if [[ "$CURRENT_PHP" != "8.4" ]]; then
|
||||||
|
|||||||
@ -32,6 +32,13 @@ function update_script() {
|
|||||||
systemctl stop photoprism
|
systemctl stop photoprism
|
||||||
msg_ok "Stopped 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"
|
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)
|
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)
|
||||||
|
|||||||
@ -27,7 +27,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
if check_for_gh_release "phpipam" "phpipam/phpipam"; then
|
if check_for_gh_release "phpipam" "phpipam/phpipam"; then
|
||||||
msg_info "Stopping Service"
|
msg_info "Stopping Service"
|
||||||
systemctl stop apache2
|
systemctl stop apache2
|
||||||
|
|||||||
@ -27,6 +27,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
if check_for_gh_release "plant-it" "MDeLuise/plant-it"; then
|
if check_for_gh_release "plant-it" "MDeLuise/plant-it"; then
|
||||||
msg_info "Stopping Service"
|
msg_info "Stopping Service"
|
||||||
systemctl stop plant-it
|
systemctl stop plant-it
|
||||||
|
|||||||
@ -27,6 +27,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
|
|
||||||
if check_for_gh_release "projectsend" "projectsend/projectsend"; then
|
if check_for_gh_release "projectsend" "projectsend/projectsend"; then
|
||||||
msg_info "Stopping Service"
|
msg_info "Stopping Service"
|
||||||
|
|||||||
@ -27,6 +27,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2)
|
CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2)
|
||||||
|
|
||||||
if [[ "$CURRENT_PHP" != "8.4" ]]; then
|
if [[ "$CURRENT_PHP" != "8.4" ]]; then
|
||||||
|
|||||||
99
ct/scanopy.sh
Normal file
99
ct/scanopy.sh
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
#!/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}"
|
||||||
@ -27,6 +27,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
msg_info "Updating Shinobi"
|
msg_info "Updating Shinobi"
|
||||||
cd /opt/Shinobi
|
cd /opt/Shinobi
|
||||||
$STD sh UPDATE.sh
|
$STD sh UPDATE.sh
|
||||||
|
|||||||
@ -27,6 +27,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
if ! grep -q "client_max_body_size[[:space:]]\+100M;" /etc/nginx/conf.d/snipeit.conf; then
|
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
|
sed -i '/index index.php;/i \ client_max_body_size 100M;' /etc/nginx/conf.d/snipeit.conf
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -27,32 +27,29 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
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"
|
msg_info "Stopping Service"
|
||||||
systemctl stop spoolman
|
systemctl stop spoolman
|
||||||
msg_ok "Stopped Service"
|
msg_ok "Stopped Service"
|
||||||
|
|
||||||
msg_info "Updating ${APP} to ${RELEASE}"
|
msg_info "Creating Backup"
|
||||||
cd /opt
|
[ -d /opt/spoolman_bak ] && rm -rf /opt/spoolman_bak
|
||||||
rm -rf spoolman_bak
|
mv /opt/spoolman /opt/spoolman_bak
|
||||||
mv spoolman spoolman_bak
|
msg_ok "Created Backup"
|
||||||
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
|
fetch_and_deploy_gh_release "spoolman" "Donkie/Spoolman" "prebuild" "latest" "/opt/spoolman" "spoolman.zip"
|
||||||
cd spoolman
|
|
||||||
|
msg_info "Updating Spoolman"
|
||||||
|
cd /opt/spoolman
|
||||||
$STD pip3 install -r requirements.txt
|
$STD pip3 install -r requirements.txt
|
||||||
curl -fsSL "https://raw.githubusercontent.com/Donkie/Spoolman/master/.env.example" -o ".env"
|
cp /opt/spoolman_bak/.env /opt/spoolman
|
||||||
rm -rf /opt/spoolman.zip
|
msg_ok "Updated Spoolman"
|
||||||
echo "${RELEASE}" >/opt/${APP}_version.txt
|
|
||||||
msg_ok "Updated ${APP} to ${RELEASE}"
|
|
||||||
|
|
||||||
msg_info "Starting Service"
|
msg_info "Starting Service"
|
||||||
systemctl start spoolman
|
systemctl start spoolman
|
||||||
msg_ok "Started Service"
|
msg_ok "Started Service"
|
||||||
msg_ok "Updated successfully!"
|
msg_ok "Updated successfully!"
|
||||||
else
|
|
||||||
msg_ok "No update required. ${APP} is already at ${RELEASE}"
|
|
||||||
fi
|
fi
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,7 +40,9 @@ function update_script() {
|
|||||||
msg_error "Backup failed: /usr/local/share/tunarr does not exist"
|
msg_error "Backup failed: /usr/local/share/tunarr does not exist"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "singlefile" "latest" "/opt/tunarr" "*linux-x64"
|
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
|
||||||
|
|
||||||
msg_info "Starting Service"
|
msg_info "Starting Service"
|
||||||
systemctl start tunarr
|
systemctl start tunarr
|
||||||
|
|||||||
@ -37,8 +37,8 @@ function update_script() {
|
|||||||
|
|
||||||
msg_info "Updating Umami"
|
msg_info "Updating Umami"
|
||||||
cd /opt/umami
|
cd /opt/umami
|
||||||
$STD yarn install
|
$STD pnpm install
|
||||||
$STD yarn run build
|
$STD pnpm run build
|
||||||
msg_ok "Updated Umami"
|
msg_ok "Updated Umami"
|
||||||
|
|
||||||
msg_info "Starting Service"
|
msg_info "Starting Service"
|
||||||
|
|||||||
@ -28,7 +28,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
if check_for_gh_release "wallabag" "wallabag/wallabag"; then
|
if check_for_gh_release "wallabag" "wallabag/wallabag"; then
|
||||||
msg_info "Stopping Services"
|
msg_info "Stopping Services"
|
||||||
systemctl stop nginx php8.3-fpm
|
systemctl stop nginx php8.3-fpm
|
||||||
|
|||||||
@ -27,7 +27,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
if check_for_gh_release "wavelog" "wavelog/wavelog"; then
|
if check_for_gh_release "wavelog" "wavelog/wavelog"; then
|
||||||
msg_info "Stopping Services"
|
msg_info "Stopping Services"
|
||||||
systemctl stop apache2
|
systemctl stop apache2
|
||||||
|
|||||||
@ -27,6 +27,7 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
setup_mariadb
|
||||||
msg_error "Wordpress should be updated via the user interface."
|
msg_error "Wordpress should be updated via the user interface."
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,7 +67,7 @@ function update_script() {
|
|||||||
|
|
||||||
rm -Rf /etc/apt/sources.list.d/zabbix.list
|
rm -Rf /etc/apt/sources.list.d/zabbix.list
|
||||||
cd /tmp
|
cd /tmp
|
||||||
ZABBIX_DEB_URL="https://repo.zabbix.com/zabbix/${ZABBIX_VERSION}/release/debian/pool/main/z/zabbix-release/zabbix-release_latest+debian13_all.deb"
|
ZABBIX_DEB_URL="https://repo.zabbix.com/zabbix/${ZABBIX_VERSION}/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
|
curl -fsSL "$ZABBIX_DEB_URL" -o /tmp/zabbix-release_latest+debian13_all.deb
|
||||||
$STD dpkg -i zabbix-release_latest+debian13_all.deb
|
$STD dpkg -i zabbix-release_latest+debian13_all.deb
|
||||||
rm -rf /tmp/zabbix-release_latest+debian13_all.deb
|
rm -rf /tmp/zabbix-release_latest+debian13_all.deb
|
||||||
|
|||||||
@ -434,12 +434,24 @@ var_unprivileged=1 # 0=privileged, 1=unprivileged
|
|||||||
#### Network
|
#### Network
|
||||||
```bash
|
```bash
|
||||||
var_brg=vmbr0 # Bridge interface
|
var_brg=vmbr0 # Bridge interface
|
||||||
var_net=veth # Network driver
|
var_net=dhcp # dhcp, static IP/CIDR, or IP range (see below)
|
||||||
var_gateway=192.168.1.1 # Default gateway
|
var_gateway=192.168.1.1 # Default gateway (required for static IP)
|
||||||
var_mtu=1500 # MTU size
|
var_mtu=1500 # MTU size
|
||||||
var_vlan=100 # VLAN ID
|
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
|
#### System
|
||||||
```bash
|
```bash
|
||||||
var_hostname=pihole # Container name
|
var_hostname=pihole # Container name
|
||||||
|
|||||||
@ -122,6 +122,31 @@ var_verbose=no \
|
|||||||
echo "✓ Container deployed successfully"
|
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
|
### Using App Defaults
|
||||||
|
|
||||||
**Step 1: Create defaults once (interactive)**
|
**Step 1: Create defaults once (interactive)**
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
dist
|
|
||||||
node_modules
|
|
||||||
.next
|
|
||||||
build
|
|
||||||
.contentlayer
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"plugins": ["prettier-plugin-tailwindcss", "prettier-plugin-organize-imports"]
|
|
||||||
}
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2024 Bram Suurd
|
Copyright (c) 2024-Present Bram Suurd
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
663
frontend/bun.lock
generated
663
frontend/bun.lock
generated
File diff suppressed because it is too large
Load Diff
104
frontend/package.json
generated
104
frontend/package.json
generated
@ -16,80 +16,70 @@
|
|||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-accordion": "^1.2.3",
|
"@radix-ui/react-accordion": "^1.2.12",
|
||||||
"@radix-ui/react-dialog": "^1.1.6",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.6",
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||||
"@radix-ui/react-icons": "^1.3.2",
|
"@radix-ui/react-icons": "^1.3.2",
|
||||||
"@radix-ui/react-label": "^2.1.2",
|
"@radix-ui/react-label": "^2.1.8",
|
||||||
"@radix-ui/react-navigation-menu": "^1.2.5",
|
"@radix-ui/react-navigation-menu": "^1.2.14",
|
||||||
"@radix-ui/react-popover": "^1.1.6",
|
"@radix-ui/react-popover": "^1.1.15",
|
||||||
"@radix-ui/react-scroll-area": "^1.2.10",
|
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||||
"@radix-ui/react-select": "^2.1.6",
|
"@radix-ui/react-select": "^2.2.6",
|
||||||
"@radix-ui/react-separator": "^1.1.2",
|
"@radix-ui/react-separator": "^1.1.8",
|
||||||
"@radix-ui/react-slot": "^1.1.2",
|
"@radix-ui/react-slot": "^1.2.4",
|
||||||
"@radix-ui/react-switch": "^1.1.3",
|
"@radix-ui/react-switch": "^1.2.6",
|
||||||
"@radix-ui/react-tabs": "^1.1.3",
|
"@radix-ui/react-tabs": "^1.1.13",
|
||||||
"@radix-ui/react-tooltip": "^1.2.0",
|
"@radix-ui/react-tooltip": "^1.2.8",
|
||||||
"@tanstack/react-query": "^5.71.1",
|
"@tanstack/react-query": "^5.90.12",
|
||||||
"chart.js": "^4.4.8",
|
"chart.js": "^4.5.1",
|
||||||
"chartjs-plugin-datalabels": "^2.2.0",
|
"chartjs-plugin-datalabels": "^2.2.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
|
"framer-motion": "^12.23.26",
|
||||||
"framer-motion": "^11.18.2",
|
|
||||||
"fuse.js": "^7.1.0",
|
"fuse.js": "^7.1.0",
|
||||||
"lucide-react": "^0.554.0",
|
"lucide-react": "^0.561.0",
|
||||||
"mini-svg-data-uri": "^1.4.4",
|
"mini-svg-data-uri": "^1.4.4",
|
||||||
"motion": "^12.23.12",
|
"motion": "^12.23.26",
|
||||||
"next": "15.5.7",
|
"next": "15.5.8",
|
||||||
"next-themes": "^0.4.4",
|
"next-themes": "^0.4.6",
|
||||||
"nuqs": "^2.4.1",
|
"nuqs": "^2.8.5",
|
||||||
"prettier-plugin-organize-imports": "^4.1.0",
|
"react": "19.2.3",
|
||||||
"react": "19.0.0",
|
"react-chartjs-2": "^5.3.1",
|
||||||
"react-chartjs-2": "^5.3.0",
|
"react-code-blocks": "^0.1.6",
|
||||||
"react-day-picker": "^9.4.3",
|
"react-datepicker": "^9.0.0",
|
||||||
"react-dom": "19.0.0",
|
"react-day-picker": "^9.12.0",
|
||||||
|
"react-dom": "19.2.3",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"react-use-measure": "^2.1.7",
|
"react-use-measure": "^2.1.7",
|
||||||
"recharts": "2.15.4",
|
"recharts": "3.6.0",
|
||||||
"sharp": "^0.33.5",
|
"sharp": "^0.34.5",
|
||||||
"sonner": "^1.7.4",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^2.6.0",
|
"tailwind-merge": "^3.4.0",
|
||||||
"zod": "^3.24.2"
|
"zod": "^4.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^4.16.1",
|
"@antfu/eslint-config": "^6.7.1",
|
||||||
"@eslint-react/eslint-plugin": "^1.52.2",
|
"@eslint-react/eslint-plugin": "^2.3.13",
|
||||||
"@next/eslint-plugin-next": "^15.3.4",
|
"@next/eslint-plugin-next": "^15.5.8",
|
||||||
"@tanstack/eslint-plugin-query": "^5.68.0",
|
"@tanstack/eslint-plugin-query": "^5.91.2",
|
||||||
"@types/node": "^22.13.16",
|
"@types/node": "^25.0.2",
|
||||||
"@types/react": "npm:types-react@19.0.0-rc.1",
|
"@types/react": "npm:types-react@19.0.0-rc.1",
|
||||||
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
|
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.29.0",
|
"@typescript-eslint/eslint-plugin": "^8.50.0",
|
||||||
"@typescript-eslint/parser": "^8.29.0",
|
"@typescript-eslint/parser": "^8.50.0",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^5.1.2",
|
||||||
"eslint": "^9.23.0",
|
"eslint": "^9.39.2",
|
||||||
"eslint-config-next": "15.0.2",
|
"eslint-config-next": "15.5.8",
|
||||||
"eslint-plugin-format": "^1.0.1",
|
"eslint-plugin-format": "^1.1.0",
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^7.0.1",
|
||||||
"eslint-plugin-react-refresh": "^0.4.20",
|
"eslint-plugin-react-refresh": "^0.4.25",
|
||||||
"jsdom": "^25.0.1",
|
"jsdom": "^27.3.0",
|
||||||
"postcss": "^8.5.3",
|
"postcss": "^8.5.6",
|
||||||
"prettier": "^3.5.3",
|
|
||||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
|
||||||
"tailwindcss": "^3.4.17",
|
"tailwindcss": "^3.4.17",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"tailwindcss-animated": "^1.1.2",
|
"tailwindcss-animated": "^1.1.2",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.9.3"
|
||||||
"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"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
59
frontend/public/json/adguardhome-sync.json
Normal file
59
frontend/public/json/adguardhome-sync.json
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -2,7 +2,7 @@
|
|||||||
"name": "Coolify",
|
"name": "Coolify",
|
||||||
"slug": "coolify",
|
"slug": "coolify",
|
||||||
"categories": [
|
"categories": [
|
||||||
6
|
3
|
||||||
],
|
],
|
||||||
"date_created": "2025-12-09",
|
"date_created": "2025-12-09",
|
||||||
"type": "ct",
|
"type": "ct",
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
"name": "Dokploy",
|
"name": "Dokploy",
|
||||||
"slug": "dokploy",
|
"slug": "dokploy",
|
||||||
"categories": [
|
"categories": [
|
||||||
6
|
3
|
||||||
],
|
],
|
||||||
"date_created": "2025-12-09",
|
"date_created": "2025-12-09",
|
||||||
"type": "ct",
|
"type": "ct",
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
"name": "Glance",
|
"name": "Glance",
|
||||||
"slug": "glance",
|
"slug": "glance",
|
||||||
"categories": [
|
"categories": [
|
||||||
9
|
10
|
||||||
],
|
],
|
||||||
"date_created": "2024-12-02",
|
"date_created": "2024-12-02",
|
||||||
"type": "ct",
|
"type": "ct",
|
||||||
|
|||||||
@ -23,7 +23,7 @@
|
|||||||
"ram": 512,
|
"ram": 512,
|
||||||
"hdd": 2,
|
"hdd": 2,
|
||||||
"os": "debian",
|
"os": "debian",
|
||||||
"version": "12"
|
"version": "13"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@ -19,8 +19,8 @@
|
|||||||
"type": "default",
|
"type": "default",
|
||||||
"script": "ct/homarr.sh",
|
"script": "ct/homarr.sh",
|
||||||
"resources": {
|
"resources": {
|
||||||
"cpu": 3,
|
"cpu": 2,
|
||||||
"ram": 6144,
|
"ram": 1024,
|
||||||
"hdd": 8,
|
"hdd": 8,
|
||||||
"os": "debian",
|
"os": "debian",
|
||||||
"version": "13"
|
"version": "13"
|
||||||
|
|||||||
48
frontend/public/json/koel.json
Normal file
48
frontend/public/json/koel.json
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "NetVisor",
|
"name": "Scanopy",
|
||||||
"slug": "netvisor",
|
"slug": "scanopy",
|
||||||
"categories": [
|
"categories": [
|
||||||
9
|
9
|
||||||
],
|
],
|
||||||
@ -9,15 +9,15 @@
|
|||||||
"updateable": true,
|
"updateable": true,
|
||||||
"privileged": false,
|
"privileged": false,
|
||||||
"interface_port": 60072,
|
"interface_port": 60072,
|
||||||
"documentation": "https://github.com/netvisor-io/netvisor",
|
"documentation": "https://github.com/scanopy/scanopy",
|
||||||
"config_path": "/opt/netvisor/.env, OIDC: /opt/netvisor/oidc.toml",
|
"config_path": "/opt/scanopy/.env, OIDC: /opt/scanopy/oidc.toml",
|
||||||
"website": "https://netvisor.io",
|
"website": "https://scanopy.net",
|
||||||
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/png/netvisor.png",
|
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/scanopy.webp",
|
||||||
"description": "Automatically discover and visually document network infrastructure",
|
"description": "Automatically discover and visually document network infrastructure",
|
||||||
"install_methods": [
|
"install_methods": [
|
||||||
{
|
{
|
||||||
"type": "default",
|
"type": "default",
|
||||||
"script": "ct/netvisor.sh",
|
"script": "ct/scanopy.sh",
|
||||||
"resources": {
|
"resources": {
|
||||||
"cpu": 2,
|
"cpu": 2,
|
||||||
"ram": 3072,
|
"ram": 3072,
|
||||||
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
export const InstallMethodSchema = z.object({
|
export const InstallMethodSchema = z.object({
|
||||||
type: z.enum(["default", "alpine"], {
|
type: z.enum(["default", "alpine"], {
|
||||||
errorMap: () => ({ message: "Type must be either 'default' or 'alpine'" }),
|
message: "Type must be either 'default' or 'alpine'",
|
||||||
}),
|
}),
|
||||||
script: z.string().min(1, "Script content cannot be empty"),
|
script: z.string().min(1, "Script content cannot be empty"),
|
||||||
resources: z.object({
|
resources: z.object({
|
||||||
@ -25,7 +25,7 @@ export const ScriptSchema = z.object({
|
|||||||
categories: z.array(z.number()),
|
categories: z.array(z.number()),
|
||||||
date_created: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").min(1, "Date is required"),
|
date_created: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").min(1, "Date is required"),
|
||||||
type: z.enum(["vm", "ct", "pve", "addon", "turnkey"], {
|
type: z.enum(["vm", "ct", "pve", "addon", "turnkey"], {
|
||||||
errorMap: () => ({ message: "Type must be either 'vm', 'ct', 'pve', 'addon' or 'turnkey'" }),
|
message: "Type must be either 'vm', 'ct', 'pve', 'addon' or 'turnkey'",
|
||||||
}),
|
}),
|
||||||
updateable: z.boolean(),
|
updateable: z.boolean(),
|
||||||
privileged: z.boolean(),
|
privileged: z.boolean(),
|
||||||
|
|||||||
@ -62,7 +62,7 @@ export default function JSONGenerator() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchCategories()
|
fetchCategories()
|
||||||
.then(setCategories)
|
.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]) => {
|
const updateScript = useCallback((key: keyof Script, value: Script[keyof Script]) => {
|
||||||
@ -75,14 +75,11 @@ export default function JSONGenerator() {
|
|||||||
|
|
||||||
if (updated.type === "pve") {
|
if (updated.type === "pve") {
|
||||||
scriptPath = `tools/pve/${updated.slug}.sh`;
|
scriptPath = `tools/pve/${updated.slug}.sh`;
|
||||||
}
|
} else if (updated.type === "addon") {
|
||||||
else if (updated.type === "addon") {
|
|
||||||
scriptPath = `tools/addon/${updated.slug}.sh`;
|
scriptPath = `tools/addon/${updated.slug}.sh`;
|
||||||
}
|
} else if (method.type === "alpine") {
|
||||||
else if (method.type === "alpine") {
|
|
||||||
scriptPath = `${updated.type}/alpine-${updated.slug}.sh`;
|
scriptPath = `${updated.type}/alpine-${updated.slug}.sh`;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
scriptPath = `${updated.type}/${updated.slug}.sh`;
|
scriptPath = `${updated.type}/${updated.slug}.sh`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,12 +142,9 @@ export default function JSONGenerator() {
|
|||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
{zodErrors && (
|
{zodErrors && (
|
||||||
<div className="mt-2 space-y-1">
|
<div className="mt-2 space-y-1">
|
||||||
{zodErrors.errors.map((error, index) => (
|
{zodErrors.issues.map((error, index) => (
|
||||||
<AlertDescription key={index} className="p-1 text-red-500">
|
<AlertDescription key={index} className="p-1 text-red-500">
|
||||||
{error.path.join(".")}
|
{error.path.join(".")} -{error.message}
|
||||||
{" "}
|
|
||||||
-
|
|
||||||
{error.message}
|
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@ -168,31 +162,25 @@ export default function JSONGenerator() {
|
|||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<Label>
|
<Label>
|
||||||
Name
|
Name <span className="text-red-500">*</span>
|
||||||
{" "}
|
|
||||||
<span className="text-red-500">*</span>
|
|
||||||
</Label>
|
</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>
|
||||||
<div>
|
<div>
|
||||||
<Label>
|
<Label>
|
||||||
Slug
|
Slug <span className="text-red-500">*</span>
|
||||||
{" "}
|
|
||||||
<span className="text-red-500">*</span>
|
|
||||||
</Label>
|
</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>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label>
|
<Label>
|
||||||
Logo
|
Logo <span className="text-red-500">*</span>
|
||||||
{" "}
|
|
||||||
<span className="text-red-500">*</span>
|
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
placeholder="Full logo URL"
|
placeholder="Full logo URL"
|
||||||
value={script.logo || ""}
|
value={script.logo || ""}
|
||||||
onChange={e => updateScript("logo", e.target.value || null)}
|
onChange={(e) => updateScript("logo", e.target.value || null)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@ -200,19 +188,17 @@ export default function JSONGenerator() {
|
|||||||
<Input
|
<Input
|
||||||
placeholder="Path to config file"
|
placeholder="Path to config file"
|
||||||
value={script.config_path || ""}
|
value={script.config_path || ""}
|
||||||
onChange={e => updateScript("config_path", e.target.value || null)}
|
onChange={(e) => updateScript("config_path", e.target.value || null)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label>
|
<Label>
|
||||||
Description
|
Description <span className="text-red-500">*</span>
|
||||||
{" "}
|
|
||||||
<span className="text-red-500">*</span>
|
|
||||||
</Label>
|
</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
placeholder="Example"
|
placeholder="Example"
|
||||||
value={script.description}
|
value={script.description}
|
||||||
onChange={e => updateScript("description", e.target.value)}
|
onChange={(e) => updateScript("description", e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Categories script={script} setScript={setScript} categories={categories} />
|
<Categories script={script} setScript={setScript} categories={categories} />
|
||||||
@ -241,7 +227,7 @@ export default function JSONGenerator() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2 w-full">
|
<div className="flex flex-col gap-2 w-full">
|
||||||
<Label>Type</Label>
|
<Label>Type</Label>
|
||||||
<Select value={script.type} onValueChange={value => updateScript("type", value)}>
|
<Select value={script.type} onValueChange={(value) => updateScript("type", value)}>
|
||||||
<SelectTrigger className="flex-1">
|
<SelectTrigger className="flex-1">
|
||||||
<SelectValue placeholder="Type" />
|
<SelectValue placeholder="Type" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
@ -256,29 +242,30 @@ export default function JSONGenerator() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="w-full flex gap-5">
|
<div className="w-full flex gap-5">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Switch checked={script.updateable} onCheckedChange={checked => updateScript("updateable", checked)} />
|
<Switch checked={script.updateable} onCheckedChange={(checked) => updateScript("updateable", checked)} />
|
||||||
<label>Updateable</label>
|
<label>Updateable</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Switch checked={script.privileged} onCheckedChange={checked => updateScript("privileged", checked)} />
|
<Switch checked={script.privileged} onCheckedChange={(checked) => updateScript("privileged", checked)} />
|
||||||
<label>Privileged</label>
|
<label>Privileged</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<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>
|
<label>Disabled</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{script.disable && (
|
{script.disable && (
|
||||||
<div>
|
<div>
|
||||||
<Label>
|
<Label>
|
||||||
Disable Description
|
Disable Description <span className="text-red-500">*</span>
|
||||||
{" "}
|
|
||||||
<span className="text-red-500">*</span>
|
|
||||||
</Label>
|
</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
placeholder="Explain why this script is disabled..."
|
placeholder="Explain why this script is disabled..."
|
||||||
value={script.disable_description || ""}
|
value={script.disable_description || ""}
|
||||||
onChange={e => updateScript("disable_description", e.target.value)}
|
onChange={(e) => updateScript("disable_description", e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -286,18 +273,18 @@ export default function JSONGenerator() {
|
|||||||
placeholder="Interface Port"
|
placeholder="Interface Port"
|
||||||
type="number"
|
type="number"
|
||||||
value={script.interface_port || ""}
|
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">
|
<div className="flex gap-2">
|
||||||
<Input
|
<Input
|
||||||
placeholder="Website URL"
|
placeholder="Website URL"
|
||||||
value={script.website || ""}
|
value={script.website || ""}
|
||||||
onChange={e => updateScript("website", e.target.value || null)}
|
onChange={(e) => updateScript("website", e.target.value || null)}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
placeholder="Documentation URL"
|
placeholder="Documentation URL"
|
||||||
value={script.documentation || ""}
|
value={script.documentation || ""}
|
||||||
onChange={e => updateScript("documentation", e.target.value || null)}
|
onChange={(e) => updateScript("documentation", e.target.value || null)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<InstallMethod script={script} setScript={setScript} setIsValid={setIsValid} setZodErrors={setZodErrors} />
|
<InstallMethod script={script} setScript={setScript} setIsValid={setIsValid} setZodErrors={setZodErrors} />
|
||||||
@ -305,20 +292,22 @@ export default function JSONGenerator() {
|
|||||||
<Input
|
<Input
|
||||||
placeholder="Username"
|
placeholder="Username"
|
||||||
value={script.default_credentials.username || ""}
|
value={script.default_credentials.username || ""}
|
||||||
onChange={e =>
|
onChange={(e) =>
|
||||||
updateScript("default_credentials", {
|
updateScript("default_credentials", {
|
||||||
...script.default_credentials,
|
...script.default_credentials,
|
||||||
username: e.target.value || null,
|
username: e.target.value || null,
|
||||||
})}
|
})
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
value={script.default_credentials.password || ""}
|
value={script.default_credentials.password || ""}
|
||||||
onChange={e =>
|
onChange={(e) =>
|
||||||
updateScript("default_credentials", {
|
updateScript("default_credentials", {
|
||||||
...script.default_credentials,
|
...script.default_credentials,
|
||||||
password: e.target.value || null,
|
password: e.target.value || null,
|
||||||
})}
|
})
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Note script={script} setScript={setScript} setIsValid={setIsValid} setZodErrors={setZodErrors} />
|
<Note script={script} setScript={setScript} setIsValid={setIsValid} setZodErrors={setZodErrors} />
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -1,50 +1,45 @@
|
|||||||
"use client"
|
"use client";
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react";
|
||||||
import * as RechartsPrimitive from "recharts"
|
import * as RechartsPrimitive from "recharts";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
// Format: { THEME_NAME: CSS_SELECTOR }
|
// Format: { THEME_NAME: CSS_SELECTOR }
|
||||||
const THEMES = { light: "", dark: ".dark" } as const
|
const THEMES = { light: "", dark: ".dark" } as const;
|
||||||
|
|
||||||
export type ChartConfig = {
|
export type ChartConfig = {
|
||||||
[k in string]: {
|
[k in string]: {
|
||||||
label?: React.ReactNode
|
label?: React.ReactNode;
|
||||||
icon?: React.ComponentType
|
icon?: React.ComponentType;
|
||||||
} & (
|
} & ({ color?: string; theme?: never } | { color?: never; theme: Record<keyof typeof THEMES, string> });
|
||||||
| { color?: string; theme?: never }
|
};
|
||||||
| { color?: never; theme: Record<keyof typeof THEMES, string> }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ChartContextProps = {
|
type ChartContextProps = {
|
||||||
config: ChartConfig
|
config: ChartConfig;
|
||||||
}
|
};
|
||||||
|
|
||||||
const ChartContext = React.createContext<ChartContextProps | null>(null)
|
const ChartContext = React.createContext<ChartContextProps | null>(null);
|
||||||
|
|
||||||
function useChart() {
|
function useChart() {
|
||||||
const context = React.useContext(ChartContext)
|
const context = React.useContext(ChartContext);
|
||||||
|
|
||||||
if (!context) {
|
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<
|
const ChartContainer = React.forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
React.ComponentProps<"div"> & {
|
React.ComponentProps<"div"> & {
|
||||||
config: ChartConfig
|
config: ChartConfig;
|
||||||
children: React.ComponentProps<
|
children: React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>["children"];
|
||||||
typeof RechartsPrimitive.ResponsiveContainer
|
|
||||||
>["children"]
|
|
||||||
}
|
}
|
||||||
>(({ id, className, children, config, ...props }, ref) => {
|
>(({ id, className, children, config, ...props }, ref) => {
|
||||||
const uniqueId = React.useId()
|
const uniqueId = React.useId();
|
||||||
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
|
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ChartContext.Provider value={{ config }}>
|
<ChartContext.Provider value={{ config }}>
|
||||||
@ -58,22 +53,18 @@ const ChartContainer = React.forwardRef<
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<ChartStyle id={chartId} config={config} />
|
<ChartStyle id={chartId} config={config} />
|
||||||
<RechartsPrimitive.ResponsiveContainer>
|
<RechartsPrimitive.ResponsiveContainer>{children}</RechartsPrimitive.ResponsiveContainer>
|
||||||
{children}
|
|
||||||
</RechartsPrimitive.ResponsiveContainer>
|
|
||||||
</div>
|
</div>
|
||||||
</ChartContext.Provider>
|
</ChartContext.Provider>
|
||||||
)
|
);
|
||||||
})
|
});
|
||||||
ChartContainer.displayName = "Chart"
|
ChartContainer.displayName = "Chart";
|
||||||
|
|
||||||
const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
|
const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
|
||||||
const colorConfig = Object.entries(config).filter(
|
const colorConfig = Object.entries(config).filter(([, config]) => config.theme || config.color);
|
||||||
([, config]) => config.theme || config.color
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!colorConfig.length) {
|
if (!colorConfig.length) {
|
||||||
return null
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -85,10 +76,8 @@ const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
|
|||||||
${prefix} [data-chart=${id}] {
|
${prefix} [data-chart=${id}] {
|
||||||
${colorConfig
|
${colorConfig
|
||||||
.map(([key, itemConfig]) => {
|
.map(([key, itemConfig]) => {
|
||||||
const color =
|
const color = itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.color;
|
||||||
itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
|
return color ? ` --color-${key}: ${color};` : null;
|
||||||
itemConfig.color
|
|
||||||
return color ? ` --color-${key}: ${color};` : null
|
|
||||||
})
|
})
|
||||||
.join("\n")}
|
.join("\n")}
|
||||||
}
|
}
|
||||||
@ -97,20 +86,37 @@ ${colorConfig
|
|||||||
.join("\n"),
|
.join("\n"),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const ChartTooltip = RechartsPrimitive.Tooltip
|
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 ChartTooltipContent = React.forwardRef<
|
const ChartTooltipContent = React.forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
|
Omit<React.ComponentProps<typeof RechartsPrimitive.Tooltip>, "content"> &
|
||||||
React.ComponentProps<"div"> & {
|
React.ComponentProps<"div"> & {
|
||||||
hideLabel?: boolean
|
active?: boolean;
|
||||||
hideIndicator?: boolean
|
payload?: TooltipPayloadItem[];
|
||||||
indicator?: "line" | "dot" | "dashed"
|
hideLabel?: boolean;
|
||||||
nameKey?: string
|
hideIndicator?: boolean;
|
||||||
labelKey?: string
|
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;
|
||||||
}
|
}
|
||||||
>(
|
>(
|
||||||
(
|
(
|
||||||
@ -131,49 +137,37 @@ const ChartTooltipContent = React.forwardRef<
|
|||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
const { config } = useChart()
|
const { config } = useChart();
|
||||||
|
|
||||||
const tooltipLabel = React.useMemo(() => {
|
const tooltipLabel = React.useMemo(() => {
|
||||||
if (hideLabel || !payload?.length) {
|
if (hideLabel || !payload?.length) {
|
||||||
return null
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [item] = payload
|
const [item] = payload;
|
||||||
const key = `${labelKey || item?.dataKey || item?.name || "value"}`
|
const key = `${labelKey || item?.dataKey || item?.name || "value"}`;
|
||||||
const itemConfig = getPayloadConfigFromPayload(config, item, key)
|
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
||||||
const value =
|
const value =
|
||||||
!labelKey && typeof label === "string"
|
!labelKey && typeof label === "string"
|
||||||
? config[label as keyof typeof config]?.label || label
|
? config[label as keyof typeof config]?.label || label
|
||||||
: itemConfig?.label
|
: itemConfig?.label;
|
||||||
|
|
||||||
if (labelFormatter) {
|
if (labelFormatter) {
|
||||||
return (
|
return <div className={cn("font-medium", labelClassName)}>{labelFormatter(value, payload)}</div>;
|
||||||
<div className={cn("font-medium", labelClassName)}>
|
|
||||||
{labelFormatter(value, payload)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return null
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className={cn("font-medium", labelClassName)}>{value}</div>
|
return <div className={cn("font-medium", labelClassName)}>{value}</div>;
|
||||||
}, [
|
}, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey]);
|
||||||
label,
|
|
||||||
labelFormatter,
|
|
||||||
payload,
|
|
||||||
hideLabel,
|
|
||||||
labelClassName,
|
|
||||||
config,
|
|
||||||
labelKey,
|
|
||||||
])
|
|
||||||
|
|
||||||
if (!active || !payload?.length) {
|
if (!active || !payload?.length) {
|
||||||
return null
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nestLabel = payload.length === 1 && indicator !== "dot"
|
const nestLabel = payload.length === 1 && indicator !== "dot";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -188,9 +182,9 @@ const ChartTooltipContent = React.forwardRef<
|
|||||||
{payload
|
{payload
|
||||||
.filter((item) => item.type !== "none")
|
.filter((item) => item.type !== "none")
|
||||||
.map((item, index) => {
|
.map((item, index) => {
|
||||||
const key = `${nameKey || item.name || item.dataKey || "value"}`
|
const key = `${nameKey || item.name || item.dataKey || "value"}`;
|
||||||
const itemConfig = getPayloadConfigFromPayload(config, item, key)
|
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
||||||
const indicatorColor = color || item.payload.fill || item.color
|
const indicatorColor = color || item.payload?.fill || item.color;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -209,16 +203,12 @@ const ChartTooltipContent = React.forwardRef<
|
|||||||
) : (
|
) : (
|
||||||
!hideIndicator && (
|
!hideIndicator && (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn("shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]", {
|
||||||
"shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]",
|
|
||||||
{
|
|
||||||
"h-2.5 w-2.5": indicator === "dot",
|
"h-2.5 w-2.5": indicator === "dot",
|
||||||
"w-1": indicator === "line",
|
"w-1": indicator === "line",
|
||||||
"w-0 border-[1.5px] border-dashed bg-transparent":
|
"w-0 border-[1.5px] border-dashed bg-transparent": indicator === "dashed",
|
||||||
indicator === "dashed",
|
|
||||||
"my-0.5": nestLabel && indicator === "dashed",
|
"my-0.5": nestLabel && indicator === "dashed",
|
||||||
}
|
})}
|
||||||
)}
|
|
||||||
style={
|
style={
|
||||||
{
|
{
|
||||||
"--color-bg": indicatorColor,
|
"--color-bg": indicatorColor,
|
||||||
@ -236,9 +226,7 @@ const ChartTooltipContent = React.forwardRef<
|
|||||||
>
|
>
|
||||||
<div className="grid gap-1.5">
|
<div className="grid gap-1.5">
|
||||||
{nestLabel ? tooltipLabel : null}
|
{nestLabel ? tooltipLabel : null}
|
||||||
<span className="text-muted-foreground">
|
<span className="text-muted-foreground">{itemConfig?.label || item.name}</span>
|
||||||
{itemConfig?.label || item.name}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
{item.value && (
|
{item.value && (
|
||||||
<span className="font-mono font-medium tabular-nums text-foreground">
|
<span className="font-mono font-medium tabular-nums text-foreground">
|
||||||
@ -249,56 +237,54 @@ const ChartTooltipContent = React.forwardRef<
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
ChartTooltipContent.displayName = "ChartTooltip"
|
ChartTooltipContent.displayName = "ChartTooltip";
|
||||||
|
|
||||||
const ChartLegend = RechartsPrimitive.Legend
|
const ChartLegend = RechartsPrimitive.Legend;
|
||||||
|
|
||||||
|
type LegendPayloadItem = {
|
||||||
|
value?: string;
|
||||||
|
type?: string;
|
||||||
|
dataKey?: string | number;
|
||||||
|
color?: string;
|
||||||
|
};
|
||||||
|
|
||||||
const ChartLegendContent = React.forwardRef<
|
const ChartLegendContent = React.forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
React.ComponentProps<"div"> &
|
React.ComponentProps<"div"> & {
|
||||||
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
|
payload?: LegendPayloadItem[];
|
||||||
hideIcon?: boolean
|
verticalAlign?: "top" | "bottom";
|
||||||
nameKey?: string
|
hideIcon?: boolean;
|
||||||
|
nameKey?: string;
|
||||||
}
|
}
|
||||||
>(
|
>(({ className, hideIcon = false, payload, verticalAlign = "bottom", nameKey }, ref) => {
|
||||||
(
|
const { config } = useChart();
|
||||||
{ className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },
|
|
||||||
ref
|
|
||||||
) => {
|
|
||||||
const { config } = useChart()
|
|
||||||
|
|
||||||
if (!payload?.length) {
|
if (!payload?.length) {
|
||||||
return null
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn("flex items-center justify-center gap-4", verticalAlign === "top" ? "pb-3" : "pt-3", className)}
|
||||||
"flex items-center justify-center gap-4",
|
|
||||||
verticalAlign === "top" ? "pb-3" : "pt-3",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{payload
|
{payload
|
||||||
.filter((item) => item.type !== "none")
|
.filter((item) => item.type !== "none")
|
||||||
.map((item) => {
|
.map((item) => {
|
||||||
const key = `${nameKey || item.dataKey || "value"}`
|
const key = `${nameKey || item.dataKey || "value"}`;
|
||||||
const itemConfig = getPayloadConfigFromPayload(config, item, key)
|
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={item.value}
|
key={item.value}
|
||||||
className={cn(
|
className={cn("flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground")}
|
||||||
"flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{itemConfig?.icon && !hideIcon ? (
|
{itemConfig?.icon && !hideIcon ? (
|
||||||
<itemConfig.icon />
|
<itemConfig.icon />
|
||||||
@ -312,58 +298,37 @@ const ChartLegendContent = React.forwardRef<
|
|||||||
)}
|
)}
|
||||||
{itemConfig?.label}
|
{itemConfig?.label}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
});
|
||||||
)
|
ChartLegendContent.displayName = "ChartLegend";
|
||||||
ChartLegendContent.displayName = "ChartLegend"
|
|
||||||
|
|
||||||
// Helper to extract item config from a payload.
|
// Helper to extract item config from a payload.
|
||||||
function getPayloadConfigFromPayload(
|
function getPayloadConfigFromPayload(config: ChartConfig, payload: unknown, key: string) {
|
||||||
config: ChartConfig,
|
|
||||||
payload: unknown,
|
|
||||||
key: string
|
|
||||||
) {
|
|
||||||
if (typeof payload !== "object" || payload === null) {
|
if (typeof payload !== "object" || payload === null) {
|
||||||
return undefined
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const payloadPayload =
|
const payloadPayload =
|
||||||
"payload" in payload &&
|
"payload" in payload && typeof payload.payload === "object" && payload.payload !== null
|
||||||
typeof payload.payload === "object" &&
|
|
||||||
payload.payload !== null
|
|
||||||
? payload.payload
|
? payload.payload
|
||||||
: undefined
|
: undefined;
|
||||||
|
|
||||||
let configLabelKey: string = key
|
let configLabelKey: string = key;
|
||||||
|
|
||||||
if (
|
if (key in payload && typeof payload[key as keyof typeof payload] === "string") {
|
||||||
key in payload &&
|
configLabelKey = payload[key as keyof typeof payload] as string;
|
||||||
typeof payload[key as keyof typeof payload] === "string"
|
|
||||||
) {
|
|
||||||
configLabelKey = payload[key as keyof typeof payload] as string
|
|
||||||
} else if (
|
} else if (
|
||||||
payloadPayload &&
|
payloadPayload &&
|
||||||
key in payloadPayload &&
|
key in payloadPayload &&
|
||||||
typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
|
typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
|
||||||
) {
|
) {
|
||||||
configLabelKey = payloadPayload[
|
configLabelKey = payloadPayload[key as keyof typeof payloadPayload] as string;
|
||||||
key as keyof typeof payloadPayload
|
|
||||||
] as string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return configLabelKey in config
|
return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config];
|
||||||
? config[configLabelKey]
|
|
||||||
: config[key as keyof typeof config]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export { ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent, ChartStyle };
|
||||||
ChartContainer,
|
|
||||||
ChartTooltip,
|
|
||||||
ChartTooltipContent,
|
|
||||||
ChartLegend,
|
|
||||||
ChartLegendContent,
|
|
||||||
ChartStyle,
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
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"],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@ -17,7 +17,8 @@ msg_info "Installing Dependencies"
|
|||||||
$STD apt install -y nginx
|
$STD apt install -y nginx
|
||||||
msg_ok "Installed Dependencies"
|
msg_ok "Installed Dependencies"
|
||||||
|
|
||||||
PHP_VERSION="8.3" PHP_MODULE="common,ctype,fileinfo,mysql,cli,tokenizer,dom,redis,session,openssl" PHP_FPM="YES" setup_php
|
PHP_VERSION="8.4"
|
||||||
|
PHP_MODULE="common,ctype,fileinfo,mysql,tokenizer,dom,redis" PHP_FPM="YES" setup_php
|
||||||
setup_composer
|
setup_composer
|
||||||
setup_mariadb
|
setup_mariadb
|
||||||
MARIADB_DB_NAME="2fauth_db" MARIADB_DB_USER="2fauth" setup_mariadb_db
|
MARIADB_DB_NAME="2fauth_db" MARIADB_DB_USER="2fauth" setup_mariadb_db
|
||||||
@ -65,7 +66,7 @@ server {
|
|||||||
error_page 404 /index.php;
|
error_page 404 /index.php;
|
||||||
|
|
||||||
location ~ \.php\$ {
|
location ~ \.php\$ {
|
||||||
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
|
fastcgi_pass unix:/var/run/php/php${PHP_VERSION}-fpm.sock;
|
||||||
fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name;
|
fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name;
|
||||||
include fastcgi_params;
|
include fastcgi_params;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,8 @@ fetch_and_deploy_gh_release "discopanel" "nickheyer/discopanel" "tarball" "lates
|
|||||||
setup_docker
|
setup_docker
|
||||||
|
|
||||||
msg_info "Setting up DiscoPanel"
|
msg_info "Setting up DiscoPanel"
|
||||||
|
cd /opt/discopanel
|
||||||
|
$STD make gen
|
||||||
cd /opt/discopanel/web/discopanel
|
cd /opt/discopanel/web/discopanel
|
||||||
$STD npm install
|
$STD npm install
|
||||||
$STD npm run build
|
$STD npm run build
|
||||||
|
|||||||
@ -14,27 +14,22 @@ network_check
|
|||||||
update_os
|
update_os
|
||||||
|
|
||||||
msg_info "Installing Dependencies"
|
msg_info "Installing Dependencies"
|
||||||
$STD apt-get install -y apt-transport-https
|
$STD apt 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"
|
msg_ok "Installed Dependencies"
|
||||||
|
|
||||||
RELEASE=$(curl -fsSL "https://api.github.com/repos/linuxserver/Heimdall/releases/latest" | awk '/tag_name/{print $4;exit}' FS='[""]')
|
PHP_VERSION="8.4" PHP_MODULE="bz2,sqlite3" PHP_FPM="YES" setup_php
|
||||||
echo "${RELEASE}" >/opt/"${APPLICATION}"_version.txt
|
setup_composer
|
||||||
msg_info "Installing Heimdall Dashboard ${RELEASE}"
|
fetch_and_deploy_gh_release "Heimdall" "linuxserver/Heimdall" "tarball"
|
||||||
curl -fsSL "https://github.com/linuxserver/Heimdall/archive/${RELEASE}.tar.gz" -o "${RELEASE}".tar.gz
|
|
||||||
tar xzf "${RELEASE}".tar.gz
|
msg_info "Setting up Heimdall-Dashboard"
|
||||||
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
|
cd /opt/Heimdall
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
$STD php artisan key:generate
|
$STD php artisan key:generate
|
||||||
msg_ok "Installed Heimdall Dashboard ${RELEASE}"
|
msg_ok "Setup Heimdall-Dashboard"
|
||||||
|
|
||||||
msg_info "Creating Service"
|
msg_info "Creating Service"
|
||||||
service_path="/etc/systemd/system/heimdall.service"
|
cat <<EOF >/etc/systemd/system/heimdall.service
|
||||||
echo "[Unit]
|
[Unit]
|
||||||
Description=Heimdall
|
Description=Heimdall
|
||||||
After=network.target
|
After=network.target
|
||||||
|
|
||||||
@ -44,14 +39,16 @@ RestartSec=5
|
|||||||
Type=simple
|
Type=simple
|
||||||
User=root
|
User=root
|
||||||
WorkingDirectory=/opt/Heimdall
|
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
|
TimeoutStopSec=30
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target" >$service_path
|
WantedBy=multi-user.target"
|
||||||
|
EOF
|
||||||
systemctl enable -q --now heimdall
|
systemctl enable -q --now heimdall
|
||||||
cd /opt/Heimdall
|
cd /opt/Heimdall
|
||||||
COMPOSER_ALLOW_SUPERUSER=1 composer dump-autoload &>/dev/null
|
export COMPOSER_ALLOW_SUPERUSER=1
|
||||||
|
$STD composer dump-autoload
|
||||||
systemctl restart heimdall.service
|
systemctl restart heimdall.service
|
||||||
msg_ok "Created Service"
|
msg_ok "Created Service"
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Copyright (c) 2021-2025 community-scripts ORG
|
# Copyright (c) 2021-2025 community-scripts ORG
|
||||||
# Author: MickLesk (Canbiz)
|
# Author: MickLesk (CanbiZ) | Co-Author: CrazyWolf13
|
||||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||||
# Source: https://github.com/homarr-labs/homarr
|
# Source: https://github.com/homarr-labs/homarr
|
||||||
|
|
||||||
@ -16,28 +16,24 @@ update_os
|
|||||||
msg_info "Installing Dependencies"
|
msg_info "Installing Dependencies"
|
||||||
$STD apt install -y \
|
$STD apt install -y \
|
||||||
redis-server \
|
redis-server \
|
||||||
ca-certificates \
|
|
||||||
make \
|
|
||||||
g++ \
|
|
||||||
build-essential \
|
|
||||||
nginx \
|
nginx \
|
||||||
gettext \
|
gettext \
|
||||||
jq \
|
openssl \
|
||||||
openssl
|
musl-dev
|
||||||
msg_ok "Installed Dependencies"
|
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_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
|
setup_nodejs
|
||||||
fetch_and_deploy_gh_release "homarr" "homarr-labs/homarr"
|
fetch_and_deploy_gh_release "homarr" "homarr-labs/homarr" "prebuild" "latest" "/opt/homarr" "build-amd64.tar.gz"
|
||||||
|
|
||||||
msg_info "Installing Homarr (Patience)"
|
msg_info "Installing Homarr"
|
||||||
cd /opt
|
# 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
|
||||||
mkdir -p /opt/homarr_db
|
mkdir -p /opt/homarr_db
|
||||||
touch /opt/homarr_db/db.sqlite
|
touch /opt/homarr_db/db.sqlite
|
||||||
SECRET_ENCRYPTION_KEY="$(openssl rand -hex 32)"
|
SECRET_ENCRYPTION_KEY="$(openssl rand -hex 32)"
|
||||||
cd /opt/homarr
|
cd /opt/homarr
|
||||||
cat <<EOF >/opt/homarr/.env
|
cat <<EOF >/opt/homarr.env
|
||||||
DB_DRIVER='better-sqlite3'
|
DB_DRIVER='better-sqlite3'
|
||||||
DB_DIALECT='sqlite'
|
DB_DIALECT='sqlite'
|
||||||
SECRET_ENCRYPTION_KEY='${SECRET_ENCRYPTION_KEY}'
|
SECRET_ENCRYPTION_KEY='${SECRET_ENCRYPTION_KEY}'
|
||||||
@ -45,54 +41,28 @@ DB_URL='/opt/homarr_db/db.sqlite'
|
|||||||
TURBO_TELEMETRY_DISABLED=1
|
TURBO_TELEMETRY_DISABLED=1
|
||||||
AUTH_PROVIDERS='credentials'
|
AUTH_PROVIDERS='credentials'
|
||||||
NODE_ENV='production'
|
NODE_ENV='production'
|
||||||
|
REDIS_IS_EXTERNAL='true'
|
||||||
EOF
|
EOF
|
||||||
$STD pnpm install --recursive --frozen-lockfile --shamefully-hoist
|
|
||||||
$STD pnpm build
|
|
||||||
msg_ok "Installed Homarr"
|
msg_ok "Installed Homarr"
|
||||||
|
|
||||||
msg_info "Copying build and config files"
|
msg_info "Copying 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
|
mkdir -p /appdata/redis
|
||||||
cp /opt/homarr/packages/redis/redis.conf /opt/homarr/redis.conf
|
chown -R redis:redis /appdata/redis
|
||||||
mkdir -p /etc/nginx/templates
|
chmod 744 /appdata/redis
|
||||||
|
cp /opt/homarr/redis.conf /etc/redis/redis.conf
|
||||||
rm /etc/nginx/nginx.conf
|
rm /etc/nginx/nginx.conf
|
||||||
|
mkdir -p /etc/nginx/templates
|
||||||
cp /opt/homarr/nginx.conf /etc/nginx/templates/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
|
echo $'#!/bin/bash\ncd /opt/homarr/apps/cli && node ./cli.cjs "$@"' >/usr/bin/homarr
|
||||||
chmod +x /usr/bin/homarr
|
chmod +x /usr/bin/homarr
|
||||||
mkdir /opt/homarr/build
|
msg_ok "Copied config files"
|
||||||
cp ./node_modules/better-sqlite3/build/Release/better_sqlite3.node ./build/better_sqlite3.node
|
|
||||||
msg_ok "Finished copying"
|
|
||||||
|
|
||||||
msg_info "Creating Services"
|
msg_info "Creating Services"
|
||||||
cat <<'EOF' >/opt/run_homarr.sh
|
mkdir -p /etc/systemd/system/redis-server.service.d/
|
||||||
#!/bin/bash
|
cat <<EOF >/etc/systemd/system/redis-server.service.d/override.conf
|
||||||
set -a
|
[Service]
|
||||||
source /opt/homarr/.env
|
ReadWritePaths=-/appdata/redis -/var/lib/redis -/var/log/redis -/var/run/redis -/etc/redis
|
||||||
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
|
EOF
|
||||||
chmod +x /opt/run_homarr.sh
|
|
||||||
cat <<EOF >/etc/systemd/system/homarr.service
|
cat <<EOF >/etc/systemd/system/homarr.service
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Homarr Service
|
Description=Homarr Service
|
||||||
@ -101,14 +71,17 @@ After=network.target
|
|||||||
[Service]
|
[Service]
|
||||||
Type=exec
|
Type=exec
|
||||||
WorkingDirectory=/opt/homarr
|
WorkingDirectory=/opt/homarr
|
||||||
EnvironmentFile=-/opt/homarr/.env
|
EnvironmentFile=-/opt/homarr.env
|
||||||
ExecStart=/opt/run_homarr.sh
|
ExecStart=/opt/homarr/run.sh
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
EOF
|
EOF
|
||||||
|
chmod +x /opt/homarr/run.sh
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable -q --now redis-server && sleep 5
|
||||||
systemctl enable -q --now homarr
|
systemctl enable -q --now homarr
|
||||||
msg_ok "Created Service"
|
msg_ok "Created Services"
|
||||||
|
|
||||||
motd_ssh
|
motd_ssh
|
||||||
customize
|
customize
|
||||||
|
|||||||
189
install/koel-install.sh
Normal file
189
install/koel-install.sh
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
#!/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
|
||||||
@ -1,142 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Copyright (c) 2021-2025 community-scripts ORG
|
|
||||||
# Author: vhsdream
|
|
||||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
|
||||||
# Source: https://github.com/netvisor-io/netvisor
|
|
||||||
|
|
||||||
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 \
|
|
||||||
build-essential \
|
|
||||||
libssl-dev \
|
|
||||||
pkg-config
|
|
||||||
msg_ok "Installed Dependencies"
|
|
||||||
|
|
||||||
PG_VERSION=17 setup_postgresql
|
|
||||||
NODE_VERSION="24" setup_nodejs
|
|
||||||
PG_DB_NAME="netvisor_db" PG_DB_USER="netvisor" PG_DB_GRANT_SUPERUSER="true" setup_postgresql_db
|
|
||||||
|
|
||||||
fetch_and_deploy_gh_release "netvisor" "netvisor-io/netvisor" "tarball" "latest" "/opt/netvisor"
|
|
||||||
|
|
||||||
TOOLCHAIN="$(grep "channel" /opt/netvisor/backend/rust-toolchain.toml | awk -F\" '{print $2}')"
|
|
||||||
RUST_TOOLCHAIN=$TOOLCHAIN setup_rust
|
|
||||||
|
|
||||||
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"
|
|
||||||
|
|
||||||
msg_info "Configuring server for first-run"
|
|
||||||
LOCAL_IP="$(hostname -I | awk '{print $1}')"
|
|
||||||
cat <<EOF >/opt/netvisor/.env
|
|
||||||
### - SERVER
|
|
||||||
NETVISOR_DATABASE_URL=postgresql://$PG_DB_USER:$PG_DB_PASS@localhost:5432/$PG_DB_NAME
|
|
||||||
NETVISOR_WEB_EXTERNAL_PATH="/opt/netvisor/ui/build"
|
|
||||||
NETVISOR_PUBLIC_URL=http://${LOCAL_IP}:60072
|
|
||||||
NETVISOR_SERVER_PORT=60072
|
|
||||||
NETVISOR_LOG_LEVEL=info
|
|
||||||
NETVISOR_INTEGRATED_DAEMON_URL=http://127.0.0.1:60073
|
|
||||||
## - uncomment to disable signups
|
|
||||||
# NETVISOR_DISABLE_REGISTRATION=true
|
|
||||||
## - uncomment when using TLS
|
|
||||||
# NETVISOR_USE_SECURE_SESSION_COOKIES=true
|
|
||||||
## - see https://github.com/imbolc/axum-client-ip?tab=readme-ov-file#configurable-vs-specific-extractors
|
|
||||||
## - before uncommenting the below
|
|
||||||
# NETVISOR_CLIENT_IP_SOURCE=
|
|
||||||
|
|
||||||
### - SMTP (password reset and notifications - optional)
|
|
||||||
# NETVISOR_SMTP_RELAY=smtp.gmail.com:587
|
|
||||||
# NETVISOR_SMTP_USERNAME=your-email@gmail.com
|
|
||||||
# NETVISOR_SMTP_PASSWORD=your-app-password
|
|
||||||
# NETVISOR_SMTP_EMAIL=netvisor@yourdomain.tld
|
|
||||||
|
|
||||||
### - INTEGRATED DAEMON
|
|
||||||
NETVISOR_SERVER_URL=http://127.0.0.1:60072
|
|
||||||
NETVISOR_BIND_ADDRESS=0.0.0.0
|
|
||||||
NETVISOR_NAME="netvisor-daemon"
|
|
||||||
NETVISOR_HEARTBEAT_INTERVAL=30
|
|
||||||
|
|
||||||
### - see https://github.com/netvisor-io/netvisor/blob/main/docs/CONFIGURATION.md for more options
|
|
||||||
EOF
|
|
||||||
|
|
||||||
cat <<EOF >/etc/systemd/system/netvisor-server.service
|
|
||||||
[Unit]
|
|
||||||
Description=NetVisor Network Discovery Server
|
|
||||||
After=network.target postgresql.service
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
WorkingDirectory=/opt/netvisor/backend
|
|
||||||
EnvironmentFile=/opt/netvisor/.env
|
|
||||||
ExecStart=/usr/bin/netvisor-server
|
|
||||||
Restart=always
|
|
||||||
RestartSec=10
|
|
||||||
StandardOutput=journal
|
|
||||||
StandardError=journal
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
EOF
|
|
||||||
|
|
||||||
systemctl enable -q --now netvisor-server
|
|
||||||
|
|
||||||
# Creating short script to configure netvisor-daemon
|
|
||||||
cat <<EOF >~/configure_daemon.sh
|
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
echo "Auto-configuring integrated daemon..."
|
|
||||||
|
|
||||||
NETWORK_ID="\$(sudo -u postgres psql -1 -t -d "${PG_DB_NAME}" -c 'SELECT id FROM networks;')"
|
|
||||||
API_KEY="\$(sudo -u postgres psql -1 -t -d "${PG_DB_NAME}" -c 'SELECT key FROM api_keys;')"
|
|
||||||
|
|
||||||
cat <<END >/etc/systemd/system/netvisor-daemon.service
|
|
||||||
[Unit]
|
|
||||||
Description=NetVisor Network Discovery Daemon
|
|
||||||
After=network-online.target
|
|
||||||
Wants=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
User=root
|
|
||||||
ExecStart=/usr/bin/netvisor-daemon --server-url http://127.0.0.1:60072 --network-id \${NETWORK_ID} --daemon-api-key \${API_KEY} --mode push
|
|
||||||
Restart=always
|
|
||||||
RestartSec=10
|
|
||||||
StandardOutput=journal
|
|
||||||
StandardError=journal
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
END
|
|
||||||
|
|
||||||
systemctl enable -q --now netvisor-daemon
|
|
||||||
echo "NetVisor daemon configured and running"
|
|
||||||
|
|
||||||
EOF
|
|
||||||
chmod +x ~/configure_daemon.sh
|
|
||||||
msg_ok "Netvisor server running - please create an account in the UI to continue."
|
|
||||||
|
|
||||||
motd_ssh
|
|
||||||
customize
|
|
||||||
cleanup_lxc
|
|
||||||
@ -230,6 +230,8 @@ systemctl enable -q --now gerbil
|
|||||||
cat <<'EOF' >/etc/systemd/system/traefik.service
|
cat <<'EOF' >/etc/systemd/system/traefik.service
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Traefik is an open-source Edge Router that makes publishing your services a fun and easy experience
|
Description=Traefik is an open-source Edge Router that makes publishing your services a fun and easy experience
|
||||||
|
Wants=network-online.target
|
||||||
|
After=network-online.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=notify
|
Type=notify
|
||||||
|
|||||||
@ -28,6 +28,8 @@ $STD apt install -y \
|
|||||||
lsb-release
|
lsb-release
|
||||||
|
|
||||||
echo 'export PATH=/usr/local:$PATH' >>~/.bashrc
|
echo 'export PATH=/usr/local:$PATH' >>~/.bashrc
|
||||||
|
echo '# Load PhotoPrism environment variables for CLI tools' >>~/.bashrc
|
||||||
|
echo 'export $(grep -v "^#" /opt/photoprism/config/.env | xargs)' >>~/.bashrc
|
||||||
export PATH=/usr/local:$PATH
|
export PATH=/usr/local:$PATH
|
||||||
msg_ok "Installed Dependencies"
|
msg_ok "Installed Dependencies"
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,11 @@ setting_up_container
|
|||||||
network_check
|
network_check
|
||||||
update_os
|
update_os
|
||||||
|
|
||||||
|
msg_info "Installing Dependencies"
|
||||||
|
$STD apt install -y rsyslog
|
||||||
|
systemctl enable -q --now rsyslog
|
||||||
|
msg_ok "Installed Dependencies"
|
||||||
|
|
||||||
msg_info "Installing Proxmox Datacenter Manager"
|
msg_info "Installing Proxmox Datacenter Manager"
|
||||||
curl -fsSL https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg -o /usr/share/keyrings/proxmox-archive-keyring.gpg
|
curl -fsSL https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg -o /usr/share/keyrings/proxmox-archive-keyring.gpg
|
||||||
setup_deb822_repo \
|
setup_deb822_repo \
|
||||||
@ -22,15 +27,15 @@ setup_deb822_repo \
|
|||||||
"trixie" \
|
"trixie" \
|
||||||
"pdm-no-subscription"
|
"pdm-no-subscription"
|
||||||
|
|
||||||
cat <<EOF >/etc/apt/sources.list.d/pdm-test.sources
|
setup_deb822_repo \
|
||||||
Types: deb
|
"pdm-test" \
|
||||||
URIs: http://download.proxmox.com/debian/pdm
|
"https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg" \
|
||||||
Suites: trixie
|
"http://download.proxmox.com/debian/pdm" \
|
||||||
Components: pdm-test
|
"trixie" \
|
||||||
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
|
"pdm-test" \
|
||||||
Enabled: false
|
"" \
|
||||||
EOF
|
"false"
|
||||||
$STD apt update
|
|
||||||
DEBIAN_FRONTEND=noninteractive
|
DEBIAN_FRONTEND=noninteractive
|
||||||
$STD apt -o Dpkg::Options::="--force-confdef" \
|
$STD apt -o Dpkg::Options::="--force-confdef" \
|
||||||
-o Dpkg::Options::="--force-confold" \
|
-o Dpkg::Options::="--force-confold" \
|
||||||
|
|||||||
140
install/scanopy-install.sh
Normal file
140
install/scanopy-install.sh
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# 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 /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 \
|
||||||
|
build-essential \
|
||||||
|
libssl-dev \
|
||||||
|
pkg-config
|
||||||
|
msg_ok "Installed Dependencies"
|
||||||
|
|
||||||
|
PG_VERSION=17 setup_postgresql
|
||||||
|
NODE_VERSION="24" setup_nodejs
|
||||||
|
PG_DB_NAME="scanopy_db" PG_DB_USER="scanopy" PG_DB_GRANT_SUPERUSER="true" setup_postgresql_db
|
||||||
|
fetch_and_deploy_gh_release "scanopy" "scanopy/scanopy" "tarball" "latest" "/opt/scanopy"
|
||||||
|
TOOLCHAIN="$(grep "channel" /opt/scanopy/backend/rust-toolchain.toml | awk -F\" '{print $2}')"
|
||||||
|
RUST_TOOLCHAIN=$TOOLCHAIN setup_rust
|
||||||
|
|
||||||
|
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 "Configuring server for first-run"
|
||||||
|
LOCAL_IP="$(hostname -I | awk '{print $1}')"
|
||||||
|
cat <<EOF >/opt/scanopy/.env
|
||||||
|
### - SERVER
|
||||||
|
scanopy_DATABASE_URL=postgresql://$PG_DB_USER:$PG_DB_PASS@localhost:5432/$PG_DB_NAME
|
||||||
|
scanopy_WEB_EXTERNAL_PATH="/opt/scanopy/ui/build"
|
||||||
|
scanopy_PUBLIC_URL=http://${LOCAL_IP}:60072
|
||||||
|
scanopy_SERVER_PORT=60072
|
||||||
|
scanopy_LOG_LEVEL=info
|
||||||
|
scanopy_INTEGRATED_DAEMON_URL=http://127.0.0.1:60073
|
||||||
|
## - uncomment to disable signups
|
||||||
|
# scanopy_DISABLE_REGISTRATION=true
|
||||||
|
## - uncomment when using TLS
|
||||||
|
# scanopy_USE_SECURE_SESSION_COOKIES=true
|
||||||
|
## - see https://github.com/imbolc/axum-client-ip?tab=readme-ov-file#configurable-vs-specific-extractors
|
||||||
|
## - before uncommenting the below
|
||||||
|
# scanopy_CLIENT_IP_SOURCE=
|
||||||
|
|
||||||
|
### - SMTP (password reset and notifications - optional)
|
||||||
|
# scanopy_SMTP_RELAY=smtp.gmail.com:587
|
||||||
|
# scanopy_SMTP_USERNAME=your-email@gmail.com
|
||||||
|
# scanopy_SMTP_PASSWORD=your-app-password
|
||||||
|
# scanopy_SMTP_EMAIL=scanopy@yourdomain.tld
|
||||||
|
|
||||||
|
### - INTEGRATED DAEMON
|
||||||
|
scanopy_SERVER_URL=http://127.0.0.1:60072
|
||||||
|
scanopy_BIND_ADDRESS=0.0.0.0
|
||||||
|
scanopy_NAME="scanopy-daemon"
|
||||||
|
scanopy_HEARTBEAT_INTERVAL=30
|
||||||
|
|
||||||
|
### - see https://github.com/scanopy/scanopy/blob/main/docs/CONFIGURATION.md for more options
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF >/etc/systemd/system/scanopy-server.service
|
||||||
|
[Unit]
|
||||||
|
Description=Scanopy Network Discovery Server
|
||||||
|
After=network.target postgresql.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
WorkingDirectory=/opt/scanopy/backend
|
||||||
|
EnvironmentFile=/opt/scanopy/.env
|
||||||
|
ExecStart=/usr/bin/scanopy-server
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
systemctl enable -q --now scanopy-server
|
||||||
|
|
||||||
|
# Creating short script to configure scanopy-daemon
|
||||||
|
cat <<EOF >~/configure_daemon.sh
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
echo "Auto-configuring integrated daemon..."
|
||||||
|
|
||||||
|
NETWORK_ID="\$(sudo -u postgres psql -1 -t -d "${PG_DB_NAME}" -c 'SELECT id FROM networks;')"
|
||||||
|
API_KEY="\$(sudo -u postgres psql -1 -t -d "${PG_DB_NAME}" -c 'SELECT key FROM api_keys;')"
|
||||||
|
|
||||||
|
cat <<END >/etc/systemd/system/scanopy-daemon.service
|
||||||
|
[Unit]
|
||||||
|
Description=Scanopy Network Discovery Daemon
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=root
|
||||||
|
ExecStart=/usr/bin/scanopy-daemon --server-url http://127.0.0.1:60072 --network-id \${NETWORK_ID} --daemon-api-key \${API_KEY} --mode push
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
END
|
||||||
|
|
||||||
|
systemctl enable -q --now scanopy-daemon
|
||||||
|
echo "Scanopy daemon configured and running"
|
||||||
|
|
||||||
|
EOF
|
||||||
|
chmod +x ~/configure_daemon.sh
|
||||||
|
msg_ok "Scanopy server running - please create an account in the UI to continue."
|
||||||
|
|
||||||
|
motd_ssh
|
||||||
|
customize
|
||||||
|
cleanup_lxc
|
||||||
@ -27,9 +27,9 @@ chown -R sonarqube:sonarqube /opt/sonarqube
|
|||||||
chmod -R 755 /opt/sonarqube
|
chmod -R 755 /opt/sonarqube
|
||||||
mkdir -p /opt/sonarqube/conf
|
mkdir -p /opt/sonarqube/conf
|
||||||
cat <<EOF >/opt/sonarqube/conf/sonar.properties
|
cat <<EOF >/opt/sonarqube/conf/sonar.properties
|
||||||
sonar.jdbc.username=${DB_USER}
|
sonar.jdbc.username=${PG_DB_USER}
|
||||||
sonar.jdbc.password=${DB_PASS}
|
sonar.jdbc.password=${PG_DB_PASS}
|
||||||
sonar.jdbc.url=jdbc:postgresql://localhost/${DB_NAME}
|
sonar.jdbc.url=jdbc:postgresql://localhost/${PG_DB_NAME}
|
||||||
sonar.web.host=0.0.0.0
|
sonar.web.host=0.0.0.0
|
||||||
sonar.web.port=9000
|
sonar.web.port=9000
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
@ -17,12 +17,10 @@ update_os
|
|||||||
msg_info "Installing Dependencies"
|
msg_info "Installing Dependencies"
|
||||||
$STD apt install -y \
|
$STD apt install -y \
|
||||||
build-essential \
|
build-essential \
|
||||||
make \
|
libpq-dev
|
||||||
libpq-dev \
|
|
||||||
ca-certificates
|
|
||||||
msg_ok "Installed Dependencies"
|
msg_ok "Installed Dependencies"
|
||||||
|
|
||||||
msg_info "Setup Python3"
|
msg_info "Setting up Python3"
|
||||||
$STD apt install -y \
|
$STD apt install -y \
|
||||||
python3-dev \
|
python3-dev \
|
||||||
python3-setuptools \
|
python3-setuptools \
|
||||||
@ -30,17 +28,13 @@ $STD apt install -y \
|
|||||||
python3-pip
|
python3-pip
|
||||||
msg_ok "Setup Python3"
|
msg_ok "Setup Python3"
|
||||||
|
|
||||||
msg_info "Installing Spoolman"
|
fetch_and_deploy_gh_release "spoolman" "Donkie/Spoolman" "prebuild" "latest" "/opt/spoolman" "spoolman.zip"
|
||||||
RELEASE=$(curl -fsSL https://github.com/Donkie/Spoolman/releases/latest | grep "title>Release" | cut -d " " -f 4)
|
|
||||||
cd /opt
|
msg_info "Setting up Spoolman"
|
||||||
curl -fsSL "https://github.com/Donkie/Spoolman/releases/download/$RELEASE/spoolman.zip" -o "spoolman.zip"
|
cd /opt/spoolman
|
||||||
$STD unzip spoolman.zip -d spoolman
|
|
||||||
rm -rf spoolman.zip
|
|
||||||
cd spoolman
|
|
||||||
$STD pip3 install --upgrade --ignore-installed -r requirements.txt
|
$STD pip3 install --upgrade --ignore-installed -r requirements.txt
|
||||||
curl -fsSL "https://raw.githubusercontent.com/Donkie/Spoolman/master/.env.example" -o ".env"
|
cp .env.example .env
|
||||||
echo "${RELEASE}" >/opt/${APPLICATION}_version.txt
|
msg_ok "Setup Spoolman"
|
||||||
msg_ok "Installed Spoolman"
|
|
||||||
|
|
||||||
msg_info "Creating Service"
|
msg_info "Creating Service"
|
||||||
cat <<'EOF' >/etc/systemd/system/spoolman.service
|
cat <<'EOF' >/etc/systemd/system/spoolman.service
|
||||||
|
|||||||
@ -61,7 +61,9 @@ else
|
|||||||
fi
|
fi
|
||||||
msg_ok "Installed and Set Up Intel Hardware Acceleration"
|
msg_ok "Installed and Set Up Intel Hardware Acceleration"
|
||||||
|
|
||||||
fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "singlefile" "latest" "/opt/tunarr" "*linux-x64"
|
fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "prebuild" "latest" "/opt/tunarr" "*linux-x64.tar.gz"
|
||||||
|
cd /opt/tunarr
|
||||||
|
mv tunarr* tunarr
|
||||||
fetch_and_deploy_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg" "prebuild" "latest" "/opt/ErsatzTV-ffmpeg" "*-linux64-gpl-7.1.tar.xz"
|
fetch_and_deploy_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg" "prebuild" "latest" "/opt/ErsatzTV-ffmpeg" "*-linux64-gpl-7.1.tar.xz"
|
||||||
|
|
||||||
msg_info "Set ErsatzTV-ffmpeg links"
|
msg_info "Set ErsatzTV-ffmpeg links"
|
||||||
|
|||||||
@ -13,16 +13,16 @@ setting_up_container
|
|||||||
network_check
|
network_check
|
||||||
update_os
|
update_os
|
||||||
|
|
||||||
NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs
|
NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs
|
||||||
PG_VERSION="17" setup_postgresql
|
PG_VERSION="17" setup_postgresql
|
||||||
PG_DB_NAME="umamidb" PG_DB_USER="umami" setup_postgresql_db
|
PG_DB_NAME="umamidb" PG_DB_USER="umami" setup_postgresql_db
|
||||||
fetch_and_deploy_gh_release "umami" "umami-software/umami" "tarball"
|
fetch_and_deploy_gh_release "umami" "umami-software/umami" "tarball"
|
||||||
|
|
||||||
msg_info "Configuring Umami"
|
msg_info "Configuring Umami"
|
||||||
cd /opt/umami
|
cd /opt/umami
|
||||||
$STD yarn install
|
$STD pnpm install
|
||||||
echo -e "DATABASE_URL=postgresql://$PG_DB_USER:$PG_DB_PASS@localhost:5432/$PG_DB_NAME" >>/opt/umami/.env
|
echo -e "DATABASE_URL=postgresql://$PG_DB_USER:$PG_DB_PASS@localhost:5432/$PG_DB_NAME" >>/opt/umami/.env
|
||||||
$STD yarn run build
|
$STD pnpm run build
|
||||||
msg_ok "Configured Umami"
|
msg_ok "Configured Umami"
|
||||||
|
|
||||||
msg_info "Creating Service"
|
msg_info "Creating Service"
|
||||||
@ -35,7 +35,7 @@ Type=simple
|
|||||||
Restart=always
|
Restart=always
|
||||||
User=root
|
User=root
|
||||||
WorkingDirectory=/opt/umami
|
WorkingDirectory=/opt/umami
|
||||||
ExecStart=/usr/bin/yarn run start
|
ExecStart=/usr/bin/pnpm run start
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|||||||
@ -32,10 +32,46 @@ echo "# Memory-optimized settings for small-scale deployments" >> /etc/valkey/va
|
|||||||
echo "maxmemory ${MAXMEMORY_MB}mb" >> /etc/valkey/valkey.conf
|
echo "maxmemory ${MAXMEMORY_MB}mb" >> /etc/valkey/valkey.conf
|
||||||
echo "maxmemory-policy allkeys-lru" >> /etc/valkey/valkey.conf
|
echo "maxmemory-policy allkeys-lru" >> /etc/valkey/valkey.conf
|
||||||
echo "maxmemory-samples 10" >> /etc/valkey/valkey.conf
|
echo "maxmemory-samples 10" >> /etc/valkey/valkey.conf
|
||||||
|
msg_ok "Installed Valkey"
|
||||||
|
|
||||||
|
echo
|
||||||
|
read -r -p "${TAB3}Enable TLS for Valkey (Sentinel mode does not supported)? [y/N]: " prompt
|
||||||
|
if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then
|
||||||
|
read -r -p "${TAB3}Use TLS-only mode (disable TCP port 6379)? [y/N]: " tls_only
|
||||||
|
msg_info "Configuring TLS for Valkey..."
|
||||||
|
|
||||||
|
create_self_signed_cert "Valkey"
|
||||||
|
TLS_DIR="/etc/ssl/valkey"
|
||||||
|
TLS_CERT="$TLS_DIR/valkey.crt"
|
||||||
|
TLS_KEY="$TLS_DIR/valkey.key"
|
||||||
|
chown valkey:valkey "$TLS_CERT" "$TLS_KEY"
|
||||||
|
|
||||||
|
if [[ ${tls_only,,} =~ ^(y|yes)$ ]]; then
|
||||||
|
{
|
||||||
|
echo ""
|
||||||
|
echo "# TLS configuration generated by Proxmox VE Valkey helper-script"
|
||||||
|
echo "port 0"
|
||||||
|
echo "tls-port 6379"
|
||||||
|
echo "tls-cert-file $TLS_DIR/valkey.crt"
|
||||||
|
echo "tls-key-file $TLS_DIR/valkey.key"
|
||||||
|
echo "tls-auth-clients no"
|
||||||
|
} >> /etc/valkey/valkey.conf
|
||||||
|
msg_ok "Enabled TLS-only mode on port 6379"
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo ""
|
||||||
|
echo "# TLS configuration generated by Proxmox VE Valkey helper-script"
|
||||||
|
echo "tls-port 6380"
|
||||||
|
echo "tls-cert-file $TLS_DIR/valkey.crt"
|
||||||
|
echo "tls-key-file $TLS_DIR/valkey.key"
|
||||||
|
echo "tls-auth-clients no"
|
||||||
|
} >> /etc/valkey/valkey.conf
|
||||||
|
msg_ok "Enabled TLS on port 6380 and TCP on 6379"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
systemctl enable -q --now valkey-server
|
systemctl enable -q --now valkey-server
|
||||||
systemctl restart valkey-server
|
systemctl restart valkey-server
|
||||||
msg_ok "Installed Valkey"
|
|
||||||
|
|
||||||
motd_ssh
|
motd_ssh
|
||||||
customize
|
customize
|
||||||
|
|||||||
@ -31,7 +31,7 @@ esac
|
|||||||
|
|
||||||
msg_info "Installing Zabbix $ZABBIX_VERSION"
|
msg_info "Installing Zabbix $ZABBIX_VERSION"
|
||||||
cd /tmp
|
cd /tmp
|
||||||
ZABBIX_DEB_URL="https://repo.zabbix.com/zabbix/${ZABBIX_VERSION}/release/debian/pool/main/z/zabbix-release/zabbix-release_latest+debian13_all.deb"
|
ZABBIX_DEB_URL="https://repo.zabbix.com/zabbix/${ZABBIX_VERSION}/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
|
curl -fsSL "$ZABBIX_DEB_URL" -o /tmp/zabbix-release_latest+debian13_all.deb
|
||||||
$STD dpkg -i /tmp/zabbix-release_latest+debian13_all.deb
|
$STD dpkg -i /tmp/zabbix-release_latest+debian13_all.deb
|
||||||
$STD apt update
|
$STD apt update
|
||||||
|
|||||||
196
misc/build.func
196
misc/build.func
@ -205,7 +205,10 @@ update_motd_ip() {
|
|||||||
# - Falls back to warning if no keys provided
|
# - Falls back to warning if no keys provided
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
install_ssh_keys_into_ct() {
|
install_ssh_keys_into_ct() {
|
||||||
[[ "$SSH" != "yes" ]] && return 0
|
[[ "${SSH:-no}" != "yes" ]] && return 0
|
||||||
|
|
||||||
|
# Ensure SSH_KEYS_FILE is defined (may not be set if advanced_settings was skipped)
|
||||||
|
: "${SSH_KEYS_FILE:=}"
|
||||||
|
|
||||||
if [[ -n "$SSH_KEYS_FILE" && -s "$SSH_KEYS_FILE" ]]; then
|
if [[ -n "$SSH_KEYS_FILE" && -s "$SSH_KEYS_FILE" ]]; then
|
||||||
msg_info "Installing selected SSH keys into CT ${CTID}"
|
msg_info "Installing selected SSH keys into CT ${CTID}"
|
||||||
@ -288,6 +291,90 @@ find_host_ssh_keys() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# SECTION 3B: IP RANGE SCANNING
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# ip_to_int() / int_to_ip()
|
||||||
|
#
|
||||||
|
# - Converts IP address to integer and vice versa for range iteration
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
ip_to_int() {
|
||||||
|
local IFS=.
|
||||||
|
read -r i1 i2 i3 i4 <<<"$1"
|
||||||
|
echo $(((i1 << 24) + (i2 << 16) + (i3 << 8) + i4))
|
||||||
|
}
|
||||||
|
|
||||||
|
int_to_ip() {
|
||||||
|
local ip=$1
|
||||||
|
echo "$(((ip >> 24) & 0xFF)).$(((ip >> 16) & 0xFF)).$(((ip >> 8) & 0xFF)).$((ip & 0xFF))"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# resolve_ip_from_range()
|
||||||
|
#
|
||||||
|
# - Takes an IP range in format "10.0.0.1/24-10.0.0.10/24"
|
||||||
|
# - Pings each IP in the range to find the first available one
|
||||||
|
# - Returns the first free IP with CIDR notation
|
||||||
|
# - Sets NET_RESOLVED to the resolved IP or empty on failure
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
resolve_ip_from_range() {
|
||||||
|
local range="$1"
|
||||||
|
local ip_cidr_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/([0-9]{1,2})$'
|
||||||
|
local ip_start ip_end
|
||||||
|
|
||||||
|
# Parse range: "10.0.0.1/24-10.0.0.10/24"
|
||||||
|
ip_start="${range%%-*}"
|
||||||
|
ip_end="${range##*-}"
|
||||||
|
|
||||||
|
if [[ ! "$ip_start" =~ $ip_cidr_regex ]] || [[ ! "$ip_end" =~ $ip_cidr_regex ]]; then
|
||||||
|
NET_RESOLVED=""
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local ip1="${ip_start%%/*}"
|
||||||
|
local ip2="${ip_end%%/*}"
|
||||||
|
local cidr="${ip_start##*/}"
|
||||||
|
|
||||||
|
local start_int=$(ip_to_int "$ip1")
|
||||||
|
local end_int=$(ip_to_int "$ip2")
|
||||||
|
|
||||||
|
for ((ip_int = start_int; ip_int <= end_int; ip_int++)); do
|
||||||
|
local ip=$(int_to_ip $ip_int)
|
||||||
|
msg_info "Checking IP: $ip"
|
||||||
|
if ! ping -c 1 -W 1 "$ip" >/dev/null 2>&1; then
|
||||||
|
NET_RESOLVED="$ip/$cidr"
|
||||||
|
msg_ok "Found free IP: ${BGN}$NET_RESOLVED${CL}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
NET_RESOLVED=""
|
||||||
|
msg_error "No free IP found in range $range"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# is_ip_range()
|
||||||
|
#
|
||||||
|
# - Checks if a string is an IP range (contains - and looks like IP/CIDR)
|
||||||
|
# - Returns 0 if it's a range, 1 otherwise
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
is_ip_range() {
|
||||||
|
local value="$1"
|
||||||
|
local ip_start ip_end
|
||||||
|
if [[ "$value" == *-* ]] && [[ "$value" != "dhcp" ]]; then
|
||||||
|
local ip_cidr_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/([0-9]{1,2})$'
|
||||||
|
ip_start="${value%%-*}"
|
||||||
|
ip_end="${value##*-}"
|
||||||
|
if [[ "$ip_start" =~ $ip_cidr_regex ]] && [[ "$ip_end" =~ $ip_cidr_regex ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# SECTION 4: STORAGE & RESOURCE MANAGEMENT
|
# SECTION 4: STORAGE & RESOURCE MANAGEMENT
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
@ -400,6 +487,18 @@ base_settings() {
|
|||||||
HN=${var_hostname:-$NSAPP}
|
HN=${var_hostname:-$NSAPP}
|
||||||
BRG=${var_brg:-"vmbr0"}
|
BRG=${var_brg:-"vmbr0"}
|
||||||
NET=${var_net:-"dhcp"}
|
NET=${var_net:-"dhcp"}
|
||||||
|
|
||||||
|
# Resolve IP range if NET contains a range (e.g., 192.168.1.100/24-192.168.1.200/24)
|
||||||
|
if is_ip_range "$NET"; then
|
||||||
|
msg_info "Scanning IP range: $NET"
|
||||||
|
if resolve_ip_from_range "$NET"; then
|
||||||
|
NET="$NET_RESOLVED"
|
||||||
|
else
|
||||||
|
msg_error "Could not find free IP in range. Falling back to DHCP."
|
||||||
|
NET="dhcp"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
IPV6_METHOD=${var_ipv6_method:-"none"}
|
IPV6_METHOD=${var_ipv6_method:-"none"}
|
||||||
IPV6_STATIC=${var_ipv6_static:-""}
|
IPV6_STATIC=${var_ipv6_static:-""}
|
||||||
GATE=${var_gateway:-""}
|
GATE=${var_gateway:-""}
|
||||||
@ -430,6 +529,15 @@ base_settings() {
|
|||||||
ENABLE_FUSE=${var_fuse:-"${1:-no}"}
|
ENABLE_FUSE=${var_fuse:-"${1:-no}"}
|
||||||
ENABLE_TUN=${var_tun:-"${1:-no}"}
|
ENABLE_TUN=${var_tun:-"${1:-no}"}
|
||||||
|
|
||||||
|
# Additional settings that may be skipped if advanced_settings is not run (e.g., App Defaults)
|
||||||
|
ENABLE_GPU=${var_gpu:-"no"}
|
||||||
|
ENABLE_NESTING=${var_nesting:-"1"}
|
||||||
|
ENABLE_KEYCTL=${var_keyctl:-"0"}
|
||||||
|
ENABLE_MKNOD=${var_mknod:-"0"}
|
||||||
|
PROTECT_CT=${var_protection:-"no"}
|
||||||
|
CT_TIMEZONE=${var_timezone:-"$timezone"}
|
||||||
|
[[ "${CT_TIMEZONE:-}" == Etc/* ]] && CT_TIMEZONE="host" # pct doesn't accept Etc/* zones
|
||||||
|
|
||||||
# Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts
|
# Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts
|
||||||
if [ -z "$var_os" ]; then
|
if [ -z "$var_os" ]; then
|
||||||
var_os="debian"
|
var_os="debian"
|
||||||
@ -445,9 +553,11 @@ base_settings() {
|
|||||||
# - Safe parser for KEY=VALUE lines from vars files
|
# - Safe parser for KEY=VALUE lines from vars files
|
||||||
# - Used by default_var_settings and app defaults loading
|
# - Used by default_var_settings and app defaults loading
|
||||||
# - Only loads whitelisted var_* keys
|
# - Only loads whitelisted var_* keys
|
||||||
|
# - Optional force parameter to override existing values (for app defaults)
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
load_vars_file() {
|
load_vars_file() {
|
||||||
local file="$1"
|
local file="$1"
|
||||||
|
local force="${2:-no}" # If "yes", override existing variables
|
||||||
[ -f "$file" ] || return 0
|
[ -f "$file" ] || return 0
|
||||||
msg_info "Loading defaults from ${file}"
|
msg_info "Loading defaults from ${file}"
|
||||||
|
|
||||||
@ -478,6 +588,12 @@ load_vars_file() {
|
|||||||
[[ "$var_key" != var_* ]] && continue
|
[[ "$var_key" != var_* ]] && continue
|
||||||
_is_whitelisted "$var_key" || continue
|
_is_whitelisted "$var_key" || continue
|
||||||
|
|
||||||
|
# Strip inline comments (anything after unquoted #)
|
||||||
|
# Only strip if not inside quotes
|
||||||
|
if [[ ! "$var_val" =~ ^[\"\'] ]]; then
|
||||||
|
var_val="${var_val%%#*}"
|
||||||
|
fi
|
||||||
|
|
||||||
# Strip quotes
|
# Strip quotes
|
||||||
if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then
|
if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then
|
||||||
var_val="${BASH_REMATCH[1]}"
|
var_val="${BASH_REMATCH[1]}"
|
||||||
@ -485,9 +601,16 @@ load_vars_file() {
|
|||||||
var_val="${BASH_REMATCH[1]}"
|
var_val="${BASH_REMATCH[1]}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Set only if not already exported
|
# Trim trailing whitespace
|
||||||
|
var_val="${var_val%"${var_val##*[![:space:]]}"}"
|
||||||
|
|
||||||
|
# Set variable: force mode overrides existing, otherwise only set if empty
|
||||||
|
if [[ "$force" == "yes" ]]; then
|
||||||
|
export "${var_key}=${var_val}"
|
||||||
|
else
|
||||||
[[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}"
|
[[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}"
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
done <"$file"
|
done <"$file"
|
||||||
msg_ok "Loaded ${file}"
|
msg_ok "Loaded ${file}"
|
||||||
}
|
}
|
||||||
@ -1055,7 +1178,10 @@ advanced_settings() {
|
|||||||
elif [ -f /etc/timezone ]; then
|
elif [ -f /etc/timezone ]; then
|
||||||
_host_timezone=$(cat /etc/timezone 2>/dev/null || echo "")
|
_host_timezone=$(cat /etc/timezone 2>/dev/null || echo "")
|
||||||
fi
|
fi
|
||||||
|
# Map Etc/* timezones to "host" (pct doesn't accept Etc/* zones)
|
||||||
|
[[ "${_host_timezone:-}" == Etc/* ]] && _host_timezone="host"
|
||||||
local _ct_timezone="${var_timezone:-$_host_timezone}"
|
local _ct_timezone="${var_timezone:-$_host_timezone}"
|
||||||
|
[[ "${_ct_timezone:-}" == Etc/* ]] && _ct_timezone="host"
|
||||||
|
|
||||||
# Helper to show current progress
|
# Helper to show current progress
|
||||||
show_progress() {
|
show_progress() {
|
||||||
@ -1303,9 +1429,10 @@ advanced_settings() {
|
|||||||
if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
|
if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
|
||||||
--title "IPv4 CONFIGURATION" \
|
--title "IPv4 CONFIGURATION" \
|
||||||
--ok-button "Next" --cancel-button "Back" \
|
--ok-button "Next" --cancel-button "Back" \
|
||||||
--menu "\nSelect IPv4 Address Assignment:" 14 60 2 \
|
--menu "\nSelect IPv4 Address Assignment:" 16 65 3 \
|
||||||
"dhcp" "Automatic (DHCP, recommended)" \
|
"dhcp" "Automatic (DHCP, recommended)" \
|
||||||
"static" "Static (manual entry)" \
|
"static" "Static (manual entry)" \
|
||||||
|
"range" "IP Range Scan (find first free IP)" \
|
||||||
3>&1 1>&2 2>&3); then
|
3>&1 1>&2 2>&3); then
|
||||||
|
|
||||||
if [[ "$result" == "static" ]]; then
|
if [[ "$result" == "static" ]]; then
|
||||||
@ -1336,6 +1463,42 @@ advanced_settings() {
|
|||||||
whiptail --msgbox "Invalid IPv4 CIDR format.\nExample: 192.168.1.100/24" 8 58
|
whiptail --msgbox "Invalid IPv4 CIDR format.\nExample: 192.168.1.100/24" 8 58
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
elif [[ "$result" == "range" ]]; then
|
||||||
|
# IP Range Scan
|
||||||
|
local ip_range
|
||||||
|
if ip_range=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
|
||||||
|
--title "IP RANGE SCAN" \
|
||||||
|
--ok-button "Scan" --cancel-button "Back" \
|
||||||
|
--inputbox "\nEnter IP range to scan for free address\n(e.g. 192.168.1.100/24-192.168.1.200/24)" 12 65 "" \
|
||||||
|
3>&1 1>&2 2>&3); then
|
||||||
|
if is_ip_range "$ip_range"; then
|
||||||
|
# Exit whiptail screen temporarily to show scan progress
|
||||||
|
clear
|
||||||
|
header_info
|
||||||
|
echo -e "${INFO}${BOLD}${DGN}Scanning IP range for free address...${CL}\n"
|
||||||
|
if resolve_ip_from_range "$ip_range"; then
|
||||||
|
# Get gateway
|
||||||
|
local gateway_ip
|
||||||
|
if gateway_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
|
||||||
|
--title "GATEWAY IP" \
|
||||||
|
--ok-button "Next" --cancel-button "Back" \
|
||||||
|
--inputbox "\nFound free IP: $NET_RESOLVED\n\nEnter Gateway IP address" 12 58 "" \
|
||||||
|
3>&1 1>&2 2>&3); then
|
||||||
|
if [[ "$gateway_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
||||||
|
_net="$NET_RESOLVED"
|
||||||
|
_gate=",gw=$gateway_ip"
|
||||||
|
((STEP++))
|
||||||
|
else
|
||||||
|
whiptail --msgbox "Invalid Gateway IP format." 8 58
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
whiptail --msgbox "No free IP found in the specified range.\nAll IPs responded to ping." 10 58
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
whiptail --msgbox "Invalid IP range format.\n\nExample: 192.168.1.100/24-192.168.1.200/24" 10 58
|
||||||
|
fi
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
_net="dhcp"
|
_net="dhcp"
|
||||||
_gate=""
|
_gate=""
|
||||||
@ -1675,6 +1838,7 @@ advanced_settings() {
|
|||||||
--inputbox "\nSet container timezone.\n\nExamples: Europe/Berlin, America/New_York, Asia/Tokyo\n\nHost timezone: ${_host_timezone:-unknown}\n\nLeave empty to inherit from host." 16 62 "$_ct_timezone" \
|
--inputbox "\nSet container timezone.\n\nExamples: Europe/Berlin, America/New_York, Asia/Tokyo\n\nHost timezone: ${_host_timezone:-unknown}\n\nLeave empty to inherit from host." 16 62 "$_ct_timezone" \
|
||||||
3>&1 1>&2 2>&3); then
|
3>&1 1>&2 2>&3); then
|
||||||
_ct_timezone="$result"
|
_ct_timezone="$result"
|
||||||
|
[[ "${_ct_timezone:-}" == Etc/* ]] && _ct_timezone="host" # pct doesn't accept Etc/* zones
|
||||||
((STEP++))
|
((STEP++))
|
||||||
else
|
else
|
||||||
((STEP--))
|
((STEP--))
|
||||||
@ -1892,13 +2056,13 @@ Advanced:
|
|||||||
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
|
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
|
||||||
echo -e "${NETWORK}${BOLD}${DGN}IPv4: ${BGN}$NET${CL}"
|
echo -e "${NETWORK}${BOLD}${DGN}IPv4: ${BGN}$NET${CL}"
|
||||||
echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}$IPV6_METHOD${CL}"
|
echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}$IPV6_METHOD${CL}"
|
||||||
echo -e "${FUSE}${BOLD}${DGN}FUSE Support: ${BGN}$ENABLE_FUSE${CL}"
|
echo -e "${FUSE}${BOLD}${DGN}FUSE Support: ${BGN}${ENABLE_FUSE:-no}${CL}"
|
||||||
[[ "$ENABLE_TUN" == "yes" ]] && echo -e "${NETWORK}${BOLD}${DGN}TUN/TAP Support: ${BGN}$ENABLE_TUN${CL}"
|
[[ "${ENABLE_TUN:-no}" == "yes" ]] && echo -e "${NETWORK}${BOLD}${DGN}TUN/TAP Support: ${BGN}$ENABLE_TUN${CL}"
|
||||||
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Nesting: ${BGN}$([ "$ENABLE_NESTING" == "1" ] && echo "Enabled" || echo "Disabled")${CL}"
|
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Nesting: ${BGN}$([ "${ENABLE_NESTING:-1}" == "1" ] && echo "Enabled" || echo "Disabled")${CL}"
|
||||||
[[ "$ENABLE_KEYCTL" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Keyctl: ${BGN}Enabled${CL}"
|
[[ "${ENABLE_KEYCTL:-0}" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Keyctl: ${BGN}Enabled${CL}"
|
||||||
echo -e "${GPU}${BOLD}${DGN}GPU Passthrough: ${BGN}$ENABLE_GPU${CL}"
|
echo -e "${GPU}${BOLD}${DGN}GPU Passthrough: ${BGN}${ENABLE_GPU:-no}${CL}"
|
||||||
[[ "$PROTECT_CT" == "yes" || "$PROTECT_CT" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Protection: ${BGN}Enabled${CL}"
|
[[ "${PROTECT_CT:-no}" == "yes" || "${PROTECT_CT:-no}" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Protection: ${BGN}Enabled${CL}"
|
||||||
[[ -n "$CT_TIMEZONE" ]] && echo -e "${INFO}${BOLD}${DGN}Timezone: ${BGN}$CT_TIMEZONE${CL}"
|
[[ -n "${CT_TIMEZONE:-}" ]] && echo -e "${INFO}${BOLD}${DGN}Timezone: ${BGN}$CT_TIMEZONE${CL}"
|
||||||
[[ "$APT_CACHER" == "yes" ]] && echo -e "${INFO}${BOLD}${DGN}APT Cacher: ${BGN}$APT_CACHER_IP${CL}"
|
[[ "$APT_CACHER" == "yes" ]] && echo -e "${INFO}${BOLD}${DGN}APT Cacher: ${BGN}$APT_CACHER_IP${CL}"
|
||||||
echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}"
|
echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}"
|
||||||
echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}"
|
echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}"
|
||||||
@ -2064,6 +2228,7 @@ install_script() {
|
|||||||
else
|
else
|
||||||
timezone="UTC"
|
timezone="UTC"
|
||||||
fi
|
fi
|
||||||
|
[[ "${timezone:-}" == Etc/* ]] && timezone="host" # pct doesn't accept Etc/* zones
|
||||||
|
|
||||||
# Show APP Header
|
# Show APP Header
|
||||||
header_info
|
header_info
|
||||||
@ -2148,8 +2313,8 @@ install_script() {
|
|||||||
header_info
|
header_info
|
||||||
echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}"
|
echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}"
|
||||||
METHOD="appdefaults"
|
METHOD="appdefaults"
|
||||||
|
load_vars_file "$(get_app_defaults_path)" "yes" # Force override script defaults
|
||||||
base_settings
|
base_settings
|
||||||
load_vars_file "$(get_app_defaults_path)"
|
|
||||||
echo_default
|
echo_default
|
||||||
defaults_target="$(get_app_defaults_path)"
|
defaults_target="$(get_app_defaults_path)"
|
||||||
break
|
break
|
||||||
@ -2665,10 +2830,12 @@ $PCT_OPTIONS_STRING"
|
|||||||
-protection 1"
|
-protection 1"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Timezone flag (if var_timezone was set)
|
# Timezone (map Etc/* to "host" as pct doesn't accept them)
|
||||||
if [ -n "${CT_TIMEZONE:-}" ]; then
|
if [ -n "${CT_TIMEZONE:-}" ]; then
|
||||||
|
local _pct_timezone="$CT_TIMEZONE"
|
||||||
|
[[ "$_pct_timezone" == Etc/* ]] && _pct_timezone="host"
|
||||||
PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING
|
PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING
|
||||||
-timezone $CT_TIMEZONE"
|
-timezone $_pct_timezone"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Password (already formatted)
|
# Password (already formatted)
|
||||||
@ -3019,8 +3186,9 @@ EOF'
|
|||||||
export LANG=\$locale_line"
|
export LANG=\$locale_line"
|
||||||
|
|
||||||
if [[ -z "${tz:-}" ]]; then
|
if [[ -z "${tz:-}" ]]; then
|
||||||
tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC")
|
tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "UTC")
|
||||||
fi
|
fi
|
||||||
|
[[ "${tz:-}" == Etc/* ]] && tz="UTC" # Normalize Etc/* to UTC for container setup
|
||||||
|
|
||||||
if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then
|
if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then
|
||||||
# Set timezone using symlink (Debian 13+ compatible)
|
# Set timezone using symlink (Debian 13+ compatible)
|
||||||
|
|||||||
@ -828,7 +828,7 @@ cleanup_lxc() {
|
|||||||
# Ruby gem
|
# Ruby gem
|
||||||
if command -v gem &>/dev/null; then $STD gem cleanup || true; fi
|
if command -v gem &>/dev/null; then $STD gem cleanup || true; fi
|
||||||
# Composer (PHP)
|
# Composer (PHP)
|
||||||
if command -v composer &>/dev/null; then $STD composer clear-cache || true; fi
|
if command -v composer &>/dev/null; then COMPOSER_ALLOW_SUPERUSER=1 && $STD composer clear-cache || true; fi
|
||||||
|
|
||||||
if command -v journalctl &>/dev/null; then
|
if command -v journalctl &>/dev/null; then
|
||||||
$STD journalctl --vacuum-time=10m || true
|
$STD journalctl --vacuum-time=10m || true
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 137 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 42 KiB |
@ -1222,6 +1222,7 @@ setup_deb822_repo() {
|
|||||||
local suite="$4"
|
local suite="$4"
|
||||||
local component="${5:-main}"
|
local component="${5:-main}"
|
||||||
local architectures="${6-}" # optional
|
local architectures="${6-}" # optional
|
||||||
|
local enabled="${7-}" # optional: "true" or "false"
|
||||||
|
|
||||||
# Validate required parameters
|
# Validate required parameters
|
||||||
if [[ -z "$name" || -z "$gpg_url" || -z "$repo_url" || -z "$suite" ]]; then
|
if [[ -z "$name" || -z "$gpg_url" || -z "$repo_url" || -z "$suite" ]]; then
|
||||||
@ -1249,9 +1250,13 @@ setup_deb822_repo() {
|
|||||||
echo "Types: deb"
|
echo "Types: deb"
|
||||||
echo "URIs: $repo_url"
|
echo "URIs: $repo_url"
|
||||||
echo "Suites: $suite"
|
echo "Suites: $suite"
|
||||||
|
# Flat repositories (suite="./" or absolute path) must not have Components
|
||||||
|
if [[ "$suite" != "./" && -n "$component" ]]; then
|
||||||
echo "Components: $component"
|
echo "Components: $component"
|
||||||
|
fi
|
||||||
[[ -n "$architectures" ]] && echo "Architectures: $architectures"
|
[[ -n "$architectures" ]] && echo "Architectures: $architectures"
|
||||||
echo "Signed-By: /etc/apt/keyrings/${name}.gpg"
|
echo "Signed-By: /etc/apt/keyrings/${name}.gpg"
|
||||||
|
[[ -n "$enabled" ]] && echo "Enabled: $enabled"
|
||||||
} >/etc/apt/sources.list.d/${name}.sources
|
} >/etc/apt/sources.list.d/${name}.sources
|
||||||
|
|
||||||
$STD apt update
|
$STD apt update
|
||||||
@ -4424,7 +4429,7 @@ function setup_rust() {
|
|||||||
# Get currently installed version
|
# Get currently installed version
|
||||||
local CURRENT_VERSION=""
|
local CURRENT_VERSION=""
|
||||||
if command -v rustc &>/dev/null; then
|
if command -v rustc &>/dev/null; then
|
||||||
CURRENT_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}')
|
CURRENT_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}' 2>/dev/null) || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Scenario 1: Rustup not installed - fresh install
|
# Scenario 1: Rustup not installed - fresh install
|
||||||
@ -4443,7 +4448,8 @@ function setup_rust() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}')
|
local RUST_VERSION
|
||||||
|
RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}' 2>/dev/null) || true
|
||||||
if [[ -z "$RUST_VERSION" ]]; then
|
if [[ -z "$RUST_VERSION" ]]; then
|
||||||
msg_error "Failed to determine Rust version"
|
msg_error "Failed to determine Rust version"
|
||||||
return 1
|
return 1
|
||||||
@ -4474,7 +4480,8 @@ function setup_rust() {
|
|||||||
# Ensure PATH is updated for current shell session
|
# Ensure PATH is updated for current shell session
|
||||||
export PATH="$CARGO_BIN:$PATH"
|
export PATH="$CARGO_BIN:$PATH"
|
||||||
|
|
||||||
local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}')
|
local RUST_VERSION
|
||||||
|
RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}' 2>/dev/null) || true
|
||||||
if [[ -z "$RUST_VERSION" ]]; then
|
if [[ -z "$RUST_VERSION" ]]; then
|
||||||
msg_error "Failed to determine Rust version after update"
|
msg_error "Failed to determine Rust version after update"
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
342
tools/addon/adguardhome-sync.sh
Normal file
342
tools/addon/adguardhome-sync.sh
Normal file
@ -0,0 +1,342 @@
|
|||||||
|
#!/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://github.com/bakito/adguardhome-sync
|
||||||
|
|
||||||
|
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func)
|
||||||
|
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func)
|
||||||
|
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func)
|
||||||
|
|
||||||
|
# Enable error handling
|
||||||
|
set -Eeuo pipefail
|
||||||
|
trap 'error_handler' ERR
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# CONFIGURATION
|
||||||
|
# ==============================================================================
|
||||||
|
APP="AdGuardHome-Sync"
|
||||||
|
APP_TYPE="addon"
|
||||||
|
INSTALL_PATH="/opt/adguardhome-sync"
|
||||||
|
CONFIG_PATH="/opt/adguardhome-sync/adguardhome-sync.yaml"
|
||||||
|
DEFAULT_PORT=8080
|
||||||
|
|
||||||
|
# Initialize all core functions (colors, formatting, icons, STD mode)
|
||||||
|
load_functions
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# HEADER
|
||||||
|
# ==============================================================================
|
||||||
|
function header_info {
|
||||||
|
clear
|
||||||
|
cat <<"EOF"
|
||||||
|
___ __ ____ __ _____
|
||||||
|
/ | ____/ /___ ___ ______ __________/ / / / /___ ____ ___ ___ / ___/__ ______ _____
|
||||||
|
/ /| |/ __ / __ `/ / / / __ `/ ___/ __ / /_/ / __ \/ __ `__ \/ _ \ \__ \/ / / / __ \/ ___/
|
||||||
|
/ ___ / /_/ / /_/ / /_/ / /_/ / / / /_/ / __ / /_/ / / / / / / __/ ___/ / /_/ / / / / /__
|
||||||
|
/_/ |_\__,_/\__, /\__,_/\__,_/_/ \__,_/_/ /_/\____/_/ /_/ /_/\___/ /____/\__, /_/ /_/\___/
|
||||||
|
/____/ /____/
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# HELPER FUNCTIONS
|
||||||
|
# ==============================================================================
|
||||||
|
get_ip() {
|
||||||
|
hostname -I 2>/dev/null | awk '{print $1}' || echo "127.0.0.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# OS DETECTION
|
||||||
|
# ==============================================================================
|
||||||
|
if [[ -f "/etc/alpine-release" ]]; then
|
||||||
|
OS="Alpine"
|
||||||
|
SERVICE_PATH="/etc/init.d/adguardhome-sync"
|
||||||
|
elif [[ -f "/etc/debian_version" ]]; then
|
||||||
|
OS="Debian"
|
||||||
|
SERVICE_PATH="/etc/systemd/system/adguardhome-sync.service"
|
||||||
|
else
|
||||||
|
msg_error "Unsupported OS detected. Exiting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# UNINSTALL
|
||||||
|
# ==============================================================================
|
||||||
|
function uninstall() {
|
||||||
|
msg_info "Uninstalling ${APP}"
|
||||||
|
if [[ "$OS" == "Alpine" ]]; then
|
||||||
|
rc-service adguardhome-sync stop &>/dev/null || true
|
||||||
|
rc-update del adguardhome-sync &>/dev/null || true
|
||||||
|
rm -f "$SERVICE_PATH"
|
||||||
|
else
|
||||||
|
systemctl disable --now adguardhome-sync.service &>/dev/null || true
|
||||||
|
rm -f "$SERVICE_PATH"
|
||||||
|
fi
|
||||||
|
rm -rf "$INSTALL_PATH"
|
||||||
|
rm -f "/usr/local/bin/update_adguardhome-sync"
|
||||||
|
rm -f "$HOME/.adguardhome-sync"
|
||||||
|
msg_ok "${APP} has been uninstalled"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# UPDATE
|
||||||
|
# ==============================================================================
|
||||||
|
function update() {
|
||||||
|
if check_for_gh_release "adguardhome-sync" "bakito/adguardhome-sync"; then
|
||||||
|
msg_info "Stopping service"
|
||||||
|
if [[ "$OS" == "Alpine" ]]; then
|
||||||
|
rc-service adguardhome-sync stop &>/dev/null || true
|
||||||
|
else
|
||||||
|
systemctl stop adguardhome-sync.service &>/dev/null || true
|
||||||
|
fi
|
||||||
|
msg_ok "Stopped service"
|
||||||
|
|
||||||
|
msg_info "Backing up configuration"
|
||||||
|
cp "$CONFIG_PATH" /tmp/adguardhome-sync.yaml.bak 2>/dev/null || true
|
||||||
|
msg_ok "Backed up configuration"
|
||||||
|
|
||||||
|
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "adguardhome-sync" "bakito/adguardhome-sync" "prebuild" "latest" "$INSTALL_PATH" "adguardhome-sync_*_linux_amd64.tar.gz"
|
||||||
|
|
||||||
|
msg_info "Restoring configuration"
|
||||||
|
cp /tmp/adguardhome-sync.yaml.bak "$CONFIG_PATH" 2>/dev/null || true
|
||||||
|
rm -f /tmp/adguardhome-sync.yaml.bak
|
||||||
|
msg_ok "Restored configuration"
|
||||||
|
|
||||||
|
msg_info "Starting service"
|
||||||
|
if [[ "$OS" == "Alpine" ]]; then
|
||||||
|
rc-service adguardhome-sync start
|
||||||
|
else
|
||||||
|
systemctl start adguardhome-sync.service
|
||||||
|
fi
|
||||||
|
msg_ok "Started service"
|
||||||
|
msg_ok "Updated successfully"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# INSTALL
|
||||||
|
# ==============================================================================
|
||||||
|
function install() {
|
||||||
|
local ip
|
||||||
|
ip=$(get_ip)
|
||||||
|
|
||||||
|
fetch_and_deploy_gh_release "adguardhome-sync" "bakito/adguardhome-sync" "prebuild" "latest" "$INSTALL_PATH" "adguardhome-sync_*_linux_amd64.tar.gz"
|
||||||
|
|
||||||
|
# Gather configuration from user
|
||||||
|
echo ""
|
||||||
|
echo -e "${TAB}Enter details for your AdGuard Home instances."
|
||||||
|
echo -e "${TAB}The Origin is your primary instance, Replica will sync from it."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Origin instance
|
||||||
|
echo -e "${YW}── Origin (Primary) Instance ──${CL}"
|
||||||
|
local origin_url origin_user origin_pass
|
||||||
|
read -rp " Origin URL (e.g., http://192.168.1.1): " origin_url
|
||||||
|
origin_url="${origin_url:-http://192.168.1.1}"
|
||||||
|
# Add http:// if no protocol specified
|
||||||
|
[[ ! "$origin_url" =~ ^https?:// ]] && origin_url="http://${origin_url}"
|
||||||
|
read -rp " Origin Username [admin]: " origin_user
|
||||||
|
origin_user="${origin_user:-admin}"
|
||||||
|
read -rsp " Origin Password: " origin_pass
|
||||||
|
echo ""
|
||||||
|
origin_pass="${origin_pass:-changeme}"
|
||||||
|
|
||||||
|
# Replica instance
|
||||||
|
echo ""
|
||||||
|
echo -e "${YW}── Replica Instance ──${CL}"
|
||||||
|
local replica_url replica_user replica_pass
|
||||||
|
read -rp " Replica URL (e.g., http://192.168.1.2): " replica_url
|
||||||
|
replica_url="${replica_url:-http://192.168.1.2}"
|
||||||
|
# Add http:// if no protocol specified
|
||||||
|
[[ ! "$replica_url" =~ ^https?:// ]] && replica_url="http://${replica_url}"
|
||||||
|
read -rp " Replica Username [admin]: " replica_user
|
||||||
|
replica_user="${replica_user:-admin}"
|
||||||
|
read -rsp " Replica Password: " replica_pass
|
||||||
|
echo ""
|
||||||
|
replica_pass="${replica_pass:-changeme}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
msg_info "Creating configuration"
|
||||||
|
cat <<EOF >"$CONFIG_PATH"
|
||||||
|
# AdGuardHome-Sync Configuration
|
||||||
|
# Documentation: https://github.com/bakito/adguardhome-sync
|
||||||
|
|
||||||
|
# Cron expression for sync interval (e.g., every 2 hours: "0 */2 * * *")
|
||||||
|
cron: "0 */2 * * *"
|
||||||
|
|
||||||
|
# Run sync on startup
|
||||||
|
runOnStart: true
|
||||||
|
|
||||||
|
# Continue sync on errors
|
||||||
|
continueOnError: false
|
||||||
|
|
||||||
|
# Origin AdGuardHome instance (primary)
|
||||||
|
origin:
|
||||||
|
url: "${origin_url}"
|
||||||
|
username: "${origin_user}"
|
||||||
|
password: "${origin_pass}"
|
||||||
|
insecureSkipVerify: false
|
||||||
|
|
||||||
|
# Replica instances (one or more)
|
||||||
|
replicas:
|
||||||
|
- url: "${replica_url}"
|
||||||
|
username: "${replica_user}"
|
||||||
|
password: "${replica_pass}"
|
||||||
|
insecureSkipVerify: false
|
||||||
|
# Add more replicas as needed:
|
||||||
|
# - url: "http://192.168.1.3"
|
||||||
|
# username: "admin"
|
||||||
|
# password: "changeme"
|
||||||
|
|
||||||
|
# API settings (web UI)
|
||||||
|
api:
|
||||||
|
port: ${DEFAULT_PORT}
|
||||||
|
darkMode: true
|
||||||
|
metrics:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Sync features (all enabled by default)
|
||||||
|
features:
|
||||||
|
dns:
|
||||||
|
accessLists: true
|
||||||
|
serverConfig: true
|
||||||
|
rewrites: true
|
||||||
|
dhcp:
|
||||||
|
serverConfig: true
|
||||||
|
staticLeases: true
|
||||||
|
generalSettings: true
|
||||||
|
queryLogConfig: true
|
||||||
|
statsConfig: true
|
||||||
|
clientSettings: true
|
||||||
|
services: true
|
||||||
|
filters: true
|
||||||
|
theme: true
|
||||||
|
EOF
|
||||||
|
chmod 600 "$CONFIG_PATH"
|
||||||
|
msg_ok "Created configuration"
|
||||||
|
|
||||||
|
msg_info "Creating service"
|
||||||
|
if [[ "$OS" == "Alpine" ]]; then
|
||||||
|
cat <<EOF >"$SERVICE_PATH"
|
||||||
|
#!/sbin/openrc-run
|
||||||
|
|
||||||
|
name="adguardhome-sync"
|
||||||
|
description="AdGuardHome Sync"
|
||||||
|
command="${INSTALL_PATH}/adguardhome-sync"
|
||||||
|
command_args="run --config ${CONFIG_PATH}"
|
||||||
|
command_background=true
|
||||||
|
pidfile="/run/\${RC_SVCNAME}.pid"
|
||||||
|
output_log="/var/log/adguardhome-sync.log"
|
||||||
|
error_log="/var/log/adguardhome-sync.log"
|
||||||
|
|
||||||
|
depend() {
|
||||||
|
need net
|
||||||
|
after firewall
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
chmod +x "$SERVICE_PATH"
|
||||||
|
rc-update add adguardhome-sync default
|
||||||
|
rc-service adguardhome-sync start
|
||||||
|
else
|
||||||
|
cat <<EOF >"$SERVICE_PATH"
|
||||||
|
[Unit]
|
||||||
|
Description=AdGuardHome Sync
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=${INSTALL_PATH}/adguardhome-sync run --config ${CONFIG_PATH}
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable --now adguardhome-sync &>/dev/null
|
||||||
|
fi
|
||||||
|
msg_ok "Created and started service"
|
||||||
|
|
||||||
|
# Create update script
|
||||||
|
msg_info "Creating update script"
|
||||||
|
cat <<'UPDATEEOF' >/usr/local/bin/update_adguardhome-sync
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# AdGuardHome-Sync Update Script
|
||||||
|
type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/adguardhome-sync.sh)"
|
||||||
|
UPDATEEOF
|
||||||
|
chmod +x /usr/local/bin/update_adguardhome-sync
|
||||||
|
msg_ok "Created update script (/usr/local/bin/update_adguardhome-sync)"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
msg_ok "${APP} installed successfully"
|
||||||
|
msg_ok "Web UI: ${BL}http://${ip}:${DEFAULT_PORT}${CL}"
|
||||||
|
msg_ok "Config: ${BL}${CONFIG_PATH}${CL}"
|
||||||
|
echo ""
|
||||||
|
msg_warn "Edit the config file to add your AdGuardHome instances!"
|
||||||
|
msg_warn " Origin: Your primary AdGuardHome instance"
|
||||||
|
msg_warn " Replicas: One or more replica instances to sync to"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# MAIN
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# Handle type=update (called from update script)
|
||||||
|
if [[ "${type:-}" == "update" ]]; then
|
||||||
|
header_info
|
||||||
|
if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/adguardhome-sync" ]]; then
|
||||||
|
update
|
||||||
|
else
|
||||||
|
msg_error "${APP} is not installed. Nothing to update."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
header_info
|
||||||
|
|
||||||
|
IP=$(get_ip)
|
||||||
|
|
||||||
|
# Check if already installed
|
||||||
|
if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/adguardhome-sync" ]]; then
|
||||||
|
msg_warn "${APP} is already installed."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -n "${TAB}Uninstall ${APP}? (y/N): "
|
||||||
|
read -r uninstall_prompt
|
||||||
|
if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then
|
||||||
|
uninstall
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -n "${TAB}Update ${APP}? (y/N): "
|
||||||
|
read -r update_prompt
|
||||||
|
if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then
|
||||||
|
update
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
msg_warn "No action selected. Exiting."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fresh installation
|
||||||
|
msg_warn "${APP} is not installed."
|
||||||
|
echo ""
|
||||||
|
echo -e "${TAB}${INFO} This will install:"
|
||||||
|
echo -e "${TAB} - AdGuardHome-Sync (Go binary)"
|
||||||
|
echo -e "${TAB} - Systemd/OpenRC service"
|
||||||
|
echo -e "${TAB} - Web UI on port ${DEFAULT_PORT}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -n "${TAB}Install ${APP}? (y/N): "
|
||||||
|
read -r install_prompt
|
||||||
|
if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then
|
||||||
|
install
|
||||||
|
else
|
||||||
|
msg_warn "Installation cancelled. Exiting."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
6
tools/headers/adguardhome-sync
Normal file
6
tools/headers/adguardhome-sync
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
___ ________ ____ __ _____
|
||||||
|
/ | ____/ / ____/_ ______ __________/ / / / /___ ____ ___ ___ / ___/__ ______ _____
|
||||||
|
/ /| |/ __ / / __/ / / / __ `/ ___/ __ / /_/ / __ \/ __ `__ \/ _ \______\__ \/ / / / __ \/ ___/
|
||||||
|
/ ___ / /_/ / /_/ / /_/ / /_/ / / / /_/ / __ / /_/ / / / / / / __/_____/__/ / /_/ / / / / /__
|
||||||
|
/_/ |_\__,_/\____/\__,_/\__,_/_/ \__,_/_/ /_/\____/_/ /_/ /_/\___/ /____/\__, /_/ /_/\___/
|
||||||
|
/____/
|
||||||
@ -578,7 +578,25 @@ fi
|
|||||||
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
|
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
|
||||||
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
|
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
|
||||||
msg_info "Retrieving the URL for the OPNsense Qcow2 Disk Image"
|
msg_info "Retrieving the URL for the OPNsense Qcow2 Disk Image"
|
||||||
URL="https://download.freebsd.org/releases/VM-IMAGES/14.2-RELEASE/amd64/Latest/FreeBSD-14.2-RELEASE-amd64.qcow2.xz"
|
# Use latest stable FreeBSD amd64 qcow2 VM image (generic, not UFS/ZFS)
|
||||||
|
RELEASE_LIST="$(curl -s https://download.freebsd.org/releases/VM-IMAGES/ \
|
||||||
|
| grep -Eo '[0-9]+\.[0-9]+-RELEASE' \
|
||||||
|
| sort -Vr \
|
||||||
|
| uniq)"
|
||||||
|
URL=""
|
||||||
|
FREEBSD_VER=""
|
||||||
|
for ver in $RELEASE_LIST; do
|
||||||
|
candidate="https://download.freebsd.org/releases/VM-IMAGES/${ver}/amd64/Latest/FreeBSD-${ver}-amd64.qcow2.xz"
|
||||||
|
if curl -fsI "$candidate" >/dev/null 2>&1; then
|
||||||
|
FREEBSD_VER="$ver"
|
||||||
|
URL="$candidate"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ -z "$URL" ]; then
|
||||||
|
msg_error "Could not find generic FreeBSD amd64 qcow2 image (non-UFS/ZFS)."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
msg_ok "Download URL: ${CL}${BL}${URL}${CL}"
|
msg_ok "Download URL: ${CL}${BL}${URL}${CL}"
|
||||||
curl -f#SL -o "$(basename "$URL")" "$URL"
|
curl -f#SL -o "$(basename "$URL")" "$URL"
|
||||||
echo -en "\e[1A\e[0K"
|
echo -en "\e[1A\e[0K"
|
||||||
|
|||||||
Reference in New Issue
Block a user