Compare commits

...

79 Commits

Author SHA1 Message Date
MickLesk
1a53c15ce3 fix(filebrowser): make noauth setup use correct database 2026-04-01 21:35:28 +02:00
community-scripts-pr-app[bot]
997946d65b Update CHANGELOG.md (#13446)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-31 22:00:14 +00:00
community-scripts-pr-app[bot]
28e3362b6a Update CHANGELOG.md (#13444)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-31 21:57:27 +00:00
CanbiZ (MickLesk)
8275531161 fix(build): skip empty gateway value in network config (#13442)
When var_gateway is set to an empty string, the resulting gw= token
in the comma-separated network string causes pct create to fail with
a 'missing key in comma-separated list property' error.

Closes #13421
2026-03-31 23:57:25 +02:00
CanbiZ (MickLesk)
b6907269e8 fix(graylog): set vm.max_map_count on host for OpenSearch (#13441)
OpenSearch requires vm.max_map_count >= 262144 but the Linux default
is 65530. Since this is not a namespaced sysctl, it must be set on
the Proxmox host rather than inside the unprivileged LXC.

Closes #13420
2026-03-31 23:57:16 +02:00
CanbiZ (MickLesk)
c53ce61472 fix(koillection): ensure newline before appending to .env.local (#13440)
If .env.local does not end with a newline, the APP_RUNTIME entry
gets concatenated with the previous line, causing Symfony to fail
with an invalid trusted header error during composer install.

Closes #13438
2026-03-31 23:57:03 +02:00
community-scripts-pr-app[bot]
7511415b89 Update CHANGELOG.md (#13426)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-30 20:39:49 +00:00
community-scripts-pr-app[bot]
2b966298f1 Update .app files (#13415)
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
2026-03-30 09:30:41 +02:00
community-scripts-pr-app[bot]
f985c7e952 Update CHANGELOG.md (#13416)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-30 07:30:16 +00:00
community-scripts-pr-app[bot]
76e95ec1d8 Update CHANGELOG.md (#13414)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-30 07:29:58 +00:00
push-app-to-main[bot]
8f335651da Add bambuddy (ct) (#13411)
Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com>
2026-03-30 09:29:48 +02:00
community-scripts-pr-app[bot]
02257e2a67 Update .app files (#13413)
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
2026-03-30 09:29:37 +02:00
community-scripts-pr-app[bot]
263bc371ec Update CHANGELOG.md (#13412)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-30 07:27:16 +00:00
CanbiZ (MickLesk)
78b2c45863 Rename: BirdNET > BirdNET-Go (#13410) 2026-03-30 09:26:50 +02:00
community-scripts-pr-app[bot]
d5924f780c Update CHANGELOG.md (#13406)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-29 20:17:41 +00:00
community-scripts-pr-app[bot]
8a5d268bd7 Update CHANGELOG.md (#13405)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-29 20:17:30 +00:00
Joery
abb8772e27 fix(victoriametrics): use jq to filter releases (#13393) 2026-03-29 22:17:15 +02:00
community-scripts-pr-app[bot]
699fd14e29 Update CHANGELOG.md (#13404)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-29 20:17:08 +00:00
community-scripts-pr-app[bot]
44de5dc34c Update CHANGELOG.md (#13403)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-29 20:16:57 +00:00
CanbiZ (MickLesk)
713677f39f fix(ollama): add error handling for Intel GPG key imports (#13397) 2026-03-29 22:16:43 +02:00
community-scripts-pr-app[bot]
96772b3c28 Update CHANGELOG.md (#13402)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-29 20:16:36 +00:00
CanbiZ (MickLesk)
4ab821815f fix(immich): ignore Redis connection error on maintenance mode disable (#13398) 2026-03-29 22:16:16 +02:00
community-scripts-pr-app[bot]
b978e17e0b Update CHANGELOG.md (#13401)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-29 20:16:13 +00:00
CanbiZ (MickLesk)
c6bbba090d fix(nginxproxymanager): unmask openresty after migration from package (#13399) 2026-03-29 22:15:50 +02:00
community-scripts-pr-app[bot]
f6cb8caa08 Update .app files (#13392)
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
2026-03-29 14:50:40 +02:00
community-scripts-pr-app[bot]
fb9547c03b Update CHANGELOG.md (#13391)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-29 12:49:18 +00:00
push-app-to-main[bot]
9e129ecf85 Add yourls (ct) (#13379)
Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com>
2026-03-29 14:48:55 +02:00
community-scripts-pr-app[bot]
52ad08e331 Update CHANGELOG.md (#13385)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-29 00:07:57 +00:00
community-scripts-pr-app[bot]
2f51b6c13c Archive old changelog entries (#13384)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-29 00:07:33 +00:00
community-scripts-pr-app[bot]
a17e36bbcb Update CHANGELOG.md (#13382)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-28 22:27:33 +00:00
community-scripts-pr-app[bot]
4be8346df9 Update CHANGELOG.md (#13381)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-28 22:27:14 +00:00
Slaviša Arežina
5e50b79046 FileFlows: add option to install Node (#13368)
* add choices

* fixes
2026-03-28 23:27:07 +01:00
krazos
30351670b7 Fix: Update gokapi binary name for v2.2.4+ and add migration step (#13377)
* Rename gokapi binary and update service

Change the installed binary name from pre-v2.2.4 `gokapi-linux_amd64` to v2.2.4+ `gokapi` and update service configuration accordingly. Add a migration step to remove any legacy `gokapi-linux_amd64` binary file, update binary reference in existing `gokapi.service`, and reload systemd before starting the service.

* Update comment for binary name migration

---------

Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com>
2026-03-28 23:26:51 +01:00
community-scripts-pr-app[bot]
022f177d0c Update CHANGELOG.md (#13372)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-28 14:50:59 +00:00
krazos
46dc693c99 Fix: update gokapi asset matching for v2.2.4+ naming convention (#13369) 2026-03-28 15:50:32 +01:00
community-scripts-pr-app[bot]
744097ac0d Update CHANGELOG.md (#13370)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-28 14:18:33 +00:00
Slaviša Arežina
2f3d393726 fix (#13365) 2026-03-28 15:18:10 +01:00
community-scripts-pr-app[bot]
1085aa5cf6 Update .app files (#13358)
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
2026-03-27 22:54:56 +01:00
community-scripts-pr-app[bot]
f07f7993ab Update CHANGELOG.md (#13359)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-27 21:44:28 +00:00
push-app-to-main[bot]
45b3a62dd5 Add matter-server (ct) (#13355)
Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com>
2026-03-27 22:44:00 +01:00
CanbiZ (MickLesk)
2eb259bd99 rm 12 check 2026-03-27 16:32:20 +01:00
community-scripts-pr-app[bot]
21584a7a5a Update CHANGELOG.md (#13349)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-27 13:46:22 +00:00
CanbiZ (MickLesk)
0c1d6c688b feat: add custom Postgres port support for Dispatcharr upgrade (#13347) 2026-03-27 14:45:53 +01:00
CanbiZ (MickLesk)
9239ecf10d fix empty crontab 2026-03-27 14:09:48 +01:00
community-scripts-pr-app[bot]
dc9fb678af Update CHANGELOG.md (#13343)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-27 12:41:43 +00:00
CanbiZ (MickLesk)
18359679d9 Refactor/Feature-Bump/Security: Update-Cron-LXCs (Now Local Mode!) (#13339)
* Refactor: Update-Cron-LXCs (Now Local Mode!)

* Update script metadata and improve error handling
2026-03-27 13:41:15 +01:00
community-scripts-pr-app[bot]
6dd227859e Update .app files (#13341)
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
2026-03-27 13:34:43 +01:00
community-scripts-pr-app[bot]
3e5a5305f6 Update CHANGELOG.md (#13342)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-27 12:25:26 +00:00
push-app-to-main[bot]
e72400b417 Add geopulse (ct) (#13320)
Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com>
2026-03-27 13:24:59 +01:00
community-scripts-pr-app[bot]
30adc4fca3 Update CHANGELOG.md (#13340)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-27 10:26:00 +00:00
CanbiZ (MickLesk)
6017a124f9 chore(immich): bump to v2.6.3 (#13324) 2026-03-27 11:25:33 +01:00
community-scripts-pr-app[bot]
924f1accd7 Update CHANGELOG.md (#13338)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-27 10:14:38 +00:00
Slaviša Arežina
9a0ab814af Fix (#13336) 2026-03-27 11:14:06 +01:00
community-scripts-pr-app[bot]
1fb8e10c42 Update CHANGELOG.md (#13326)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-26 21:09:12 +00:00
Tom Frenzel
81516991a8 SparkyFitness-Garmin: fix app name (#13325) 2026-03-26 22:08:45 +01:00
community-scripts-pr-app[bot]
095783de3e Update .app files (#13322)
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
2026-03-26 19:55:49 +01:00
community-scripts-pr-app[bot]
11777a51cc Update CHANGELOG.md (#13321)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-26 18:36:03 +00:00
push-app-to-main[bot]
88e1813f96 BirdNET (#13313)
* Add birdnet (ct)

* Update author in birdnet.sh script

---------

Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com>
Co-authored-by: CanbiZ (MickLesk) <47820557+MickLesk@users.noreply.github.com>
2026-03-26 19:35:33 +01:00
community-scripts-pr-app[bot]
6c44215000 Update CHANGELOG.md (#13317)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-26 15:53:32 +00:00
CanbiZ (MickLesk)
d4e20816c7 core: APT/APK Mirror Fallback for CDN Failures (#13316) 2026-03-26 16:53:04 +01:00
community-scripts-pr-app[bot]
d12a0f1701 Update CHANGELOG.md (#13315)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-26 15:08:09 +00:00
CanbiZ (MickLesk)
fbe5b57c76 core/tools: replace generic return 1 exit_codes with more specific exit_codes (#13311) 2026-03-26 16:07:38 +01:00
community-scripts-pr-app[bot]
b14dfccc99 Update CHANGELOG.md (#13314)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-26 15:07:06 +00:00
CanbiZ (MickLesk)
38a283e549 update(frigate): bump to v0.17.1 and fix OpenVino model build (#13304) 2026-03-26 16:06:36 +01:00
community-scripts-pr-app[bot]
d06a70819d Update CHANGELOG.md (#13307)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-26 11:35:20 +00:00
community-scripts-pr-app[bot]
53e73e2f1a Update CHANGELOG.md (#13306)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-26 11:34:51 +00:00
Tom Frenzel
ee66508879 SparkyFitness: add garmin microservice (#12642)
* feat(sparkyfitness): add garmin microservice

* chore(sparkyfitness): add note to json

* chore(sparkyfitness): set garmin url

* feat(sparkyfitness): add garmin install option to update menu

* fix(sparkyfitness): typo

* chore(sparkyfitness): streamline naming and refactor menu

* feat: add sparlkyfitness garmin addon

* fix: remove EOF

* fix: update uninstall

* chore: update telemetry

* fix: app name

* chore: update env vars

* fix: typo

* chore: update default port

* fix: message format

* fix: update uninstall

* fix: rename functions

* fix: remove custom header_info

* fix: add header file

* chore: revert function naming
2026-03-26 12:34:28 +01:00
community-scripts-pr-app[bot]
83cfa0b5b4 Update CHANGELOG.md (#13305)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-26 11:24:51 +00:00
CanbiZ (MickLesk)
0a458c0a11 Immich: Bump to 2.6.2 | use start.sh in service, ensure DB_HOSTNAME in .env | Fix Rights Issue with ZFS Shares (#13199)
* fix(immich): use start.sh in service, ensure DB_HOSTNAME in .env

* Bump Immich to v2.6.2 and adjust chown handling

Update Immich release references from v2.6.1 to v2.6.2 in ct/immich.sh and install/immich-install.sh. Replace broad recursive chown -R on the install dir with a safer approach that avoids recursing into the upload directory (which may be a mounted volume with restricted permissions): set ownership on the install dir itself, chown each top-level entry except 'upload', and attempt to chown the upload path while ignoring errors. Also adjust ordering for /var/log/immich chown to avoid permission issues when enabling services.
2026-03-26 12:24:26 +01:00
community-scripts-pr-app[bot]
6703fca0e4 Update CHANGELOG.md (#13301)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-26 09:12:14 +00:00
CanbiZ (MickLesk)
42fbf1afc5 core: use /usr/bin/install to prevent function shadowing (#13299) 2026-03-26 10:11:47 +01:00
community-scripts-pr-app[bot]
d1c5b03fa7 Update CHANGELOG.md (#13298)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-26 09:04:24 +00:00
CanbiZ (MickLesk)
b9a39db667 fix(tools.func): pin npm to 11.11.0 to work around Node.js 22.22.2 regression (#13296)
Node.js 22.22.2 ships with a broken npm self-upgrade path where 'npm install -g npm@latest' fails with MODULE_NOT_FOUND for promise-retry. Pin to npm@11.11.0 as a known-good version until the upstream issue is resolved. Ref: nodejs/node#62425, npm/cli#9151
2026-03-26 10:04:00 +01:00
community-scripts-pr-app[bot]
0872f086ef Update CHANGELOG.md (#13297)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-26 08:54:51 +00:00
CanbiZ (MickLesk)
4eecca8aea fix(tools.func): use absolute path for install in setup_uv
Using bare 'install' command gets shadowed when scripts define their own install() function, causing setup_uv to hang. Use /usr/bin/install instead.
2026-03-26 09:54:17 +01:00
CanbiZ (MickLesk)
d915dee103 Update website link from .com to .org 2026-03-25 17:52:46 +01:00
CanbiZ (MickLesk)
97bf744e96 fix typo (org instead of com) 2026-03-25 17:48:03 +01:00
community-scripts-pr-app[bot]
7425f5d8fe Update CHANGELOG.md (#13284)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-25 12:06:51 +00:00
CanbiZ (MickLesk)
53bc492fdb Make shell command substitutions safe with || true (#13279)
Add defensive fallbacks (|| true) to multiple command substitutions to prevent non-zero exits when commands produce no output or are unavailable. Changes touch misc/api.func, misc/build.func and misc/tools.func and cover places like lspci, /proc/cpuinfo parsing, /etc/os-release reads, hostname -I usage, grep reads from vars files and maps, pct config parsing, storage/template lookups, tool version detection, NVIDIA driver version extraction, and MeiliSearch config parsing. These edits do not change functional behavior aside from ensuring the scripts continue running (variables will be empty) instead of failing in stricter shells or when commands return non-zero status.
2026-03-25 13:06:21 +01:00
81 changed files with 2606 additions and 821 deletions

145
.github/changelogs/2026/03.md generated vendored
View File

@@ -1,3 +1,148 @@
## 2026-03-28
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Fix: Update gokapi binary name for v2.2.4+ and add migration step [@krazos](https://github.com/krazos) ([#13377](https://github.com/community-scripts/ProxmoxVE/pull/13377))
- Fix: update gokapi asset matching for v2.2.4+ naming convention [@krazos](https://github.com/krazos) ([#13369](https://github.com/community-scripts/ProxmoxVE/pull/13369))
- Tandoor Recipes: Add missing env variable [@tremor021](https://github.com/tremor021) ([#13365](https://github.com/community-scripts/ProxmoxVE/pull/13365))
- #### ✨ New Features
- FileFlows: add option to install Node [@tremor021](https://github.com/tremor021) ([#13368](https://github.com/community-scripts/ProxmoxVE/pull/13368))
## 2026-03-27
### 🆕 New Scripts
- Matter-Server ([#13355](https://github.com/community-scripts/ProxmoxVE/pull/13355))
- GeoPulse ([#13320](https://github.com/community-scripts/ProxmoxVE/pull/13320))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- RevealJS: Switch from gulp to vite [@tremor021](https://github.com/tremor021) ([#13336](https://github.com/community-scripts/ProxmoxVE/pull/13336))
- #### ✨ New Features
- Dispatcharr add custom Postgres port support for upgrade [@MickLesk](https://github.com/MickLesk) ([#13347](https://github.com/community-scripts/ProxmoxVE/pull/13347))
- Immich: bump to v2.6.3 [@MickLesk](https://github.com/MickLesk) ([#13324](https://github.com/community-scripts/ProxmoxVE/pull/13324))
### 🧰 Tools
- #### ✨ New Features
- Refactor/Feature-Bump/Security: Update-Cron-LXCs (Now Local Mode!) [@MickLesk](https://github.com/MickLesk) ([#13339](https://github.com/community-scripts/ProxmoxVE/pull/13339))
## 2026-03-26
### 🆕 New Scripts
- BirdNET ([#13313](https://github.com/community-scripts/ProxmoxVE/pull/13313))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Immich: Bump to 2.6.2 | use start.sh in service, ensure DB_HOSTNAME in .env | Fix Rights Issue with ZFS Shares [@MickLesk](https://github.com/MickLesk) ([#13199](https://github.com/community-scripts/ProxmoxVE/pull/13199))
- #### ✨ New Features
- SparkyFitness: add garmin microservice as addon [@tomfrenzel](https://github.com/tomfrenzel) ([#12642](https://github.com/community-scripts/ProxmoxVE/pull/12642))
- Frigate: bump to v0.17.1 & change build order [@MickLesk](https://github.com/MickLesk) ([#13304](https://github.com/community-scripts/ProxmoxVE/pull/13304))
### 💾 Core
- #### 🐞 Bug Fixes
- tools.func: pin npm to 11.11.0 to work around Node.js 22.22.2 regression [@MickLesk](https://github.com/MickLesk) ([#13296](https://github.com/community-scripts/ProxmoxVE/pull/13296))
- #### ✨ New Features
- core: APT/APK Mirror Fallback for CDN Failures [@MickLesk](https://github.com/MickLesk) ([#13316](https://github.com/community-scripts/ProxmoxVE/pull/13316))
- core/tools: replace generic return 1 exit_codes with more specific exit_codes [@MickLesk](https://github.com/MickLesk) ([#13311](https://github.com/community-scripts/ProxmoxVE/pull/13311))
- #### 🔧 Refactor
- core: use /usr/bin/install to prevent function shadowing [@MickLesk](https://github.com/MickLesk) ([#13299](https://github.com/community-scripts/ProxmoxVE/pull/13299))
### 🧰 Tools
- #### 🐞 Bug Fixes
- SparkyFitness-Garmin: fix app name [@tomfrenzel](https://github.com/tomfrenzel) ([#13325](https://github.com/community-scripts/ProxmoxVE/pull/13325))
## 2026-03-25
### 🚀 Updated Scripts
- #### ✨ New Features
- Komodo v2: migrate env vars to v2 and update source [@MickLesk](https://github.com/MickLesk) ([#13262](https://github.com/community-scripts/ProxmoxVE/pull/13262))
### 💾 Core
- #### 🔧 Refactor
- core: make shell command substitutions safe with || true [@MickLesk](https://github.com/MickLesk) ([#13279](https://github.com/community-scripts/ProxmoxVE/pull/13279))
## 2026-03-24
### 🆕 New Scripts
- Homebrew (Addon) ([#13249](https://github.com/community-scripts/ProxmoxVE/pull/13249))
- NextExplorer ([#13252](https://github.com/community-scripts/ProxmoxVE/pull/13252))
### 🚀 Updated Scripts
- #### ✨ New Features
- Turnkey: modernize turnkey.sh with shared libraries [@MickLesk](https://github.com/MickLesk) ([#13242](https://github.com/community-scripts/ProxmoxVE/pull/13242))
- #### 🔧 Refactor
- chore: replace helper-scripts.com with community-scripts.com [@MickLesk](https://github.com/MickLesk) ([#13244](https://github.com/community-scripts/ProxmoxVE/pull/13244))
### 🗑️ Deleted Scripts
- Remove: Booklore [@MickLesk](https://github.com/MickLesk) ([#13265](https://github.com/community-scripts/ProxmoxVE/pull/13265))
## 2026-03-23
### 🚀 Updated Scripts
- #### 🔧 Refactor
- core: harden shell scripts against injection and insecure permissions [@MickLesk](https://github.com/MickLesk) ([#13239](https://github.com/community-scripts/ProxmoxVE/pull/13239))
## 2026-03-22
### 🆕 New Scripts
- versitygw ([#13180](https://github.com/community-scripts/ProxmoxVE/pull/13180))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Adventurelog: pin DRF <3.15 to fix coreapi module removal [@MickLesk](https://github.com/MickLesk) ([#13194](https://github.com/community-scripts/ProxmoxVE/pull/13194))
- #### ✨ New Features
- ConvertX: add libreoffice-writer for ODT/document conversions [@MickLesk](https://github.com/MickLesk) ([#13196](https://github.com/community-scripts/ProxmoxVE/pull/13196))
- #### 🔧 Refactor
- iSponsorblockTV: add AVX CPU check before installation [@MickLesk](https://github.com/MickLesk) ([#13197](https://github.com/community-scripts/ProxmoxVE/pull/13197))
### 💾 Core
- #### 🐞 Bug Fixes
- core: guard against empty IPv6 address in static mode [@MickLesk](https://github.com/MickLesk) ([#13195](https://github.com/community-scripts/ProxmoxVE/pull/13195))
## 2026-03-21 ## 2026-03-21
### 🚀 Updated Scripts ### 🚀 Updated Scripts

View File

@@ -29,6 +29,9 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
@@ -42,7 +45,7 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
<details> <details>
<summary><h4>March (21 entries)</h4></summary> <summary><h4>March (28 entries)</h4></summary>
[View March 2026 Changelog](.github/changelogs/2026/03.md) [View March 2026 Changelog](.github/changelogs/2026/03.md)
@@ -426,6 +429,124 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details> </details>
## 2026-03-31
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Graylog: set vm.max_map_count on host for OpenSearch [@MickLesk](https://github.com/MickLesk) ([#13441](https://github.com/community-scripts/ProxmoxVE/pull/13441))
- Koillection: ensure newline before appending to .env.local [@MickLesk](https://github.com/MickLesk) ([#13440](https://github.com/community-scripts/ProxmoxVE/pull/13440))
### 💾 Core
- #### 🔧 Refactor
- core: skip empty gateway value in network config [@MickLesk](https://github.com/MickLesk) ([#13442](https://github.com/community-scripts/ProxmoxVE/pull/13442))
## 2026-03-30
### 🆕 New Scripts
- Bambuddy ([#13411](https://github.com/community-scripts/ProxmoxVE/pull/13411))
### 🚀 Updated Scripts
- #### 💥 Breaking Changes
- Rename: BirdNET > BirdNET-Go [@MickLesk](https://github.com/MickLesk) ([#13410](https://github.com/community-scripts/ProxmoxVE/pull/13410))
## 2026-03-29
### 🆕 New Scripts
- YOURLS ([#13379](https://github.com/community-scripts/ProxmoxVE/pull/13379))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- fix(victoriametrics): use jq to filter releases [@Joery-M](https://github.com/Joery-M) ([#13393](https://github.com/community-scripts/ProxmoxVE/pull/13393))
- Ollama: add error handling for Intel GPG key imports [@MickLesk](https://github.com/MickLesk) ([#13397](https://github.com/community-scripts/ProxmoxVE/pull/13397))
- Immich: ignore Redis connection error on maintenance mode disable [@MickLesk](https://github.com/MickLesk) ([#13398](https://github.com/community-scripts/ProxmoxVE/pull/13398))
- NPM: unmask openresty after migration from package [@MickLesk](https://github.com/MickLesk) ([#13399](https://github.com/community-scripts/ProxmoxVE/pull/13399))
## 2026-03-28
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Fix: Update gokapi binary name for v2.2.4+ and add migration step [@krazos](https://github.com/krazos) ([#13377](https://github.com/community-scripts/ProxmoxVE/pull/13377))
- Fix: update gokapi asset matching for v2.2.4+ naming convention [@krazos](https://github.com/krazos) ([#13369](https://github.com/community-scripts/ProxmoxVE/pull/13369))
- Tandoor Recipes: Add missing env variable [@tremor021](https://github.com/tremor021) ([#13365](https://github.com/community-scripts/ProxmoxVE/pull/13365))
- #### ✨ New Features
- FileFlows: add option to install Node [@tremor021](https://github.com/tremor021) ([#13368](https://github.com/community-scripts/ProxmoxVE/pull/13368))
## 2026-03-27
### 🆕 New Scripts
- Matter-Server ([#13355](https://github.com/community-scripts/ProxmoxVE/pull/13355))
- GeoPulse ([#13320](https://github.com/community-scripts/ProxmoxVE/pull/13320))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- RevealJS: Switch from gulp to vite [@tremor021](https://github.com/tremor021) ([#13336](https://github.com/community-scripts/ProxmoxVE/pull/13336))
- #### ✨ New Features
- Dispatcharr add custom Postgres port support for upgrade [@MickLesk](https://github.com/MickLesk) ([#13347](https://github.com/community-scripts/ProxmoxVE/pull/13347))
- Immich: bump to v2.6.3 [@MickLesk](https://github.com/MickLesk) ([#13324](https://github.com/community-scripts/ProxmoxVE/pull/13324))
### 🧰 Tools
- #### ✨ New Features
- Refactor/Feature-Bump/Security: Update-Cron-LXCs (Now Local Mode!) [@MickLesk](https://github.com/MickLesk) ([#13339](https://github.com/community-scripts/ProxmoxVE/pull/13339))
## 2026-03-26
### 🆕 New Scripts
- BirdNET ([#13313](https://github.com/community-scripts/ProxmoxVE/pull/13313))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Immich: Bump to 2.6.2 | use start.sh in service, ensure DB_HOSTNAME in .env | Fix Rights Issue with ZFS Shares [@MickLesk](https://github.com/MickLesk) ([#13199](https://github.com/community-scripts/ProxmoxVE/pull/13199))
- #### ✨ New Features
- SparkyFitness: add garmin microservice as addon [@tomfrenzel](https://github.com/tomfrenzel) ([#12642](https://github.com/community-scripts/ProxmoxVE/pull/12642))
- Frigate: bump to v0.17.1 & change build order [@MickLesk](https://github.com/MickLesk) ([#13304](https://github.com/community-scripts/ProxmoxVE/pull/13304))
### 💾 Core
- #### 🐞 Bug Fixes
- tools.func: pin npm to 11.11.0 to work around Node.js 22.22.2 regression [@MickLesk](https://github.com/MickLesk) ([#13296](https://github.com/community-scripts/ProxmoxVE/pull/13296))
- #### ✨ New Features
- core: APT/APK Mirror Fallback for CDN Failures [@MickLesk](https://github.com/MickLesk) ([#13316](https://github.com/community-scripts/ProxmoxVE/pull/13316))
- core/tools: replace generic return 1 exit_codes with more specific exit_codes [@MickLesk](https://github.com/MickLesk) ([#13311](https://github.com/community-scripts/ProxmoxVE/pull/13311))
- #### 🔧 Refactor
- core: use /usr/bin/install to prevent function shadowing [@MickLesk](https://github.com/MickLesk) ([#13299](https://github.com/community-scripts/ProxmoxVE/pull/13299))
### 🧰 Tools
- #### 🐞 Bug Fixes
- SparkyFitness-Garmin: fix app name [@tomfrenzel](https://github.com/tomfrenzel) ([#13325](https://github.com/community-scripts/ProxmoxVE/pull/13325))
## 2026-03-25 ## 2026-03-25
### 🚀 Updated Scripts ### 🚀 Updated Scripts
@@ -434,6 +555,12 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
- Komodo v2: migrate env vars to v2 and update source [@MickLesk](https://github.com/MickLesk) ([#13262](https://github.com/community-scripts/ProxmoxVE/pull/13262)) - Komodo v2: migrate env vars to v2 and update source [@MickLesk](https://github.com/MickLesk) ([#13262](https://github.com/community-scripts/ProxmoxVE/pull/13262))
### 💾 Core
- #### 🔧 Refactor
- core: make shell command substitutions safe with || true [@MickLesk](https://github.com/MickLesk) ([#13279](https://github.com/community-scripts/ProxmoxVE/pull/13279))
## 2026-03-24 ## 2026-03-24
### 🆕 New Scripts ### 🆕 New Scripts
@@ -1061,249 +1188,4 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
- #### ✨ New Features - #### ✨ New Features
- core: graceful fallback for apt-get update failures [@MickLesk](https://github.com/MickLesk) ([#12386](https://github.com/community-scripts/ProxmoxVE/pull/12386)) - core: graceful fallback for apt-get update failures [@MickLesk](https://github.com/MickLesk) ([#12386](https://github.com/community-scripts/ProxmoxVE/pull/12386))
- core: Improve error outputs across core functions [@MickLesk](https://github.com/MickLesk) ([#12378](https://github.com/community-scripts/ProxmoxVE/pull/12378)) - core: Improve error outputs across core functions [@MickLesk](https://github.com/MickLesk) ([#12378](https://github.com/community-scripts/ProxmoxVE/pull/12378))
## 2026-02-26
### 🆕 New Scripts
- Kima-Hub ([#12319](https://github.com/community-scripts/ProxmoxVE/pull/12319))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- tools.func: update glx alternatives / nvidia alternative if nvidia glx are missing [@MickLesk](https://github.com/MickLesk) ([#12372](https://github.com/community-scripts/ProxmoxVE/pull/12372))
- hotfix: overseer version [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12366](https://github.com/community-scripts/ProxmoxVE/pull/12366))
- #### ✨ New Features
- Add ffmpeg for booklore (ffprobe) [@MickLesk](https://github.com/MickLesk) ([#12371](https://github.com/community-scripts/ProxmoxVE/pull/12371))
- [QOL] Immich: add warning regarding library compilation time [@vhsdream](https://github.com/vhsdream) ([#12345](https://github.com/community-scripts/ProxmoxVE/pull/12345))
### 🧰 Tools
- #### 🐞 Bug Fixes
- Improves adguardhome-sync addon when running on alpine LXCs [@Darkangeel-hd](https://github.com/Darkangeel-hd) ([#12362](https://github.com/community-scripts/ProxmoxVE/pull/12362))
- #### ✨ New Features
- Add Alpine support and improve Tailscale install [@MickLesk](https://github.com/MickLesk) ([#12370](https://github.com/community-scripts/ProxmoxVE/pull/12370))
### 📚 Documentation
- fix wrong link on contributions README.md [@Darkangeel-hd](https://github.com/Darkangeel-hd) ([#12363](https://github.com/community-scripts/ProxmoxVE/pull/12363))
### 📂 Github
- github: add workflow to autom. close unauthorized new-script PRs [@MickLesk](https://github.com/MickLesk) ([#12356](https://github.com/community-scripts/ProxmoxVE/pull/12356))
## 2026-02-25
### 🆕 New Scripts
- Zerobyte ([#12321](https://github.com/community-scripts/ProxmoxVE/pull/12321))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- fix: overseer migration [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12340](https://github.com/community-scripts/ProxmoxVE/pull/12340))
- add: vikunja: daemon reload [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12323](https://github.com/community-scripts/ProxmoxVE/pull/12323))
- opnsense-VM: Use ip link to verify bridge existence [@MickLesk](https://github.com/MickLesk) ([#12329](https://github.com/community-scripts/ProxmoxVE/pull/12329))
- wger: Use $http_host for proxy Host header [@MickLesk](https://github.com/MickLesk) ([#12327](https://github.com/community-scripts/ProxmoxVE/pull/12327))
- Passbolt: Update Nginx config `client_max_body_size` [@tremor021](https://github.com/tremor021) ([#12313](https://github.com/community-scripts/ProxmoxVE/pull/12313))
- Zammad: configure Elasticsearch before zammad start [@MickLesk](https://github.com/MickLesk) ([#12308](https://github.com/community-scripts/ProxmoxVE/pull/12308))
- #### 🔧 Refactor
- OpenProject: Various fixes [@tremor021](https://github.com/tremor021) ([#12246](https://github.com/community-scripts/ProxmoxVE/pull/12246))
### 💾 Core
- #### 🐞 Bug Fixes
- Fix detection of ssh keys [@1-tempest](https://github.com/1-tempest) ([#12230](https://github.com/community-scripts/ProxmoxVE/pull/12230))
- #### ✨ New Features
- tools.func: Improve GitHub/Codeberg API error handling and error output [@MickLesk](https://github.com/MickLesk) ([#12330](https://github.com/community-scripts/ProxmoxVE/pull/12330))
- #### 🔧 Refactor
- core: remove duplicate traps, consolidate error handling and harden signal traps [@MickLesk](https://github.com/MickLesk) ([#12316](https://github.com/community-scripts/ProxmoxVE/pull/12316))
### 📂 Github
- github: improvements for node drift wf [@MickLesk](https://github.com/MickLesk) ([#12309](https://github.com/community-scripts/ProxmoxVE/pull/12309))
## 2026-02-24
### 🚀 Updated Scripts
- several scripts: add additional github link in source [@MickLesk](https://github.com/MickLesk) ([#12282](https://github.com/community-scripts/ProxmoxVE/pull/12282))
- adds further documentation during the installation script. [@d12rio](https://github.com/d12rio) ([#12248](https://github.com/community-scripts/ProxmoxVE/pull/12248))
- #### 🐞 Bug Fixes
- [Fix] PatchMon: remove VITE_API_URL from frontend env [@vhsdream](https://github.com/vhsdream) ([#12294](https://github.com/community-scripts/ProxmoxVE/pull/12294))
- fix(searxng): remove orphaned fi causing syntax error [@mark-jeffrey](https://github.com/mark-jeffrey) ([#12283](https://github.com/community-scripts/ProxmoxVE/pull/12283))
- Refactor n8n [@MickLesk](https://github.com/MickLesk) ([#12264](https://github.com/community-scripts/ProxmoxVE/pull/12264))
- Firefly: PHP bump [@tremor021](https://github.com/tremor021) ([#12247](https://github.com/community-scripts/ProxmoxVE/pull/12247))
- #### ✨ New Features
- Databasus: add mariadb path for mysql/mariadb backups | add mongodb database tools [@MickLesk](https://github.com/MickLesk) ([#12259](https://github.com/community-scripts/ProxmoxVE/pull/12259))
- make searxng updateable [@shtefko](https://github.com/shtefko) ([#12207](https://github.com/community-scripts/ProxmoxVE/pull/12207))
- #### 💥 Breaking Changes
- fix: wealthfolio for v3 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11765](https://github.com/community-scripts/ProxmoxVE/pull/11765))
- #### 🔧 Refactor
- bump various scripts from Node 22 to 24 [@MickLesk](https://github.com/MickLesk) ([#12265](https://github.com/community-scripts/ProxmoxVE/pull/12265))
### 💾 Core
- #### 🐞 Bug Fixes
- core: fix broken "command not found" after err_trap [@MickLesk](https://github.com/MickLesk) ([#12280](https://github.com/community-scripts/ProxmoxVE/pull/12280))
- #### ✨ New Features
- tools.func: add get_latest_gh_tag helper function [@MickLesk](https://github.com/MickLesk) ([#12261](https://github.com/community-scripts/ProxmoxVE/pull/12261))
### 🧰 Tools
- Arcane ([#12263](https://github.com/community-scripts/ProxmoxVE/pull/12263))
### 📂 Github
- github: add weekly Node.js version drift check workflow [@MickLesk](https://github.com/MickLesk) ([#12267](https://github.com/community-scripts/ProxmoxVE/pull/12267))
- add: workflow to close stale PRs [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12243](https://github.com/community-scripts/ProxmoxVE/pull/12243))
## 2026-02-23
### 🆕 New Scripts
- SeaweedFS ([#12220](https://github.com/community-scripts/ProxmoxVE/pull/12220))
- Sonobarr ([#12221](https://github.com/community-scripts/ProxmoxVE/pull/12221))
- SparkyFitness ([#12185](https://github.com/community-scripts/ProxmoxVE/pull/12185))
- Frigate v16.4 [@MickLesk](https://github.com/MickLesk) ([#11887](https://github.com/community-scripts/ProxmoxVE/pull/11887))
### 🚀 Updated Scripts
- #### ✨ New Features
- memos: unpin version due new release artifacts [@MickLesk](https://github.com/MickLesk) ([#12224](https://github.com/community-scripts/ProxmoxVE/pull/12224))
- core: Enhance signal handling, reported "status" and logs [@MickLesk](https://github.com/MickLesk) ([#12216](https://github.com/community-scripts/ProxmoxVE/pull/12216))
- #### 🔧 Refactor
- booklore v2: embed frontend, bump Java to 25, remove nginx [@MickLesk](https://github.com/MickLesk) ([#12223](https://github.com/community-scripts/ProxmoxVE/pull/12223))
### 🗑️ Deleted Scripts
- Remove: Huntarr (deprecated & Security) [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#12226](https://github.com/community-scripts/ProxmoxVE/pull/12226))
### 💾 Core
- #### 🔧 Refactor
- core: Improve error handling and logging for LXC builds [@MickLesk](https://github.com/MickLesk) ([#12208](https://github.com/community-scripts/ProxmoxVE/pull/12208))
### 🌐 Website
- #### 🐞 Bug Fixes
- calibre-web: update default credentials [@LaevaertK](https://github.com/LaevaertK) ([#12201](https://github.com/community-scripts/ProxmoxVE/pull/12201))
- #### 📝 Script Information
- chore: update Frigate documentation and website URLs [@JohnICB](https://github.com/JohnICB) ([#12218](https://github.com/community-scripts/ProxmoxVE/pull/12218))
## 2026-02-22
### 🆕 New Scripts
- Gramps-Web ([#12157](https://github.com/community-scripts/ProxmoxVE/pull/12157))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- fix: Apache Guacamole - bump to Temurin JDK 17 to resolve Debian 13 (Trixie) install failure [@Copilot](https://github.com/Copilot) ([#12161](https://github.com/community-scripts/ProxmoxVE/pull/12161))
- Docker-VM: add error handling for virt-customize finalization [@MickLesk](https://github.com/MickLesk) ([#12127](https://github.com/community-scripts/ProxmoxVE/pull/12127))
- [Fix] Sure: add Sidekiq service [@vhsdream](https://github.com/vhsdream) ([#12186](https://github.com/community-scripts/ProxmoxVE/pull/12186))
- #### ✨ New Features
- Refactor & Bump to v2: Plex [@MickLesk](https://github.com/MickLesk) ([#12179](https://github.com/community-scripts/ProxmoxVE/pull/12179))
- #### 🔧 Refactor
- karakeep: bump to node 24 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12183](https://github.com/community-scripts/ProxmoxVE/pull/12183))
### 💾 Core
- #### ✨ New Features
- tools.func: add GitHub API rate-limit detection and GITHUB_TOKEN support [@MickLesk](https://github.com/MickLesk) ([#12176](https://github.com/community-scripts/ProxmoxVE/pull/12176))
### 🧰 Tools
- CR*NMASTER ([#12065](https://github.com/community-scripts/ProxmoxVE/pull/12065))
- #### 🔧 Refactor
- Update package management commands in clean-lxcs.sh [@heinemannj](https://github.com/heinemannj) ([#12166](https://github.com/community-scripts/ProxmoxVE/pull/12166))
### ❔ Uncategorized
- calibre-web: Update logo URL [@MickLesk](https://github.com/MickLesk) ([#12178](https://github.com/community-scripts/ProxmoxVE/pull/12178))
## 2026-02-21
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Pangolin: restore config before db migration, use drizzle-kit push [@MickLesk](https://github.com/MickLesk) ([#12130](https://github.com/community-scripts/ProxmoxVE/pull/12130))
- PLANKA: fix msg's [@danielalanbates](https://github.com/danielalanbates) ([#12143](https://github.com/community-scripts/ProxmoxVE/pull/12143))
### 🌐 Website
- #### 📝 Script Information
- MediaManager: Update documentation URL [@tremor021](https://github.com/tremor021) ([#12154](https://github.com/community-scripts/ProxmoxVE/pull/12154))
## 2026-02-20
### 🆕 New Scripts
- Sure ([#12114](https://github.com/community-scripts/ProxmoxVE/pull/12114))
- Calibre-Web ([#12115](https://github.com/community-scripts/ProxmoxVE/pull/12115))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Zammad: fix Elasticsearch JVM config and add daemon-reload [@MickLesk](https://github.com/MickLesk) ([#12125](https://github.com/community-scripts/ProxmoxVE/pull/12125))
- Huntarr: add build-essential for native pip dependencies [@MickLesk](https://github.com/MickLesk) ([#12126](https://github.com/community-scripts/ProxmoxVE/pull/12126))
- Dokploy: fix update function [@vhsdream](https://github.com/vhsdream) ([#12116](https://github.com/community-scripts/ProxmoxVE/pull/12116))
- #### 💥 Breaking Changes
- recyclarr: adjust paths for v8.0 breaking changes [@MickLesk](https://github.com/MickLesk) ([#12129](https://github.com/community-scripts/ProxmoxVE/pull/12129))
- #### 🔧 Refactor
- Planka: migrate data paths to new v2 directory structure [@MickLesk](https://github.com/MickLesk) ([#12128](https://github.com/community-scripts/ProxmoxVE/pull/12128))
### 🌐 Website
- #### 📝 Script Information
- fixen broken link to dawarich documentation [@RiX012](https://github.com/RiX012) ([#12103](https://github.com/community-scripts/ProxmoxVE/pull/12103))

View File

@@ -5,7 +5,7 @@
<p><em>A Community Legacy in Memory of @tteck</em></p> <p><em>A Community Legacy in Memory of @tteck</em></p>
<p> <p>
<a href="https://community-scripts.com"> <a href="https://community-scripts.org">
<img src="https://img.shields.io/badge/🌐_Website-Visit-4c9b3f?style=for-the-badge&labelColor=2d3748" alt="Website" /> <img src="https://img.shields.io/badge/🌐_Website-Visit-4c9b3f?style=for-the-badge&labelColor=2d3748" alt="Website" />
</a> </a>
<a href="https://discord.gg/3AnUqsXnmK"> <a href="https://discord.gg/3AnUqsXnmK">

78
ct/bambuddy.sh Normal file
View File

@@ -0,0 +1,78 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: Adrian-RDA
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/maziggy/bambuddy
APP="Bambuddy"
var_tags="${var_tags:-media;3d-printing}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-2048}"
var_disk="${var_disk:-10}"
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/bambuddy ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "bambuddy" "maziggy/bambuddy"; then
msg_info "Stopping Service"
systemctl stop bambuddy
msg_ok "Stopped Service"
msg_info "Backing up Configuration and Data"
cp /opt/bambuddy/.env /opt/bambuddy.env.bak
cp -r /opt/bambuddy/data /opt/bambuddy_data_bak
msg_ok "Backed up Configuration and Data"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "bambuddy" "maziggy/bambuddy" "tarball" "latest" "/opt/bambuddy"
msg_info "Updating Python Dependencies"
cd /opt/bambuddy
$STD uv venv
$STD uv pip install -r requirements.txt
msg_ok "Updated Python Dependencies"
msg_info "Rebuilding Frontend"
cd /opt/bambuddy/frontend
$STD npm install
$STD npm run build
msg_ok "Rebuilt Frontend"
msg_info "Restoring Configuration and Data"
cp /opt/bambuddy.env.bak /opt/bambuddy/.env
cp -r /opt/bambuddy_data_bak/. /opt/bambuddy/data/
rm -f /opt/bambuddy.env.bak
rm -rf /opt/bambuddy_data_bak
msg_ok "Restored Configuration and Data"
msg_info "Starting Service"
systemctl start bambuddy
msg_ok "Started Service"
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}:8000${CL}"

63
ct/birdnet-go.sh Normal file
View File

@@ -0,0 +1,63 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/tphakala/birdnet-go
APP="BirdNET-Go"
var_tags="${var_tags:-monitoring;ai;nature}"
var_cpu="${var_cpu:-4}"
var_ram="${var_ram:-2048}"
var_disk="${var_disk:-12}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
var_gpu="${var_gpu:-no}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -f /usr/local/bin/birdnet-go ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "birdnet" "tphakala/birdnet-go"; then
msg_info "Stopping Service"
systemctl stop birdnet
msg_ok "Stopped Service"
fetch_and_deploy_gh_release "birdnet" "tphakala/birdnet-go" "prebuild" "latest" "/opt/birdnet" "birdnet-go-linux-amd64.tar.gz"
msg_info "Deploying Binary"
cp /opt/birdnet/birdnet-go /usr/local/bin/birdnet-go
chmod +x /usr/local/bin/birdnet-go
cp -r /opt/birdnet/libtensorflowlite_c.so /usr/local/lib/ || true
ldconfig
msg_ok "Deployed Binary"
msg_info "Starting Service"
systemctl start birdnet
msg_ok "Started Service"
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}:8080${CL}"

View File

@@ -70,7 +70,7 @@ function update_script() {
source /opt/dispatcharr/.env source /opt/dispatcharr/.env
set +o allexport set +o allexport
if [[ -n "$POSTGRES_DB" ]] && [[ -n "$POSTGRES_USER" ]] && [[ -n "$POSTGRES_PASSWORD" ]]; then if [[ -n "$POSTGRES_DB" ]] && [[ -n "$POSTGRES_USER" ]] && [[ -n "$POSTGRES_PASSWORD" ]]; then
PGPASSWORD=$POSTGRES_PASSWORD pg_dump -U $POSTGRES_USER -h ${POSTGRES_HOST:-localhost} $POSTGRES_DB >/tmp/dispatcharr_db_$(date +%F).sql PGPASSWORD=$POSTGRES_PASSWORD pg_dump -U "$POSTGRES_USER" -h "${POSTGRES_HOST:-localhost}" -p "${POSTGRES_PORT:-5432}" "$POSTGRES_DB" >/tmp/dispatcharr_db_$(date +%F).sql
msg_info "Database backup created" msg_info "Database backup created"
fi fi
fi fi

View File

@@ -29,11 +29,11 @@ function update_script() {
msg_error "No ${APP} Installation Found!" msg_error "No ${APP} Installation Found!"
exit exit
fi fi
update_available=$(curl -fsSL -X 'GET' "http://localhost:19200/api/status/update-available" -H 'accept: application/json' | jq .UpdateAvailable) update_available=$(curl -fsSL -X 'GET' "http://localhost:19200/api/status/update-available" -H 'accept: application/json' | jq .UpdateAvailable)
if [[ "${update_available}" == "true" ]]; then if [[ "${update_available}" == "true" ]]; then
msg_info "Stopping Service" msg_info "Stopping Service"
systemctl stop fileflows systemctl stop fileflows*
msg_info "Stopped Service" msg_info "Stopped Service"
msg_info "Creating Backup" msg_info "Creating Backup"
@@ -45,7 +45,7 @@ function update_script() {
fetch_and_deploy_from_url "https://fileflows.com/downloads/zip" "/opt/fileflows" fetch_and_deploy_from_url "https://fileflows.com/downloads/zip" "/opt/fileflows"
msg_info "Starting Service" msg_info "Starting Service"
systemctl start fileflows systemctl start fileflows*
msg_ok "Started Service" msg_ok "Started Service"
msg_ok "Updated successfully!" msg_ok "Updated successfully!"
else else

71
ct/geopulse.sh Normal file
View File

@@ -0,0 +1,71 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: CrazyWolf13
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/tess1o/geopulse
APP="GeoPulse"
var_tags="${var_tags:-location;tracking;gps}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-1024}"
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 [[ ! -f /opt/geopulse/backend/geopulse-backend ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "geopulse-backend" "tess1o/geopulse"; then
msg_info "Stopping Service"
systemctl stop geopulse-backend
msg_ok "Stopped Service"
if [[ "$(uname -m)" == "aarch64" ]]; then
if grep -qi "raspberry\|bcm" /proc/cpuinfo 2>/dev/null; then
BINARY_PATTERN="geopulse-backend-native-arm64-compat-*"
else
BINARY_PATTERN="geopulse-backend-native-arm64-[!c]*"
fi
else
if grep -q avx2 /proc/cpuinfo && grep -q bmi2 /proc/cpuinfo && grep -q fma /proc/cpuinfo; then
BINARY_PATTERN="geopulse-backend-native-amd64-[!c]*"
else
BINARY_PATTERN="geopulse-backend-native-amd64-compat-*"
fi
fi
fetch_and_deploy_gh_release "geopulse-backend" "tess1o/geopulse" "singlefile" "latest" "/opt/geopulse/backend" "${BINARY_PATTERN}"
fetch_and_deploy_gh_release "geopulse-frontend" "tess1o/geopulse" "prebuild" "latest" "/var/www/geopulse" "geopulse-frontend-*.tar.gz"
msg_info "Starting Service"
systemctl start geopulse-backend
msg_ok "Started Service"
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}"
echo -e "${INFO}${YW} To create an admin account, run:${CL}"
echo -e "${TAB}${BGN}/usr/local/bin/create-geopulse-admin${CL}"

View File

@@ -32,7 +32,16 @@ function update_script() {
systemctl stop gokapi systemctl stop gokapi
msg_ok "Stopped Service" msg_ok "Stopped Service"
fetch_and_deploy_gh_release "gokapi" "Forceu/Gokapi" "prebuild" "latest" "/opt/gokapi" "gokapi-linux_amd64.zip" fetch_and_deploy_gh_release "gokapi" "Forceu/Gokapi" "prebuild" "latest" "/opt/gokapi" "*linux*amd64.zip"
# Migrate from pre-v2.2.4 binary name (gokapi-linux_amd64 -> gokapi)
if [[ -f /opt/gokapi/gokapi-linux_amd64 ]]; then
rm -f /opt/gokapi/gokapi-linux_amd64
fi
if grep -q "gokapi-linux_amd64" /etc/systemd/system/gokapi.service 2>/dev/null; then
sed -i 's|gokapi-linux_amd64|gokapi|g' /etc/systemd/system/gokapi.service
systemctl daemon-reload
fi
msg_info "Starting Service" msg_info "Starting Service"
systemctl start gokapi systemctl start gokapi

View File

@@ -64,6 +64,12 @@ function update_script() {
} }
start start
if [[ $(sysctl -n vm.max_map_count 2>/dev/null) -lt 262144 ]]; then
sysctl -w vm.max_map_count=262144 >/dev/null 2>&1
echo "vm.max_map_count=262144" >/etc/sysctl.d/graylog.conf
fi
build_container build_container
description description

6
ct/headers/bambuddy Normal file
View File

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

6
ct/headers/birdnet-go Normal file
View File

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

6
ct/headers/geopulse Normal file
View File

@@ -0,0 +1,6 @@
______ ____ __
/ ____/__ ____ / __ \__ __/ /_______
/ / __/ _ \/ __ \/ /_/ / / / / / ___/ _ \
/ /_/ / __/ /_/ / ____/ /_/ / (__ ) __/
\____/\___/\____/_/ \__,_/_/____/\___/

6
ct/headers/matter-server Normal file
View File

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

6
ct/headers/yourls Normal file
View File

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

View File

@@ -73,7 +73,7 @@ function update_script() {
$STD curl -fsSL https://github.com/filebrowser/filebrowser/releases/download/v2.23.0/linux-amd64-filebrowser.tar.gz | tar -xzv -C /usr/local/bin $STD curl -fsSL https://github.com/filebrowser/filebrowser/releases/download/v2.23.0/linux-amd64-filebrowser.tar.gz | tar -xzv -C /usr/local/bin
$STD filebrowser config init -a '0.0.0.0' $STD filebrowser config init -a '0.0.0.0'
$STD filebrowser config set -a '0.0.0.0' $STD filebrowser config set -a '0.0.0.0'
$STD filebrowser users add admin community-scripts.com --perm.admin $STD filebrowser users add admin community-scripts.org --perm.admin
msg_ok "Installed FileBrowser" msg_ok "Installed FileBrowser"
msg_info "Creating Service" msg_info "Creating Service"
@@ -93,7 +93,7 @@ WantedBy=default.target" >$service_path
msg_ok "Completed successfully!\n" msg_ok "Completed successfully!\n"
echo -e "FileBrowser should be reachable by going to the following URL. echo -e "FileBrowser should be reachable by going to the following URL.
${BL}http://$LOCAL_IP:8080${CL} admin|community-scripts.com\n" ${BL}http://$LOCAL_IP:8080${CL} admin|community-scripts.org\n"
exit exit
fi fi
} }

View File

@@ -109,7 +109,7 @@ EOF
msg_ok "Image-processing libraries up to date" msg_ok "Image-processing libraries up to date"
fi fi
RELEASE="v2.6.1" RELEASE="v2.6.3"
if check_for_gh_release "Immich" "immich-app/immich" "${RELEASE}" "each release is tested individually before the version is updated. Please do not open issues for this"; then if check_for_gh_release "Immich" "immich-app/immich" "${RELEASE}" "each release is tested individually before the version is updated. Please do not open issues for this"; then
if [[ $(cat ~/.immich) > "2.5.1" ]]; then if [[ $(cat ~/.immich) > "2.5.1" ]]; then
msg_info "Enabling Maintenance Mode" msg_info "Enabling Maintenance Mode"
@@ -214,7 +214,10 @@ EOF
cd "$SRC_DIR"/machine-learning cd "$SRC_DIR"/machine-learning
mkdir -p "$ML_DIR" mkdir -p "$ML_DIR"
chown -R immich:immich "$INSTALL_DIR" # chown excluding upload dir contents (may be a mount with restricted permissions)
chown immich:immich "$INSTALL_DIR"
find "$INSTALL_DIR" -maxdepth 1 -mindepth 1 ! -name upload -exec chown -R immich:immich {} +
chown immich:immich "${UPLOAD_DIR:-$INSTALL_DIR/upload}" 2>/dev/null || true
chown immich:immich ./uv.lock chown immich:immich ./uv.lock
export VIRTUAL_ENV="${ML_DIR}"/ml-venv export VIRTUAL_ENV="${ML_DIR}"/ml-venv
export UV_HTTP_TIMEOUT=300 export UV_HTTP_TIMEOUT=300
@@ -263,11 +266,24 @@ EOF
[[ ! -f /usr/bin/immich ]] && ln -sf "$APP_DIR"/cli/bin/immich /usr/bin/immich [[ ! -f /usr/bin/immich ]] && ln -sf "$APP_DIR"/cli/bin/immich /usr/bin/immich
[[ ! -f /usr/bin/immich-admin ]] && ln -sf "$APP_DIR"/bin/immich-admin /usr/bin/immich-admin [[ ! -f /usr/bin/immich-admin ]] && ln -sf "$APP_DIR"/bin/immich-admin /usr/bin/immich-admin
chown -R immich:immich "$INSTALL_DIR" if ! grep -q '^DB_HOSTNAME=' "$INSTALL_DIR"/.env; then
sed -i '/^DB_DATABASE_NAME/a DB_HOSTNAME=127.0.0.1' "$INSTALL_DIR"/.env
fi
if grep -q 'ExecStart=/usr/bin/node' /etc/systemd/system/immich-web.service; then
sed -i '/^EnvironmentFile=/d' /etc/systemd/system/immich-web.service
sed -i "s|^ExecStart=.*|ExecStart=${APP_DIR}/bin/start.sh|" /etc/systemd/system/immich-web.service
systemctl daemon-reload
fi
# chown excluding upload dir contents (may be a mount with restricted permissions)
chown immich:immich "$INSTALL_DIR"
find "$INSTALL_DIR" -maxdepth 1 -mindepth 1 ! -name upload -exec chown -R immich:immich {} +
chown immich:immich "${UPLOAD_DIR:-$INSTALL_DIR/upload}" 2>/dev/null || true
if [[ "${MAINT_MODE:-0}" == 1 ]]; then if [[ "${MAINT_MODE:-0}" == 1 ]]; then
msg_info "Disabling Maintenance Mode" msg_info "Disabling Maintenance Mode"
cd /opt/immich/app/bin cd /opt/immich/app/bin
$STD ./immich-admin disable-maintenance-mode $STD ./immich-admin disable-maintenance-mode || true
unset MAINT_MODE unset MAINT_MODE
$STD cd - $STD cd -
msg_ok "Disabled Maintenance Mode" msg_ok "Disabled Maintenance Mode"

View File

@@ -48,7 +48,9 @@ function update_script() {
# Ensure APP_RUNTIME is in .env.local for CLI commands (upgrades from older versions) # Ensure APP_RUNTIME is in .env.local for CLI commands (upgrades from older versions)
if ! grep -q "APP_RUNTIME" /opt/koillection/.env.local 2>/dev/null; then if ! grep -q "APP_RUNTIME" /opt/koillection/.env.local 2>/dev/null; then
echo 'APP_RUNTIME="Symfony\Component\Runtime\SymfonyRuntime"' >> /opt/koillection/.env.local # Ensure file ends with newline before appending to avoid concatenation
[[ -s /opt/koillection/.env.local && -n "$(tail -c 1 /opt/koillection/.env.local)" ]] && echo "" >>/opt/koillection/.env.local
echo 'APP_RUNTIME="Symfony\Component\Runtime\SymfonyRuntime"' >>/opt/koillection/.env.local
fi fi
export COMPOSER_ALLOW_SUPERUSER=1 export COMPOSER_ALLOW_SUPERUSER=1

60
ct/matter-server.sh Normal file
View File

@@ -0,0 +1,60 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/matter-js/python-matter-server
APP="Matter-Server"
var_tags="${var_tags:-matter;iot;smart-home}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-2048}"
var_disk="${var_disk:-4}"
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/matter-server ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "matter-server" "matter-js/python-matter-server"; then
msg_info "Stopping Service"
systemctl stop matter-server
msg_ok "Stopped Service"
msg_info "Updating Matter Server"
MATTER_VERSION=$(get_latest_github_release "matter-js/python-matter-server")
$STD uv pip install --python /opt/matter-server/.venv/bin/python --upgrade "python-matter-server[server]==${MATTER_VERSION}"
echo "${MATTER_VERSION}" >~/.matter-server
msg_ok "Updated Matter Server"
fetch_and_deploy_gh_release "chip-ota-provider-app" "home-assistant-libs/matter-linux-ota-provider" "singlefile" "latest" "/usr/local/bin" "chip-ota-provider-app-x86-64"
msg_info "Starting Service"
systemctl start matter-server
msg_ok "Started Service"
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} Matter Server WebSocket API is running on port 5580.${CL}"
echo -e "${TAB}${GATEWAY}${BGN}ws://${IP}:5580/ws${CL}"

View File

@@ -28,11 +28,6 @@ function update_script() {
exit exit
fi fi
if [[ $(grep -E '^VERSION_ID=' /etc/os-release) == *"12"* ]]; then
msg_error "Wrong Debian version detected!"
msg_error "Please create a snapshot first. You must upgrade your LXC to Debian Trixie before updating. Visit: https://github.com/community-scripts/ProxmoxVE/discussions/7489"
exit
fi
if command -v node &>/dev/null; then if command -v node &>/dev/null; then
CURRENT_NODE_VERSION=$(node --version | cut -d'v' -f2 | cut -d'.' -f1) CURRENT_NODE_VERSION=$(node --version | cut -d'v' -f2 | cut -d'.' -f1)
@@ -98,6 +93,7 @@ ExecStart=/usr/local/openresty/nginx/sbin/nginx -g 'daemon off;'
WantedBy=multi-user.target WantedBy=multi-user.target
EOF EOF
systemctl daemon-reload systemctl daemon-reload
systemctl unmask openresty 2>/dev/null || true
systemctl restart openresty systemctl restart openresty
msg_ok "Built OpenResty" msg_ok "Built OpenResty"
fi fi

View File

@@ -68,7 +68,7 @@ function update_script() {
$STD curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash $STD curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash
$STD filebrowser config init -a '0.0.0.0' $STD filebrowser config init -a '0.0.0.0'
$STD filebrowser config set -a '0.0.0.0' $STD filebrowser config set -a '0.0.0.0'
$STD filebrowser users add admin community-scripts.com --perm.admin $STD filebrowser users add admin community-scripts.org --perm.admin
msg_ok "Installed FileBrowser" msg_ok "Installed FileBrowser"
msg_info "Creating Service" msg_info "Creating Service"
@@ -90,7 +90,7 @@ EOF
msg_ok "Completed successfully!\n" msg_ok "Completed successfully!\n"
echo -e "FileBrowser should be reachable by going to the following URL. echo -e "FileBrowser should be reachable by going to the following URL.
${BL}http://$LOCAL_IP:8080${CL} admin|community-scripts.com\n" ${BL}http://$LOCAL_IP:8080${CL} admin|community-scripts.org\n"
exit exit
fi fi
if [ "$UPD" == "4" ]; then if [ "$UPD" == "4" ]; then

View File

@@ -40,7 +40,7 @@ function update_script() {
cd /opt/revealjs cd /opt/revealjs
$STD npm install $STD npm install
cp -f /opt/index.html /opt/revealjs cp -f /opt/index.html /opt/revealjs
sed -i '25s/localhost/0.0.0.0/g' /opt/revealjs/gulpfile.js sed -i 's/"vite"/"vite --host"/g' package.json
rm -f /opt/index.html rm -f /opt/index.html
msg_ok "Updated RevealJS" msg_ok "Updated RevealJS"

View File

@@ -50,7 +50,7 @@ function update_script() {
/opt/semaphore/config.json /opt/semaphore/config.json
SEM_PW=$(cat ~/semaphore.creds) SEM_PW=$(cat ~/semaphore.creds)
systemctl start semaphore systemctl start semaphore
$STD semaphore user add --admin --login admin --email admin@community-scripts.com --name Administrator --password "${SEM_PW}" --config /opt/semaphore/config.json $STD semaphore user add --admin --login admin --email admin@community-scripts.org --name Administrator --password "${SEM_PW}" --config /opt/semaphore/config.json
msg_ok "Moved from BoltDB to SQLite" msg_ok "Moved from BoltDB to SQLite"
fi fi

View File

@@ -33,6 +33,10 @@ function update_script() {
exit exit
fi fi
if ! grep -q "^ALLOWED_HOSTS=" /opt/tandoor/.env; then
echo "ALLOWED_HOSTS=${LOCAL_IP}" >>/opt/tandoor/.env
fi
if check_for_gh_release "tandoor" "TandoorRecipes/recipes"; then if check_for_gh_release "tandoor" "TandoorRecipes/recipes"; then
msg_info "Stopping Service" msg_info "Stopping Service"
systemctl stop tandoor systemctl stop tandoor

View File

@@ -34,24 +34,26 @@ function update_script() {
[[ -f /etc/systemd/system/victoriametrics-logs.service ]] && systemctl stop victoriametrics-logs [[ -f /etc/systemd/system/victoriametrics-logs.service ]] && systemctl stop victoriametrics-logs
msg_ok "Stopped Service" msg_ok "Stopped Service"
victoriametrics_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases/latest" | victoriametrics_release=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases" |
jq -r '.assets[].name' | jq -r '.[] | select(.assets[].name | match("^victoria-metrics-linux-amd64-v[0-9.]+.tar.gz$")) | .tag_name' |
grep -E '^victoria-metrics-linux-amd64-v[0-9.]+\.tar\.gz$') head -n 1)
vmutils_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases/latest" |
jq -r '.assets[].name' |
grep -E '^vmutils-linux-amd64-v[0-9.]+\.tar\.gz$')
fetch_and_deploy_gh_release "victoriametrics" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "$victoriametrics_filename" msg_debug "Using release $victoriametrics_release"
fetch_and_deploy_gh_release "vmutils" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "$vmutils_filename"
victoriametrics_filename="victoria-metrics-linux-amd64-${victoriametrics_release}.tar.gz"
vmutils_filename="vmutils-linux-amd64-${victoriametrics_release}.tar.gz"
fetch_and_deploy_gh_release "victoriametrics" "VictoriaMetrics/VictoriaMetrics" "prebuild" "$victoriametrics_release" "/opt/victoriametrics" "$victoriametrics_filename"
fetch_and_deploy_gh_release "vmutils" "VictoriaMetrics/VictoriaMetrics" "prebuild" "$victoriametrics_release" "/opt/victoriametrics" "$vmutils_filename"
if [[ -f /etc/systemd/system/victoriametrics-logs.service ]]; then if [[ -f /etc/systemd/system/victoriametrics-logs.service ]]; then
vmlogs_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaLogs/releases/latest" | vmlogs_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaLogs/releases/latest" |
jq -r '.assets[].name' | jq -r '.assets[].name' |
grep -E '^victoria-logs-linux-amd64-v[0-9.]+\.tar\.gz$') grep -E '^victoria-logs-linux-amd64-v[0-9.]+\.tar\.gz$')
vlutils_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaLogs/releases/latest" | vlutils_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaLogs/releases/latest" |
jq -r '.assets[].name' | jq -r '.assets[].name' |
grep -E '^vlutils-linux-amd64-v[0-9.]+\.tar\.gz$') grep -E '^vlutils-linux-amd64-v[0-9.]+\.tar\.gz$')
fetch_and_deploy_gh_release "victorialogs" "VictoriaMetrics/VictoriaLogs" "prebuild" "latest" "/opt/victoriametrics" "$vmlogs_filename" fetch_and_deploy_gh_release "victorialogs" "VictoriaMetrics/VictoriaLogs" "prebuild" "latest" "/opt/victoriametrics" "$vmlogs_filename"
fetch_and_deploy_gh_release "vlutils" "VictoriaMetrics/VictoriaLogs" "prebuild" "latest" "/opt/victoriametrics" "$vlutils_filename" fetch_and_deploy_gh_release "vlutils" "VictoriaMetrics/VictoriaLogs" "prebuild" "latest" "/opt/victoriametrics" "$vlutils_filename"
fi fi

66
ct/yourls.sh Normal file
View File

@@ -0,0 +1,66 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://yourls.org/
APP="YOURLS"
var_tags="${var_tags:-url-shortener;php}"
var_cpu="${var_cpu:-1}"
var_ram="${var_ram:-512}"
var_disk="${var_disk:-4}"
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 [[ ! -f /opt/yourls/yourls-loader.php ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "yourls" "YOURLS/YOURLS"; then
msg_info "Stopping Service"
systemctl stop nginx
msg_ok "Stopped Service"
msg_info "Backing up Configuration"
cp -r /opt/yourls/user /opt/yourls_user.bak
msg_ok "Backed up Configuration"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "yourls" "YOURLS/YOURLS" "tarball"
chown -R www-data:www-data /opt/yourls
msg_info "Restoring Configuration"
cp -r /opt/yourls_user.bak/. /opt/yourls/user/
rm -rf /opt/yourls_user.bak
msg_ok "Restored Configuration"
msg_info "Starting Service"
systemctl start nginx
msg_ok "Started Service"
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} First, complete the database setup at:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/admin/install.php${CL}"
echo -e "${INFO}${YW} Admin credentials are in the install log:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}grep -A2 'admin' /opt/yourls/user/config.php${CL}"

View File

@@ -62,10 +62,10 @@ expect "Email address"
send "\r" send "\r"
expect "Password" expect "Password"
send "community-scripts.com\r" send "community-scripts.org\r"
expect "Password (again)" expect "Password (again)"
send "community-scripts.com\r" send "community-scripts.org\r"
expect eof expect eof
EOF EOF

View File

@@ -58,7 +58,7 @@ service:
use_prerelease: false use_prerelease: false
dashboard: dashboard:
icon: https://raw.githubusercontent.com/community-scripts/ProxmoxVE/refs/heads/main/misc/images/logo.png icon: https://raw.githubusercontent.com/community-scripts/ProxmoxVE/refs/heads/main/misc/images/logo.png
icon_link_to: https://community-scripts.com/ icon_link_to: https://community-scripts.org/
web_url: https://github.com/community-scripts/ProxmoxVE/releases web_url: https://github.com/community-scripts/ProxmoxVE/releases
EOF EOF
msg_ok "Setup Config" msg_ok "Setup Config"

View File

@@ -0,0 +1,67 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: Adrian-RDA
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/maziggy/bambuddy
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 libglib2.0-0
msg_ok "Installed Dependencies"
PYTHON_VERSION="3.13" setup_uv
NODE_VERSION="22" setup_nodejs
fetch_and_deploy_gh_release "bambuddy" "maziggy/bambuddy" "tarball" "latest" "/opt/bambuddy"
msg_info "Setting up Python Environment"
cd /opt/bambuddy
$STD uv venv
$STD uv pip install -r requirements.txt
msg_ok "Set up Python Environment"
msg_info "Building Frontend"
cd /opt/bambuddy/frontend
$STD npm install
$STD npm run build
msg_ok "Built Frontend"
msg_info "Configuring Bambuddy"
mkdir -p /opt/bambuddy/data /opt/bambuddy/logs
cat <<EOF >/opt/bambuddy/.env
DEBUG=false
LOG_LEVEL=INFO
LOG_TO_FILE=true
EOF
msg_ok "Configured Bambuddy"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/bambuddy.service
[Unit]
Description=Bambuddy - Bambu Lab Print Management
Documentation=https://github.com/maziggy/bambuddy
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/bambuddy
ExecStart=/opt/bambuddy/.venv/bin/uvicorn backend.app.main:app --host 0.0.0.0 --port 8000
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now bambuddy
msg_ok "Created Service"
motd_ssh
customize
cleanup_lxc

View File

@@ -0,0 +1,56 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/tphakala/birdnet-go
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 \
libasound2 \
sox \
alsa-utils \
ffmpeg
msg_ok "Installed Dependencies"
fetch_and_deploy_gh_release "birdnet" "tphakala/birdnet-go" "prebuild" "latest" "/opt/birdnet" "birdnet-go-linux-amd64.tar.gz"
msg_info "Setting up BirdNET-Go"
cp /opt/birdnet/birdnet-go /usr/local/bin/birdnet-go
chmod +x /usr/local/bin/birdnet-go
cp -r /opt/birdnet/libtensorflowlite_c.so /usr/local/lib/ || true
ldconfig
mkdir -p /opt/birdnet/data/clips
msg_ok "Set up BirdNET-Go"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/birdnet.service
[Unit]
Description=BirdNET
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/birdnet/data
ExecStart=/usr/local/bin/birdnet-go realtime
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now birdnet
msg_ok "Created Service"
motd_ssh
customize
cleanup_lxc

View File

@@ -33,13 +33,25 @@ msg_ok "Installed ASP.NET Core Runtime"
fetch_and_deploy_from_url "https://fileflows.com/downloads/zip" "/opt/fileflows" fetch_and_deploy_from_url "https://fileflows.com/downloads/zip" "/opt/fileflows"
msg_info "Setup FileFlows"
$STD ln -svf /usr/bin/ffmpeg /usr/local/bin/ffmpeg $STD ln -svf /usr/bin/ffmpeg /usr/local/bin/ffmpeg
$STD ln -svf /usr/bin/ffprobe /usr/local/bin/ffprobe $STD ln -svf /usr/bin/ffprobe /usr/local/bin/ffprobe
cd /opt/fileflows/Server
dotnet FileFlows.Server.dll --systemd install --root true read -r -p "${TAB3}Do you want to install FileFlows Server or Node? (S/N): " install_server
systemctl enable -q --now fileflows
msg_ok "Setup FileFlows" if [[ "$install_server" =~ ^[Ss]$ ]]; then
msg_info "Installing FileFlows Server"
cd /opt/fileflows/Server
$STD dotnet FileFlows.Server.dll --systemd install --root true
systemctl enable -q --now fileflows
msg_ok "Installed FileFlows Server"
else
msg_info "Installing FileFlows Node"
cd /opt/fileflows/Node
$STD dotnet FileFlows.Node.dll
$STD dotnet FileFlows.Node.dll --systemd install --root true
systemctl enable -q --now fileflows-node
msg_ok "Installed FileFlows Node"
fi
motd_ssh motd_ssh
customize customize

View File

@@ -110,7 +110,7 @@ export AUTOGRAPH_VERBOSITY=0
export GLOG_minloglevel=3 export GLOG_minloglevel=3
export GLOG_logtostderr=0 export GLOG_logtostderr=0
fetch_and_deploy_gh_release "frigate" "blakeblackshear/frigate" "tarball" "v0.17.0" "/opt/frigate" fetch_and_deploy_gh_release "frigate" "blakeblackshear/frigate" "tarball" "v0.17.1" "/opt/frigate"
msg_info "Building Nginx" msg_info "Building Nginx"
$STD bash /opt/frigate/docker/main/build_nginx.sh $STD bash /opt/frigate/docker/main/build_nginx.sh
@@ -182,23 +182,6 @@ cp /opt/frigate/audio-labelmap.txt /audio-labelmap.txt
rm -f /tmp/yamnet.tar.gz rm -f /tmp/yamnet.tar.gz
msg_ok "Downloaded Audio Model" msg_ok "Downloaded Audio Model"
msg_info "Installing HailoRT Runtime"
$STD bash /opt/frigate/docker/main/install_hailort.sh
cp -a /opt/frigate/docker/main/rootfs/. /
sed -i '/^.*unset DEBIAN_FRONTEND.*$/d' /opt/frigate/docker/main/install_deps.sh
echo "libedgetpu1-max libedgetpu/accepted-eula boolean true" | debconf-set-selections
echo "libedgetpu1-max libedgetpu/install-confirm-max boolean true" | debconf-set-selections
echo 'force-overwrite' >/etc/dpkg/dpkg.cfg.d/force-overwrite
$STD bash /opt/frigate/docker/main/install_deps.sh
rm -f /etc/dpkg/dpkg.cfg.d/force-overwrite
$STD pip3 install -U /wheels/*.whl
ldconfig
msg_ok "Installed HailoRT Runtime"
msg_info "Installing MemryX Runtime"
$STD bash /opt/frigate/docker/main/install_memryx.sh
msg_ok "Installed MemryX Runtime"
msg_info "Installing OpenVino" msg_info "Installing OpenVino"
$STD pip3 install -r /opt/frigate/docker/main/requirements-ov.txt $STD pip3 install -r /opt/frigate/docker/main/requirements-ov.txt
msg_ok "Installed OpenVino" msg_ok "Installed OpenVino"
@@ -228,6 +211,23 @@ else
msg_warn "OpenVino build failed (CPU may not support required instructions). Frigate will use CPU model." msg_warn "OpenVino build failed (CPU may not support required instructions). Frigate will use CPU model."
fi fi
msg_info "Installing HailoRT Runtime"
$STD bash /opt/frigate/docker/main/install_hailort.sh
cp -a /opt/frigate/docker/main/rootfs/. /
sed -i '/^.*unset DEBIAN_FRONTEND.*$/d' /opt/frigate/docker/main/install_deps.sh
echo "libedgetpu1-max libedgetpu/accepted-eula boolean true" | debconf-set-selections
echo "libedgetpu1-max libedgetpu/install-confirm-max boolean true" | debconf-set-selections
echo 'force-overwrite' >/etc/dpkg/dpkg.cfg.d/force-overwrite
$STD bash /opt/frigate/docker/main/install_deps.sh
rm -f /etc/dpkg/dpkg.cfg.d/force-overwrite
$STD pip3 install -U /wheels/*.whl
ldconfig
msg_ok "Installed HailoRT Runtime"
msg_info "Installing MemryX Runtime"
$STD bash /opt/frigate/docker/main/install_memryx.sh
msg_ok "Installed MemryX Runtime"
msg_info "Building Frigate Application (Patience)" msg_info "Building Frigate Application (Patience)"
cd /opt/frigate cd /opt/frigate
$STD pip3 install -r /opt/frigate/docker/main/requirements-dev.txt $STD pip3 install -r /opt/frigate/docker/main/requirements-dev.txt

205
install/geopulse-install.sh Normal file
View File

@@ -0,0 +1,205 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: CrazyWolf13
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/tess1o/geopulse
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 \
openssl \
nginx
msg_ok "Installed Dependencies"
PG_VERSION="17" PG_MODULES="postgis" setup_postgresql
PG_DB_NAME="geopulse" PG_DB_USER="geopulse" PG_DB_EXTENSIONS="postgis,postgis_topology" setup_postgresql_db
msg_info "Generating Security Keys"
mkdir -p /opt/geopulse/{backend,keys}
mkdir -p /etc/geopulse /var/www/geopulse /var/lib/geopulse/dumps
mkdir -p /var/log/geopulse/{backend,nginx}
openssl genpkey -algorithm RSA -out /opt/geopulse/keys/jwt-private-key.pem 2>/dev/null
openssl rsa -pubout -in /opt/geopulse/keys/jwt-private-key.pem -out /opt/geopulse/keys/jwt-public-key.pem 2>/dev/null
openssl rand -base64 32 >/opt/geopulse/keys/ai-encryption-key.txt
chmod 640 /opt/geopulse/keys/jwt-private-key.pem /opt/geopulse/keys/jwt-public-key.pem /opt/geopulse/keys/ai-encryption-key.txt
msg_ok "Generated Security Keys"
if [[ "$(uname -m)" == "aarch64" ]]; then
if grep -qi "raspberry\|bcm" /proc/cpuinfo 2>/dev/null; then
BINARY_PATTERN="geopulse-backend-native-arm64-compat-*"
else
BINARY_PATTERN="geopulse-backend-native-arm64-[!c]*"
fi
else
if grep -q avx2 /proc/cpuinfo && grep -q bmi2 /proc/cpuinfo && grep -q fma /proc/cpuinfo; then
BINARY_PATTERN="geopulse-backend-native-amd64-[!c]*"
else
BINARY_PATTERN="geopulse-backend-native-amd64-compat-*"
fi
fi
fetch_and_deploy_gh_release "geopulse-backend" "tess1o/geopulse" "singlefile" "latest" "/opt/geopulse/backend" "${BINARY_PATTERN}"
fetch_and_deploy_gh_release "geopulse-frontend" "tess1o/geopulse" "prebuild" "latest" "/var/www/geopulse" "geopulse-frontend-*.tar.gz"
msg_info "Configuring GeoPulse"
cat <<EOF >/etc/geopulse/geopulse.env
GEOPULSE_PUBLIC_BASE_URL=http://${LOCAL_IP}
GEOPULSE_UI_URL=http://${LOCAL_IP}
GEOPULSE_CORS_ENABLED=false
GEOPULSE_CORS_ORIGINS=
QUARKUS_HTTP_PORT=8080
GEOPULSE_POSTGRES_URL=jdbc:postgresql://localhost:5432/${PG_DB_NAME}
GEOPULSE_POSTGRES_HOST=localhost
GEOPULSE_POSTGRES_PORT=5432
GEOPULSE_POSTGRES_DB=${PG_DB_NAME}
GEOPULSE_POSTGRES_USERNAME=${PG_DB_USER}
GEOPULSE_POSTGRES_PASSWORD=${PG_DB_PASS}
GEOPULSE_JWT_PRIVATE_KEY_LOCATION=file:/opt/geopulse/keys/jwt-private-key.pem
GEOPULSE_JWT_PUBLIC_KEY_LOCATION=file:/opt/geopulse/keys/jwt-public-key.pem
GEOPULSE_AI_ENCRYPTION_KEY_LOCATION=file:/opt/geopulse/keys/ai-encryption-key.txt
QUARKUS_LOG_FILE_ENABLE=true
QUARKUS_LOG_FILE_PATH=/var/log/geopulse/backend/geopulse.log
QUARKUS_LOG_FILE_ROTATION_MAX_FILE_SIZE=10M
QUARKUS_LOG_FILE_ROTATION_MAX_BACKUP_INDEX=5
EOF
chmod 640 /etc/geopulse/geopulse.env
msg_ok "Configured GeoPulse"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/geopulse-backend.service
[Unit]
Description=GeoPulse Backend
After=network.target postgresql.service
Wants=postgresql.service
[Service]
Type=simple
User=root
WorkingDirectory=/opt/geopulse/backend
EnvironmentFile=/etc/geopulse/geopulse.env
ExecStart=/opt/geopulse/backend/geopulse-backend -Dquarkus.http.host=0.0.0.0 -XX:MaximumHeapSizePercent=70 -XX:MaximumYoungGenerationSizePercent=15
Restart=on-failure
RestartSec=10
StandardOutput=append:/var/log/geopulse/backend/geopulse-stdout.log
StandardError=append:/var/log/geopulse/backend/geopulse-stderr.log
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now geopulse-backend
msg_ok "Created Service"
msg_info "Configuring Nginx"
mkdir -p /var/cache/nginx/osm_tiles
cat <<'EOF' >/etc/nginx/sites-available/geopulse.conf
proxy_cache_path /var/cache/nginx/osm_tiles levels=1:2 keys_zone=osm_cache:100m max_size=10g inactive=30d use_temp_path=off;
map $uri $osm_subdomain {
~^/osm/tiles/a/ "a";
~^/osm/tiles/b/ "b";
~^/osm/tiles/c/ "c";
default "a";
}
server {
listen 80;
server_name _;
root /var/www/geopulse;
index index.html;
client_max_body_size 100M;
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_comp_level 6;
gzip_min_length 1000;
location ~* ^/(?!osm/).*\.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000";
}
location ^~ /osm/tiles/ {
resolver 8.8.8.8 valid=300s;
resolver_timeout 10s;
rewrite ^/osm/tiles/[abc]/(.*)$ /$1 break;
proxy_pass https://$osm_subdomain.tile.openstreetmap.org;
proxy_cache osm_cache;
proxy_cache_key "$scheme$proxy_host$uri";
proxy_cache_valid 200 30d;
proxy_cache_valid 404 1m;
proxy_cache_valid 502 503 504 1m;
proxy_ignore_headers Cache-Control Expires Set-Cookie;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
proxy_cache_lock on;
proxy_set_header Cookie "";
proxy_set_header Authorization "";
proxy_set_header User-Agent "GeoPulse/1.0";
proxy_set_header Host $osm_subdomain.tile.openstreetmap.org;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_connect_timeout 10s;
proxy_read_timeout 10s;
expires 30d;
add_header Cache-Control "public, immutable";
add_header X-Cache-Status $upstream_cache_status always;
}
location /api/ {
proxy_pass http://localhost:8080/api/;
proxy_connect_timeout 3600s;
proxy_send_timeout 3600s;
proxy_read_timeout 3600s;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
try_files $uri $uri/ /index.html;
}
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
access_log /var/log/geopulse/nginx/access.log;
error_log /var/log/geopulse/nginx/error.log;
}
EOF
ln -sf /etc/nginx/sites-available/geopulse.conf /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
systemctl enable -q --now nginx
systemctl reload nginx
msg_ok "Configured Nginx"
msg_info "Creating Admin Helper"
cat <<'EOF' >/usr/local/bin/create-geopulse-admin
#!/usr/bin/env bash
read -rp "Enter admin email address: " ADMIN_EMAIL
if [[ -z "$ADMIN_EMAIL" ]]; then
echo "No email provided. Aborting."
exit 1
fi
sed -i '/^GEOPULSE_ADMIN_EMAIL=/d' /etc/geopulse/geopulse.env
echo "GEOPULSE_ADMIN_EMAIL=${ADMIN_EMAIL}" >>/etc/geopulse/geopulse.env
systemctl restart geopulse-backend
echo "Admin email set to '${ADMIN_EMAIL}'. Register with this email in the GeoPulse UI to receive admin privileges."
EOF
chmod +x /usr/local/bin/create-geopulse-admin
msg_ok "Created Admin Helper"
motd_ssh
customize
cleanup_lxc

View File

@@ -13,11 +13,11 @@ setting_up_container
network_check network_check
update_os update_os
fetch_and_deploy_gh_release "gokapi" "Forceu/Gokapi" "prebuild" "latest" "/opt/gokapi" "gokapi-linux_amd64.zip" fetch_and_deploy_gh_release "gokapi" "Forceu/Gokapi" "prebuild" "latest" "/opt/gokapi" "*linux*amd64.zip"
msg_info "Configuring Gokapi" msg_info "Configuring Gokapi"
mkdir -p /opt/gokapi/{data,config} mkdir -p /opt/gokapi/{data,config}
chmod +x /opt/gokapi/gokapi-linux_amd64 chmod +x /opt/gokapi/gokapi
msg_ok "Configured Gokapi" msg_ok "Configured Gokapi"
msg_info "Creating Service" msg_info "Creating Service"
@@ -29,7 +29,7 @@ Description=gokapi
Type=simple Type=simple
Environment=GOKAPI_DATA_DIR=/opt/gokapi/data Environment=GOKAPI_DATA_DIR=/opt/gokapi/data
Environment=GOKAPI_CONFIG_DIR=/opt/gokapi/config Environment=GOKAPI_CONFIG_DIR=/opt/gokapi/config
ExecStart=/opt/gokapi/gokapi-linux_amd64 ExecStart=/opt/gokapi/gokapi
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

@@ -35,7 +35,7 @@ PG_DB_NAME="healthchecks_db" PG_DB_USER="hc_user" PG_DB_PASS=$(openssl rand -bas
msg_info "Setup Keys (Admin / Secret)" msg_info "Setup Keys (Admin / Secret)"
SECRET_KEY="$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-32)" SECRET_KEY="$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-32)"
ADMIN_EMAIL="admin@community-scripts.com" ADMIN_EMAIL="admin@community-scripts.org"
ADMIN_PASSWORD="$PG_DB_PASS" ADMIN_PASSWORD="$PG_DB_PASS"
{ {
echo "healthchecks Admin Email: $ADMIN_EMAIL" echo "healthchecks Admin Email: $ADMIN_EMAIL"

View File

@@ -295,7 +295,7 @@ ML_DIR="${APP_DIR}/machine-learning"
GEO_DIR="${INSTALL_DIR}/geodata" GEO_DIR="${INSTALL_DIR}/geodata"
mkdir -p {"${APP_DIR}","${UPLOAD_DIR}","${GEO_DIR}","${INSTALL_DIR}"/cache} mkdir -p {"${APP_DIR}","${UPLOAD_DIR}","${GEO_DIR}","${INSTALL_DIR}"/cache}
fetch_and_deploy_gh_release "Immich" "immich-app/immich" "tarball" "v2.6.1" "$SRC_DIR" fetch_and_deploy_gh_release "Immich" "immich-app/immich" "tarball" "v2.6.3" "$SRC_DIR"
PNPM_VERSION="$(jq -r '.packageManager | split("@")[1] | split("+")[0]' ${SRC_DIR}/package.json)" PNPM_VERSION="$(jq -r '.packageManager | split("@")[1] | split("+")[0]' ${SRC_DIR}/package.json)"
NODE_VERSION="24" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs NODE_VERSION="24" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs
@@ -344,7 +344,11 @@ msg_ok "Installed Immich Server, Web and Plugin Components"
cd "$SRC_DIR"/machine-learning cd "$SRC_DIR"/machine-learning
$STD useradd -U -s /usr/sbin/nologin -r -M -d "$INSTALL_DIR" immich $STD useradd -U -s /usr/sbin/nologin -r -M -d "$INSTALL_DIR" immich
mkdir -p "$ML_DIR" && chown -R immich:immich "$INSTALL_DIR" mkdir -p "$ML_DIR"
# chown excluding upload dir contents (may be a mount with restricted permissions)
chown immich:immich "$INSTALL_DIR"
find "$INSTALL_DIR" -maxdepth 1 -mindepth 1 ! -name upload -exec chown -R immich:immich {} +
chown immich:immich "$UPLOAD_DIR" 2>/dev/null || true
export VIRTUAL_ENV="${ML_DIR}/ml-venv" export VIRTUAL_ENV="${ML_DIR}/ml-venv"
export UV_HTTP_TIMEOUT=300 export UV_HTTP_TIMEOUT=300
if [[ -f ~/.openvino ]]; then if [[ -f ~/.openvino ]]; then
@@ -469,8 +473,7 @@ User=immich
Group=immich Group=immich
UMask=0077 UMask=0077
WorkingDirectory=${APP_DIR} WorkingDirectory=${APP_DIR}
EnvironmentFile=${INSTALL_DIR}/.env ExecStart=${APP_DIR}/bin/start.sh
ExecStart=/usr/bin/node ${APP_DIR}/dist/main
Restart=on-failure Restart=on-failure
SyslogIdentifier=immich-web SyslogIdentifier=immich-web
StandardOutput=append:/var/log/immich/web.log StandardOutput=append:/var/log/immich/web.log
@@ -500,7 +503,11 @@ StandardError=append:/var/log/immich/ml.log
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
EOF EOF
chown -R immich:immich "$INSTALL_DIR" /var/log/immich chown -R immich:immich /var/log/immich
# chown excluding upload dir contents (may be a mount with restricted permissions)
chown immich:immich "$INSTALL_DIR"
find "$INSTALL_DIR" -maxdepth 1 -mindepth 1 ! -name upload -exec chown -R immich:immich {} +
chown immich:immich "$UPLOAD_DIR" 2>/dev/null || true
systemctl enable -q --now immich-ml.service immich-web.service systemctl enable -q --now immich-ml.service immich-web.service
msg_ok "Modified user, created env file, scripts and services" msg_ok "Modified user, created env file, scripts and services"

View File

@@ -17,7 +17,7 @@ fetch_and_deploy_gh_release "inspircd" "inspircd/inspircd" "binary" "latest" "/o
msg_info "Configuring InspIRCd" msg_info "Configuring InspIRCd"
cat <<EOF >/etc/inspircd/inspircd.conf cat <<EOF >/etc/inspircd/inspircd.conf
<define name="networkDomain" value="community-scripts.com"> <define name="networkDomain" value="community-scripts.org">
<define name="networkName" value="Proxmox VE Helper-Scripts"> <define name="networkName" value="Proxmox VE Helper-Scripts">
<server <server

View File

@@ -55,10 +55,10 @@ $STD expect <<EOF
set timeout -1 set timeout -1
log_user 0 log_user 0
spawn bin/console kimai:user:create admin admin@community-scripts.com ROLE_SUPER_ADMIN spawn bin/console kimai:user:create admin admin@community-scripts.org ROLE_SUPER_ADMIN
expect "Please enter the password:" expect "Please enter the password:"
send "community-scripts.com\r" send "community-scripts.org\r"
expect eof expect eof
EOF EOF

View File

@@ -0,0 +1,72 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/matter-js/python-matter-server
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 \
libuv1 \
libjson-c5 \
libnl-3-200 \
libnl-route-3-200 \
iputils-ping \
iproute2
msg_ok "Installed Dependencies"
UV_PYTHON="3.12" setup_uv
msg_info "Setting up Matter Server"
mkdir -p /opt/matter-server/data/credentials
if [ -L /data ]; then
rm -f /data
fi
if [ ! -e /data ]; then
ln -s /opt/matter-server/data /data
fi
$STD uv venv /opt/matter-server/.venv
MATTER_VERSION=$(get_latest_github_release "matter-js/python-matter-server")
$STD uv pip install --python /opt/matter-server/.venv/bin/python "python-matter-server[server]==${MATTER_VERSION}"
echo "${MATTER_VERSION}" >~/.matter-server
msg_ok "Set up Matter Server"
fetch_and_deploy_gh_release "chip-ota-provider-app" "home-assistant-libs/matter-linux-ota-provider" "singlefile" "latest" "/usr/local/bin" "chip-ota-provider-app-x86-64"
msg_info "Configuring Network"
cat <<EOF >/etc/sysctl.d/99-matter.conf
net.ipv4.igmp_max_memberships=1024
EOF
$STD sysctl -p /etc/sysctl.d/99-matter.conf
msg_ok "Configured Network"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/matter-server.service
[Unit]
Description=Matter Server
After=network.target
[Service]
Type=simple
User=root
ExecStart=/opt/matter-server/.venv/bin/matter-server --storage-path /data --paa-root-cert-dir /data/credentials
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now matter-server
msg_ok "Created Service"
motd_ssh
customize
cleanup_lxc

View File

@@ -33,7 +33,7 @@ $STD yarn config set ignore-engines true
$STD yarn install $STD yarn install
$STD yarn run production $STD yarn run production
$STD php artisan key:generate $STD php artisan key:generate
$STD php artisan setup:production --email=admin@community-scripts.com --password=community-scripts.com --force $STD php artisan setup:production --email=admin@community-scripts.org --password=community-scripts.org --force
chown -R www-data:www-data /opt/monica chown -R www-data:www-data /opt/monica
chmod -R 775 /opt/monica/storage chmod -R 775 /opt/monica/storage
echo "* * * * * root php /opt/monica/artisan schedule:run >> /dev/null 2>&1" >>/etc/crontab echo "* * * * * root php /opt/monica/artisan schedule:run >> /dev/null 2>&1" >>/etc/crontab

View File

@@ -94,7 +94,7 @@ expect "Administrator username" {
send "community-scripts\r" send "community-scripts\r"
} }
expect "Administrator email address" { expect "Administrator email address" {
send "admin@community-scripts.com\r" send "admin@community-scripts.org\r"
} }
expect "Password" { expect "Password" {
send "community-scripts\r" send "community-scripts\r"

View File

@@ -60,7 +60,7 @@ read -r -p "${TAB3}Enter your ACME Email: " ACME_EMAIL_INPUT
yq -i " yq -i "
.services.npmplus.environment |= .services.npmplus.environment |=
(map(select(. != \"TZ=*\" and . != \"ACME_EMAIL=*\" and . != \"INITIAL_ADMIN_EMAIL=*\" and . != \"INITIAL_ADMIN_PASSWORD=*\")) + (map(select(. != \"TZ=*\" and . != \"ACME_EMAIL=*\" and . != \"INITIAL_ADMIN_EMAIL=*\" and . != \"INITIAL_ADMIN_PASSWORD=*\")) +
[\"TZ=$TZ_INPUT\", \"ACME_EMAIL=$ACME_EMAIL_INPUT\", \"INITIAL_ADMIN_EMAIL=admin@local.com\", \"INITIAL_ADMIN_PASSWORD=community-scripts.com\"]) [\"TZ=$TZ_INPUT\", \"ACME_EMAIL=$ACME_EMAIL_INPUT\", \"INITIAL_ADMIN_EMAIL=admin@local.com\", \"INITIAL_ADMIN_PASSWORD=community-scripts.org\"])
" /opt/compose.yaml " /opt/compose.yaml
msg_info "Building and Starting NPMplus (Patience)" msg_info "Building and Starting NPMplus (Patience)"

View File

@@ -22,7 +22,7 @@ msg_ok "Installed Dependencies"
msg_info "Setting up Intel® Repositories" msg_info "Setting up Intel® Repositories"
mkdir -p /usr/share/keyrings mkdir -p /usr/share/keyrings
curl -fsSL https://repositories.intel.com/gpu/intel-graphics.key | gpg --dearmor -o /usr/share/keyrings/intel-graphics.gpg curl -fsSL https://repositories.intel.com/gpu/intel-graphics.key | gpg --dearmor -o /usr/share/keyrings/intel-graphics.gpg 2>/dev/null || true
cat <<EOF >/etc/apt/sources.list.d/intel-gpu.sources cat <<EOF >/etc/apt/sources.list.d/intel-gpu.sources
Types: deb Types: deb
URIs: https://repositories.intel.com/gpu/ubuntu URIs: https://repositories.intel.com/gpu/ubuntu
@@ -31,7 +31,7 @@ Components: client
Architectures: amd64 i386 Architectures: amd64 i386
Signed-By: /usr/share/keyrings/intel-graphics.gpg Signed-By: /usr/share/keyrings/intel-graphics.gpg
EOF EOF
curl -fsSL https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB | gpg --dearmor -o /usr/share/keyrings/oneapi-archive-keyring.gpg curl -fsSL https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB | gpg --dearmor -o /usr/share/keyrings/oneapi-archive-keyring.gpg 2>/dev/null || true
cat <<EOF >/etc/apt/sources.list.d/oneAPI.sources cat <<EOF >/etc/apt/sources.list.d/oneAPI.sources
Types: deb Types: deb
URIs: https://apt.repos.intel.com/oneapi URIs: https://apt.repos.intel.com/oneapi

View File

@@ -99,7 +99,7 @@ PHOTOPRISM_DEBUG='false'
PHOTOPRISM_LOG_LEVEL='info' PHOTOPRISM_LOG_LEVEL='info'
# Site Info # Site Info
PHOTOPRISM_SITE_CAPTION='https://community-scripts.com' PHOTOPRISM_SITE_CAPTION='https://community-scripts.org'
PHOTOPRISM_SITE_DESCRIPTION='' PHOTOPRISM_SITE_DESCRIPTION=''
PHOTOPRISM_SITE_AUTHOR='' PHOTOPRISM_SITE_AUTHOR=''
EOF EOF

View File

@@ -19,7 +19,7 @@ fetch_and_deploy_gh_release "revealjs" "hakimel/reveal.js" "tarball"
msg_info "Configuring ${APPLICATION}" msg_info "Configuring ${APPLICATION}"
cd /opt/revealjs cd /opt/revealjs
$STD npm install $STD npm install
sed -i '25s/localhost/0.0.0.0/g' /opt/revealjs/gulpfile.js sed -i 's/"vite"/"vite --host"/g' package.json
msg_ok "Setup ${APPLICATION}" msg_ok "Setup ${APPLICATION}"
msg_info "Creating Service" msg_info "Creating Service"

View File

@@ -40,7 +40,7 @@ cat <<EOF >/opt/semaphore/config.json
"access_key_encryption": "${SEM_KEY}" "access_key_encryption": "${SEM_KEY}"
} }
EOF EOF
$STD semaphore user add --admin --login admin --email admin@community-scripts.com --name Administrator --password "${SEM_PW}" --config /opt/semaphore/config.json $STD semaphore user add --admin --login admin --email admin@community-scripts.org --name Administrator --password "${SEM_PW}" --config /opt/semaphore/config.json
echo "${SEM_PW}" >~/semaphore.creds echo "${SEM_PW}" >~/semaphore.creds
msg_ok "Setup Semaphore" msg_ok "Setup Semaphore"

View File

@@ -40,6 +40,7 @@ sed \
-e "s|^SPARKY_FITNESS_SERVER_HOST=.*|SPARKY_FITNESS_SERVER_HOST=localhost|" \ -e "s|^SPARKY_FITNESS_SERVER_HOST=.*|SPARKY_FITNESS_SERVER_HOST=localhost|" \
-e "s|^SPARKY_FITNESS_SERVER_PORT=.*|SPARKY_FITNESS_SERVER_PORT=3010|" \ -e "s|^SPARKY_FITNESS_SERVER_PORT=.*|SPARKY_FITNESS_SERVER_PORT=3010|" \
-e "s|^SPARKY_FITNESS_FRONTEND_URL=.*|SPARKY_FITNESS_FRONTEND_URL=http://${LOCAL_IP}:80|" \ -e "s|^SPARKY_FITNESS_FRONTEND_URL=.*|SPARKY_FITNESS_FRONTEND_URL=http://${LOCAL_IP}:80|" \
-e "s|^GARMIN_MICROSERVICE_URL=.*|GARMIN_MICROSERVICE_URL=http://${LOCAL_IP}:8000|" \
-e "s|^SPARKY_FITNESS_API_ENCRYPTION_KEY=.*|SPARKY_FITNESS_API_ENCRYPTION_KEY=$(openssl rand -hex 32)|" \ -e "s|^SPARKY_FITNESS_API_ENCRYPTION_KEY=.*|SPARKY_FITNESS_API_ENCRYPTION_KEY=$(openssl rand -hex 32)|" \
-e "s|^BETTER_AUTH_SECRET=.*|BETTER_AUTH_SECRET=$(openssl rand -hex 32)|" \ -e "s|^BETTER_AUTH_SECRET=.*|BETTER_AUTH_SECRET=$(openssl rand -hex 32)|" \
"/etc/sparkyfitness/.env" "/etc/sparkyfitness/.env"

View File

@@ -47,6 +47,7 @@ $STD yarn install
$STD yarn build $STD yarn build
cat <<EOF >/opt/tandoor/.env cat <<EOF >/opt/tandoor/.env
SECRET_KEY=$SECRET_KEY SECRET_KEY=$SECRET_KEY
ALLOWED_HOSTS=$LOCAL_IP
TZ=Europe/Berlin TZ=Europe/Berlin
DB_ENGINE=django.db.backends.postgresql DB_ENGINE=django.db.backends.postgresql

View File

@@ -14,16 +14,16 @@ network_check
update_os update_os
msg_info "Getting latest version of VictoriaMetrics" msg_info "Getting latest version of VictoriaMetrics"
victoriametrics_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases/latest" |
jq -r '.assets[].name' |
grep -E '^victoria-metrics-linux-amd64-v[0-9.]+\.tar\.gz$')
vmutils_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases/latest" |
jq -r '.assets[].name' |
grep -E '^vmutils-linux-amd64-v[0-9.]+\.tar\.gz$')
msg_ok "Got latest version of VictoriaMetrics"
fetch_and_deploy_gh_release "victoriametrics" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "$victoriametrics_filename" victoriametrics_release=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases" |
fetch_and_deploy_gh_release "vmutils" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "$vmutils_filename" jq -r '.[] | select(.assets[].name | match("^victoria-metrics-linux-amd64-v[0-9.]+.tar.gz$")) | .tag_name' |
head -n 1)
victoriametrics_filename="victoria-metrics-linux-amd64-${victoriametrics_release}.tar.gz"
vmutils_filename="vmutils-linux-amd64-${victoriametrics_release}.tar.gz"
msg_ok "Got version $victoriametrics_release of VictoriaMetrics"
fetch_and_deploy_gh_release "victoriametrics" "VictoriaMetrics/VictoriaMetrics" "prebuild" "$victoriametrics_release" "/opt/victoriametrics" "$victoriametrics_filename"
fetch_and_deploy_gh_release "vmutils" "VictoriaMetrics/VictoriaMetrics" "prebuild" "$victoriametrics_release" "/opt/victoriametrics" "$vmutils_filename"
read -r -p "${TAB3}Would you like to add VictoriaLogs? <y/N> " prompt read -r -p "${TAB3}Would you like to add VictoriaLogs? <y/N> " prompt

91
install/yourls-install.sh Normal file
View File

@@ -0,0 +1,91 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://yourls.org/
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
msg_ok "Installed Dependencies"
setup_mariadb
MARIADB_DB_NAME="yourls" MARIADB_DB_USER="yourls" setup_mariadb_db
PHP_VERSION="8.3" PHP_FPM="YES" PHP_MODULE="mysql,mbstring,gd,xml,curl" setup_php
fetch_and_deploy_gh_release "yourls" "YOURLS/YOURLS" "tarball"
msg_info "Configuring YOURLS"
COOKIEKEY=$(openssl rand -hex 24)
YOURLS_PASS=$(openssl rand -base64 12 | tr -dc 'a-zA-Z0-9' | cut -c1-16)
cat <<EOF >/opt/yourls/user/config.php
<?php
define( 'YOURLS_DB_USER', '${MARIADB_DB_USER}' );
define( 'YOURLS_DB_PASS', '${MARIADB_DB_PASS}' );
define( 'YOURLS_DB_NAME', '${MARIADB_DB_NAME}' );
define( 'YOURLS_DB_HOST', 'localhost' );
define( 'YOURLS_DB_PREFIX', 'yourls_' );
define( 'YOURLS_SITE', 'http://${LOCAL_IP}' );
define( 'YOURLS_LANG', '' );
define( 'YOURLS_UNIQUE_URLS', true );
define( 'YOURLS_PRIVATE', true );
define( 'YOURLS_COOKIEKEY', '${COOKIEKEY}' );
\$yourls_user_passwords = [
'admin' => '${YOURLS_PASS}',
];
define( 'YOURLS_URL_CONVERT', 36 );
define( 'YOURLS_DEBUG', false );
EOF
chown -R www-data:www-data /opt/yourls
msg_ok "Configured YOURLS"
msg_info "Configuring Nginx"
cat <<EOF >/etc/nginx/sites-available/yourls
server {
listen 80 default_server;
server_name _;
root /opt/yourls;
index index.php;
location / {
try_files \$uri \$uri/ /yourls-loader.php\$is_args\$args;
}
location ~ \.php\$ {
try_files \$uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)\$;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
fastcgi_param PATH_INFO \$fastcgi_path_info;
}
location ~* \.(jpg|jpeg|gif|css|png|js|ico|woff|woff2)\$ {
access_log off;
expires max;
}
location ~ /\.ht {
deny all;
}
}
EOF
ln -sf /etc/nginx/sites-available/yourls /etc/nginx/sites-enabled/yourls
rm -f /etc/nginx/sites-enabled/default
$STD nginx -t
systemctl enable -q --now nginx
systemctl reload nginx
msg_ok "Configured Nginx"
motd_ssh
customize
cleanup_lxc

View File

@@ -135,10 +135,34 @@ network_check() {
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
} }
# This function updates the Container OS by running apt-get update and upgrade # This function updates the Container OS by running apk upgrade with mirror fallback
update_os() { update_os() {
msg_info "Updating Container OS" msg_info "Updating Container OS"
$STD apk -U upgrade if ! $STD apk -U upgrade; then
msg_warn "apk update failed (dl-cdn.alpinelinux.org), trying alternate mirrors..."
local alpine_mirrors="mirror.init7.net ftp.halifax.rwth-aachen.de mirrors.edge.kernel.org alpine.mirror.wearetriple.com mirror.leaseweb.com uk.alpinelinux.org dl-2.alpinelinux.org dl-4.alpinelinux.org"
local apk_ok=false
for m in $(printf '%s\n' $alpine_mirrors | shuf); do
if timeout 2 bash -c "echo >/dev/tcp/$m/80" 2>/dev/null; then
msg_custom "${INFO}" "${YW}" "Attempting mirror: ${m}"
cat <<EOF >/etc/apk/repositories
http://$m/alpine/latest-stable/main
http://$m/alpine/latest-stable/community
EOF
if $STD apk -U upgrade; then
msg_ok "CDN set to ${m}: tests passed"
apk_ok=true
break
else
msg_warn "Mirror ${m} failed"
fi
fi
done
if [[ "$apk_ok" != true ]]; then
msg_error "All Alpine mirrors failed. Check network or try again later."
exit 1
fi
fi
local tools_content local tools_content
tools_content=$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) || { tools_content=$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) || {
msg_error "Failed to download tools.func" msg_error "Failed to download tools.func"

View File

@@ -20,7 +20,7 @@ need_tool() {
msg_info "Installing tools: $*" msg_info "Installing tools: $*"
apk add --no-cache "$@" >/dev/null 2>&1 || { apk add --no-cache "$@" >/dev/null 2>&1 || {
msg_error "apk add failed for: $*" msg_error "apk add failed for: $*"
return 1 return 100
} }
msg_ok "Tools ready: $*" msg_ok "Tools ready: $*"
fi fi
@@ -52,17 +52,17 @@ ensure_usr_local_bin_persist() {
download_with_progress() { download_with_progress() {
# $1 url, $2 dest # $1 url, $2 dest
local url="$1" out="$2" cl local url="$1" out="$2" cl
need_tool curl pv || return 1 need_tool curl pv || return 127
cl=$(curl -fsSLI "$url" 2>/dev/null | awk 'tolower($0) ~ /^content-length:/ {print $2}' | tr -d '\r') cl=$(curl -fsSLI "$url" 2>/dev/null | awk 'tolower($0) ~ /^content-length:/ {print $2}' | tr -d '\r')
if [ -n "$cl" ]; then if [ -n "$cl" ]; then
curl -fsSL "$url" | pv -s "$cl" >"$out" || { curl -fsSL "$url" | pv -s "$cl" >"$out" || {
msg_error "Download failed: $url" msg_error "Download failed: $url"
return 1 return 250
} }
else else
curl -fL# -o "$out" "$url" || { curl -fL# -o "$out" "$url" || {
msg_error "Download failed: $url" msg_error "Download failed: $url"
return 1 return 250
} }
fi fi
} }
@@ -82,14 +82,14 @@ check_for_gh_release() {
net_resolves api.github.com || { net_resolves api.github.com || {
msg_error "DNS/network error: api.github.com" msg_error "DNS/network error: api.github.com"
return 1 return 6
} }
need_tool curl jq || return 1 need_tool curl jq || return 127
tag=$(curl -fsSL "https://api.github.com/repos/${source}/releases/latest" | jq -r '.tag_name // empty') tag=$(curl -fsSL "https://api.github.com/repos/${source}/releases/latest" | jq -r '.tag_name // empty')
[ -z "$tag" ] && { [ -z "$tag" ] && {
msg_error "Unable to fetch latest tag for $app" msg_error "Unable to fetch latest tag for $app"
return 1 return 22
} }
release="${tag#v}" release="${tag#v}"
@@ -133,12 +133,12 @@ fetch_and_deploy_gh() {
net_resolves api.github.com || { net_resolves api.github.com || {
msg_error "DNS/network error" msg_error "DNS/network error"
return 1 return 6
} }
need_tool curl jq tar || return 1 need_tool curl jq tar || return 127
[ "$mode" = "prebuild" ] || [ "$mode" = "singlefile" ] && need_tool unzip >/dev/null 2>&1 || true [ "$mode" = "prebuild" ] || [ "$mode" = "singlefile" ] && need_tool unzip >/dev/null 2>&1 || true
tmpd="$(mktemp -d)" || return 1 tmpd="$(mktemp -d)" || return 252
mkdir -p "$target" mkdir -p "$target"
# Release JSON # Release JSON
@@ -146,13 +146,13 @@ fetch_and_deploy_gh() {
json="$(curl -fsSL "https://api.github.com/repos/$repo/releases/latest")" || { json="$(curl -fsSL "https://api.github.com/repos/$repo/releases/latest")" || {
msg_error "GitHub API failed" msg_error "GitHub API failed"
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 22
} }
else else
json="$(curl -fsSL "https://api.github.com/repos/$repo/releases/tags/$version")" || { json="$(curl -fsSL "https://api.github.com/repos/$repo/releases/tags/$version")" || {
msg_error "GitHub API failed" msg_error "GitHub API failed"
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 22
} }
fi fi
@@ -163,7 +163,7 @@ fetch_and_deploy_gh() {
[ -z "$version" ] && { [ -z "$version" ] && {
msg_error "No tag in release json" msg_error "No tag in release json"
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 65
} }
case "$mode" in case "$mode" in
@@ -173,26 +173,26 @@ fetch_and_deploy_gh() {
filename="${app_lc}-${version}.tar.gz" filename="${app_lc}-${version}.tar.gz"
download_with_progress "$url" "$tmpd/$filename" || { download_with_progress "$url" "$tmpd/$filename" || {
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 250
} }
tar -xzf "$tmpd/$filename" -C "$tmpd" || { tar -xzf "$tmpd/$filename" -C "$tmpd" || {
msg_error "tar extract failed" msg_error "tar extract failed"
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 251
} }
unpack="$(find "$tmpd" -mindepth 1 -maxdepth 1 -type d | head -n1)" unpack="$(find "$tmpd" -mindepth 1 -maxdepth 1 -type d | head -n1)"
# copy content of unpack to target # copy content of unpack to target
(cd "$unpack" && tar -cf - .) | (cd "$target" && tar -xf -) || { (cd "$unpack" && tar -cf - .) | (cd "$target" && tar -xf -) || {
msg_error "copy failed" msg_error "copy failed"
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 252
} }
;; ;;
prebuild) prebuild)
[ -n "$pattern" ] || { [ -n "$pattern" ] || {
msg_error "prebuild requires asset pattern" msg_error "prebuild requires asset pattern"
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 65
} }
url="$(printf '%s' "$json" | jq -r '.assets[].browser_download_url' | awk -v p="$pattern" ' url="$(printf '%s' "$json" | jq -r '.assets[].browser_download_url' | awk -v p="$pattern" '
BEGIN{IGNORECASE=1} BEGIN{IGNORECASE=1}
@@ -201,19 +201,19 @@ fetch_and_deploy_gh() {
[ -z "$url" ] && { [ -z "$url" ] && {
msg_error "asset not found for pattern: $pattern" msg_error "asset not found for pattern: $pattern"
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 250
} }
filename="${url##*/}" filename="${url##*/}"
download_with_progress "$url" "$tmpd/$filename" || { download_with_progress "$url" "$tmpd/$filename" || {
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 250
} }
# unpack archive (Zip or tarball) # unpack archive (Zip or tarball)
case "$filename" in case "$filename" in
*.zip) *.zip)
need_tool unzip || { need_tool unzip || {
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 127
} }
mkdir -p "$tmpd/unp" mkdir -p "$tmpd/unp"
unzip -q "$tmpd/$filename" -d "$tmpd/unp" unzip -q "$tmpd/$filename" -d "$tmpd/unp"
@@ -225,7 +225,7 @@ fetch_and_deploy_gh() {
*) *)
msg_error "unsupported archive: $filename" msg_error "unsupported archive: $filename"
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 251
;; ;;
esac esac
# top-level folder strippen # top-level folder strippen
@@ -234,13 +234,13 @@ fetch_and_deploy_gh() {
(cd "$unpack" && tar -cf - .) | (cd "$target" && tar -xf -) || { (cd "$unpack" && tar -cf - .) | (cd "$target" && tar -xf -) || {
msg_error "copy failed" msg_error "copy failed"
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 252
} }
else else
(cd "$tmpd/unp" && tar -cf - .) | (cd "$target" && tar -xf -) || { (cd "$tmpd/unp" && tar -cf - .) | (cd "$target" && tar -xf -) || {
msg_error "copy failed" msg_error "copy failed"
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 252
} }
fi fi
;; ;;
@@ -248,7 +248,7 @@ fetch_and_deploy_gh() {
[ -n "$pattern" ] || { [ -n "$pattern" ] || {
msg_error "singlefile requires asset pattern" msg_error "singlefile requires asset pattern"
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 65
} }
url="$(printf '%s' "$json" | jq -r '.assets[].browser_download_url' | awk -v p="$pattern" ' url="$(printf '%s' "$json" | jq -r '.assets[].browser_download_url' | awk -v p="$pattern" '
BEGIN{IGNORECASE=1} BEGIN{IGNORECASE=1}
@@ -257,19 +257,19 @@ fetch_and_deploy_gh() {
[ -z "$url" ] && { [ -z "$url" ] && {
msg_error "asset not found for pattern: $pattern" msg_error "asset not found for pattern: $pattern"
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 250
} }
filename="${url##*/}" filename="${url##*/}"
download_with_progress "$url" "$target/$app" || { download_with_progress "$url" "$target/$app" || {
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 250
} }
chmod +x "$target/$app" chmod +x "$target/$app"
;; ;;
*) *)
msg_error "Unknown mode: $mode" msg_error "Unknown mode: $mode"
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 65
;; ;;
esac esac
@@ -291,20 +291,20 @@ setup_yq() {
return 0 return 0
fi fi
need_tool curl || return 1 need_tool curl || return 127
local arch bin url tmp local arch bin url tmp
case "$(uname -m)" in case "$(uname -m)" in
x86_64) arch="amd64" ;; x86_64) arch="amd64" ;;
aarch64) arch="arm64" ;; aarch64) arch="arm64" ;;
*) *)
msg_error "Unsupported arch for yq: $(uname -m)" msg_error "Unsupported arch for yq: $(uname -m)"
return 1 return 238
;; ;;
esac esac
url="https://github.com/mikefarah/yq/releases/latest/download/yq_linux_${arch}" url="https://github.com/mikefarah/yq/releases/latest/download/yq_linux_${arch}"
tmp="$(mktemp)" tmp="$(mktemp)"
download_with_progress "$url" "$tmp" || return 1 download_with_progress "$url" "$tmp" || return 250
install -m 0755 "$tmp" /usr/local/bin/yq /usr/bin/install -m 0755 "$tmp" /usr/local/bin/yq
rm -f "$tmp" rm -f "$tmp"
msg_ok "Setup yq ($(yq --version 2>/dev/null))" msg_ok "Setup yq ($(yq --version 2>/dev/null))"
} }
@@ -313,13 +313,13 @@ setup_yq() {
# Adminer Alpine # Adminer Alpine
# ------------------------------ # ------------------------------
setup_adminer() { setup_adminer() {
need_tool curl || return 1 need_tool curl || return 127
msg_info "Setup Adminer (Alpine)" msg_info "Setup Adminer (Alpine)"
mkdir -p /var/www/localhost/htdocs/adminer mkdir -p /var/www/localhost/htdocs/adminer
curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \ curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \
-o /var/www/localhost/htdocs/adminer/index.php || { -o /var/www/localhost/htdocs/adminer/index.php || {
msg_error "Adminer download failed" msg_error "Adminer download failed"
return 1 return 250
} }
msg_ok "Adminer at /adminer (served by your webserver)" msg_ok "Adminer at /adminer (served by your webserver)"
} }
@@ -329,7 +329,7 @@ setup_adminer() {
# optional: PYTHON_VERSION="3.12" # optional: PYTHON_VERSION="3.12"
# ------------------------------ # ------------------------------
setup_uv() { setup_uv() {
need_tool curl tar || return 1 need_tool curl tar || return 127
local UV_BIN="/usr/local/bin/uv" local UV_BIN="/usr/local/bin/uv"
local arch tarball url tmpd ver installed local arch tarball url tmpd ver installed
@@ -338,7 +338,7 @@ setup_uv() {
aarch64) arch="aarch64-unknown-linux-musl" ;; aarch64) arch="aarch64-unknown-linux-musl" ;;
*) *)
msg_error "Unsupported arch for uv: $(uname -m)" msg_error "Unsupported arch for uv: $(uname -m)"
return 1 return 238
;; ;;
esac esac
@@ -346,7 +346,7 @@ setup_uv() {
ver="${ver#v}" ver="${ver#v}"
[ -z "$ver" ] && { [ -z "$ver" ] && {
msg_error "uv: cannot determine latest version" msg_error "uv: cannot determine latest version"
return 1 return 250
} }
if has "$UV_BIN"; then if has "$UV_BIN"; then
@@ -360,29 +360,29 @@ setup_uv() {
msg_info "Setup uv $ver" msg_info "Setup uv $ver"
fi fi
tmpd="$(mktemp -d)" || return 1 tmpd="$(mktemp -d)" || return 252
tarball="uv-${arch}.tar.gz" tarball="uv-${arch}.tar.gz"
url="https://github.com/astral-sh/uv/releases/download/v${ver}/${tarball}" url="https://github.com/astral-sh/uv/releases/download/v${ver}/${tarball}"
download_with_progress "$url" "$tmpd/uv.tar.gz" || { download_with_progress "$url" "$tmpd/uv.tar.gz" || {
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 250
} }
tar -xzf "$tmpd/uv.tar.gz" -C "$tmpd" || { tar -xzf "$tmpd/uv.tar.gz" -C "$tmpd" || {
msg_error "uv: extract failed" msg_error "uv: extract failed"
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 251
} }
# tar contains ./uv # tar contains ./uv
if [ -x "$tmpd/uv" ]; then if [ -x "$tmpd/uv" ]; then
install -m 0755 "$tmpd/uv" "$UV_BIN" /usr/bin/install -m 0755 "$tmpd/uv" "$UV_BIN"
else else
# fallback: in subfolder # fallback: in subfolder
install -m 0755 "$tmpd"/*/uv "$UV_BIN" 2>/dev/null || { /usr/bin/install -m 0755 "$tmpd"/*/uv "$UV_BIN" 2>/dev/null || {
msg_error "uv binary not found in tar" msg_error "uv binary not found in tar"
rm -rf "$tmpd" rm -rf "$tmpd"
return 1 return 252
} }
fi fi
rm -rf "$tmpd" rm -rf "$tmpd"
@@ -395,13 +395,13 @@ setup_uv() {
$0 ~ "^cpython-"maj"\\." { print $0 }' | awk -F- '{print $2}' | sort -V | tail -n1)" $0 ~ "^cpython-"maj"\\." { print $0 }' | awk -F- '{print $2}' | sort -V | tail -n1)"
[ -z "$match" ] && { [ -z "$match" ] && {
msg_error "No matching Python for $PYTHON_VERSION" msg_error "No matching Python for $PYTHON_VERSION"
return 1 return 250
} }
if ! uv python list | grep -q "cpython-${match}-linux"; then if ! uv python list | grep -q "cpython-${match}-linux"; then
msg_info "Installing Python $match via uv" msg_info "Installing Python $match via uv"
uv python install "$match" || { uv python install "$match" || {
msg_error "uv python install failed" msg_error "uv python install failed"
return 1 return 150
} }
msg_ok "Python $match installed (uv)" msg_ok "Python $match installed (uv)"
fi fi
@@ -421,7 +421,7 @@ setup_java() {
msg_info "Setup Java (OpenJDK $JAVA_VERSION)" msg_info "Setup Java (OpenJDK $JAVA_VERSION)"
apk add --no-cache "$pkg" >/dev/null 2>&1 || { apk add --no-cache "$pkg" >/dev/null 2>&1 || {
msg_error "apk add $pkg failed" msg_error "apk add $pkg failed"
return 1 return 100
} }
# set JAVA_HOME # set JAVA_HOME
local prof="/etc/profile.d/20-java.sh" local prof="/etc/profile.d/20-java.sh"
@@ -441,32 +441,32 @@ setup_go() {
msg_info "Setup Go (apk)" msg_info "Setup Go (apk)"
apk add --no-cache go >/dev/null 2>&1 || { apk add --no-cache go >/dev/null 2>&1 || {
msg_error "apk add go failed" msg_error "apk add go failed"
return 1 return 100
} }
msg_ok "Go ready: $(go version 2>/dev/null)" msg_ok "Go ready: $(go version 2>/dev/null)"
return 0 return 0
fi fi
need_tool curl tar || return 1 need_tool curl tar || return 127
local ARCH TARBALL URL TMP local ARCH TARBALL URL TMP
case "$(uname -m)" in case "$(uname -m)" in
x86_64) ARCH="amd64" ;; x86_64) ARCH="amd64" ;;
aarch64) ARCH="arm64" ;; aarch64) ARCH="arm64" ;;
*) *)
msg_error "Unsupported arch for Go: $(uname -m)" msg_error "Unsupported arch for Go: $(uname -m)"
return 1 return 238
;; ;;
esac esac
TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz"
URL="https://go.dev/dl/${TARBALL}" URL="https://go.dev/dl/${TARBALL}"
msg_info "Setup Go $GO_VERSION (tarball)" msg_info "Setup Go $GO_VERSION (tarball)"
TMP="$(mktemp)" TMP="$(mktemp)"
download_with_progress "$URL" "$TMP" || return 1 download_with_progress "$URL" "$TMP" || return 250
rm -rf /usr/local/go rm -rf /usr/local/go
tar -C /usr/local -xzf "$TMP" || { tar -C /usr/local -xzf "$TMP" || {
msg_error "extract go failed" msg_error "extract go failed"
rm -f "$TMP" rm -f "$TMP"
return 1 return 251
} }
rm -f "$TMP" rm -f "$TMP"
ln -sf /usr/local/go/bin/go /usr/local/bin/go ln -sf /usr/local/go/bin/go /usr/local/bin/go
@@ -488,7 +488,7 @@ setup_composer() {
# Fallback to generic php if 83 not available # Fallback to generic php if 83 not available
apk add --no-cache php-cli php-openssl php-phar php-iconv >/dev/null 2>&1 || { apk add --no-cache php-cli php-openssl php-phar php-iconv >/dev/null 2>&1 || {
msg_error "Failed to install php-cli for composer" msg_error "Failed to install php-cli for composer"
return 1 return 100
} }
} }
msg_ok "PHP CLI ready: $(php -v | head -n1)" msg_ok "PHP CLI ready: $(php -v | head -n1)"
@@ -500,14 +500,14 @@ setup_composer() {
msg_info "Setup Composer" msg_info "Setup Composer"
fi fi
need_tool curl || return 1 need_tool curl || return 127
curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php || { curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php || {
msg_error "composer installer download failed" msg_error "composer installer download failed"
return 1 return 250
} }
php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer >/dev/null 2>&1 || { php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer >/dev/null 2>&1 || {
msg_error "composer install failed" msg_error "composer install failed"
return 1 return 150
} }
rm -f /tmp/composer-setup.php rm -f /tmp/composer-setup.php
ensure_usr_local_bin_persist ensure_usr_local_bin_persist

View File

@@ -504,7 +504,7 @@ detect_gpu() {
GPU_PASSTHROUGH="unknown" GPU_PASSTHROUGH="unknown"
local gpu_line local gpu_line
gpu_line=$(lspci 2>/dev/null | grep -iE "VGA|3D|Display" | head -1) gpu_line=$(lspci 2>/dev/null | grep -iE "VGA|3D|Display" | head -1 || true)
if [[ -n "$gpu_line" ]]; then if [[ -n "$gpu_line" ]]; then
# Extract model: everything after the colon, clean up # Extract model: everything after the colon, clean up
@@ -543,7 +543,7 @@ detect_cpu() {
if [[ -f /proc/cpuinfo ]]; then if [[ -f /proc/cpuinfo ]]; then
local vendor_id local vendor_id
vendor_id=$(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | tr -d ' ') vendor_id=$(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | tr -d ' ' || true)
case "$vendor_id" in case "$vendor_id" in
GenuineIntel) CPU_VENDOR="intel" ;; GenuineIntel) CPU_VENDOR="intel" ;;
@@ -557,7 +557,7 @@ detect_cpu() {
esac esac
# Extract model name and clean it up # Extract model name and clean it up
CPU_MODEL=$(grep -m1 "model name" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | sed 's/^ *//' | sed 's/(R)//g' | sed 's/(TM)//g' | sed 's/ */ /g' | cut -c1-64) CPU_MODEL=$(grep -m1 "model name" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | sed 's/^ *//' | sed 's/(R)//g' | sed 's/(TM)//g' | sed 's/ */ /g' | cut -c1-64 || true)
fi fi
export CPU_VENDOR CPU_MODEL export CPU_VENDOR CPU_MODEL
@@ -1347,8 +1347,8 @@ post_addon_to_api() {
# Detect OS info # Detect OS info
local os_type="" os_version="" local os_type="" os_version=""
if [[ -f /etc/os-release ]]; then if [[ -f /etc/os-release ]]; then
os_type=$(grep "^ID=" /etc/os-release | cut -d= -f2 | tr -d '"') os_type=$(grep "^ID=" /etc/os-release | cut -d= -f2 | tr -d '"' || true)
os_version=$(grep "^VERSION_ID=" /etc/os-release | cut -d= -f2 | tr -d '"') os_version=$(grep "^VERSION_ID=" /etc/os-release | cut -d= -f2 | tr -d '"' || true)
fi fi
local JSON_PAYLOAD local JSON_PAYLOAD

View File

@@ -173,10 +173,10 @@ get_current_ip() {
# Check for Debian/Ubuntu (uses hostname -I) # Check for Debian/Ubuntu (uses hostname -I)
if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then
# Try IPv4 first # Try IPv4 first
CURRENT_IP=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n1) CURRENT_IP=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n1 || true)
# Fallback to IPv6 if no IPv4 # Fallback to IPv6 if no IPv4
if [[ -z "$CURRENT_IP" ]]; then if [[ -z "$CURRENT_IP" ]]; then
CURRENT_IP=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E ':' | head -n1) CURRENT_IP=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E ':' | head -n1 || true)
fi fi
# Check for Alpine (uses ip command) # Check for Alpine (uses ip command)
elif grep -q 'ID=alpine' /etc/os-release; then elif grep -q 'ID=alpine' /etc/os-release; then
@@ -267,12 +267,12 @@ install_ssh_keys_into_ct() {
msg_info "Installing selected SSH keys into CT ${CTID}" msg_info "Installing selected SSH keys into CT ${CTID}"
pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' || { pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' || {
msg_error "prepare /root/.ssh failed" msg_error "prepare /root/.ssh failed"
return 1 return 252
} }
pct push "$CTID" "$SSH_KEYS_FILE" /root/.ssh/authorized_keys >/dev/null 2>&1 || pct push "$CTID" "$SSH_KEYS_FILE" /root/.ssh/authorized_keys >/dev/null 2>&1 ||
pct exec "$CTID" -- sh -c "cat > /root/.ssh/authorized_keys" <"$SSH_KEYS_FILE" || { pct exec "$CTID" -- sh -c "cat > /root/.ssh/authorized_keys" <"$SSH_KEYS_FILE" || {
msg_error "write authorized_keys failed" msg_error "write authorized_keys failed"
return 1 return 252
} }
pct exec "$CTID" -- sh -c 'chmod 600 /root/.ssh/authorized_keys' || true pct exec "$CTID" -- sh -c 'chmod 600 /root/.ssh/authorized_keys' || true
msg_ok "Installed SSH keys into CT ${CTID}" msg_ok "Installed SSH keys into CT ${CTID}"
@@ -839,7 +839,7 @@ choose_and_set_storage_for_file() {
template) key="var_template_storage" ;; template) key="var_template_storage" ;;
*) *)
msg_error "Unknown storage class: $class" msg_error "Unknown storage class: $class"
return 1 return 65
;; ;;
esac esac
@@ -862,7 +862,7 @@ choose_and_set_storage_for_file() {
fi fi
else else
# If the current value is preselectable, we could show it, but per your requirement we always offer selection # If the current value is preselectable, we could show it, but per your requirement we always offer selection
select_storage "$class" || return 1 select_storage "$class" || return 150
fi fi
_write_storage_to_vars "$vf" "$key" "$STORAGE_RESULT" _write_storage_to_vars "$vf" "$key" "$STORAGE_RESULT"
@@ -1264,7 +1264,7 @@ default_var_settings() {
return 0 return 0
} }
done done
return 1 return 252
} }
# Allow override of storages via env (for non-interactive use cases) # Allow override of storages via env (for non-interactive use cases)
[ -n "${var_template_storage:-}" ] && TEMPLATE_STORAGE="$var_template_storage" [ -n "${var_template_storage:-}" ] && TEMPLATE_STORAGE="$var_template_storage"
@@ -1357,7 +1357,7 @@ EOF
local dv local dv
dv="$(_find_default_vars)" || { dv="$(_find_default_vars)" || {
msg_error "default.vars not found after ensure step" msg_error "default.vars not found after ensure step"
return 1 return 252
} }
load_vars_file "$dv" load_vars_file "$dv"
@@ -1642,7 +1642,7 @@ maybe_offer_save_app_defaults() {
if whiptail --backtitle "Proxmox VE Helper Scripts" \ if whiptail --backtitle "Proxmox VE Helper Scripts" \
--yesno "Save these advanced settings as defaults for ${APP}?\n\nThis will create:\n${app_vars_path}" 12 72; then --yesno "Save these advanced settings as defaults for ${APP}?\n\nThis will create:\n${app_vars_path}" 12 72; then
mkdir -p "$(dirname "$app_vars_path")" mkdir -p "$(dirname "$app_vars_path")"
install -m 0644 "$new_tmp" "$app_vars_path" /usr/bin/install -m 0644 "$new_tmp" "$app_vars_path"
msg_ok "Saved app defaults: ${app_vars_path}" msg_ok "Saved app defaults: ${app_vars_path}"
fi fi
rm -f "$new_tmp" "$diff_tmp" rm -f "$new_tmp" "$diff_tmp"
@@ -1676,7 +1676,7 @@ maybe_offer_save_app_defaults() {
case "$sel" in case "$sel" in
"Update Defaults") "Update Defaults")
install -m 0644 "$new_tmp" "$app_vars_path" /usr/bin/install -m 0644 "$new_tmp" "$app_vars_path"
msg_ok "Updated app defaults: ${app_vars_path}" msg_ok "Updated app defaults: ${app_vars_path}"
break break
;; ;;
@@ -1704,8 +1704,8 @@ ensure_storage_selection_for_vars_file() {
# Read stored values (if any) # Read stored values (if any)
local tpl ct local tpl ct
tpl=$(grep -E '^var_template_storage=' "$vf" | cut -d= -f2-) tpl=$(grep -E '^var_template_storage=' "$vf" | cut -d= -f2- || true)
ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2-) ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2- || true)
if [[ -n "$tpl" && -n "$ct" ]]; then if [[ -n "$tpl" && -n "$ct" ]]; then
TEMPLATE_STORAGE="$tpl" TEMPLATE_STORAGE="$tpl"
@@ -1840,7 +1840,7 @@ advanced_settings() {
if [[ -n "$BRIDGES" ]]; then if [[ -n "$BRIDGES" ]]; then
while IFS= read -r bridge; do while IFS= read -r bridge; do
if [[ -n "$bridge" ]]; then if [[ -n "$bridge" ]]; then
local description=$(grep -A 10 "iface $bridge" /etc/network/interfaces 2>/dev/null | grep '^#' | head -n1 | sed 's/^#\s*//;s/^[- ]*//') local description=$(grep -A 10 "iface $bridge" /etc/network/interfaces 2>/dev/null | grep '^#' | head -n1 | sed 's/^#\s*//;s/^[- ]*//' || true)
BRIDGE_MENU_OPTIONS+=("$bridge" "${description:- }") BRIDGE_MENU_OPTIONS+=("$bridge" "${description:- }")
fi fi
done <<<"$BRIDGES" done <<<"$BRIDGES"
@@ -3322,7 +3322,7 @@ configure_ssh_settings() {
tag="${tag%\"}" tag="${tag%\"}"
tag="${tag#\"}" tag="${tag#\"}"
local line local line
line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2- || true)
[[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE"
done done
;; ;;
@@ -3349,7 +3349,7 @@ configure_ssh_settings() {
tag="${tag%\"}" tag="${tag%\"}"
tag="${tag#\"}" tag="${tag#\"}"
local line local line
line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2- || true)
[[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE"
done done
else else
@@ -3530,6 +3530,7 @@ build_container() {
# Gateway # Gateway
if [[ -n "$GATE" ]]; then if [[ -n "$GATE" ]]; then
case "$GATE" in case "$GATE" in
,gw=) ;;
,gw=*) NET_STRING+="$GATE" ;; ,gw=*) NET_STRING+="$GATE" ;;
*) NET_STRING+=",gw=$GATE" ;; *) NET_STRING+=",gw=$GATE" ;;
esac esac
@@ -4050,7 +4051,7 @@ EOF
# Fix Debian 13 LXC template bug where / is owned by nobody:nogroup # Fix Debian 13 LXC template bug where / is owned by nobody:nogroup
# This must be done from the host as unprivileged containers cannot chown / # This must be done from the host as unprivileged containers cannot chown /
local rootfs local rootfs
rootfs=$(pct config "$CTID" | grep -E '^rootfs:' | sed 's/rootfs: //' | cut -d',' -f1) rootfs=$(pct config "$CTID" | grep -E '^rootfs:' | sed 's/rootfs: //' | cut -d',' -f1 || true)
if [[ -n "$rootfs" ]]; then if [[ -n "$rootfs" ]]; then
local mount_point="/var/lib/lxc/${CTID}/rootfs" local mount_point="/var/lib/lxc/${CTID}/rootfs"
if [[ -d "$mount_point" ]] && [[ "$(stat -c '%U' "$mount_point")" != "root" ]]; then if [[ -d "$mount_point" ]] && [[ "$(stat -c '%U' "$mount_point")" != "root" ]]; then
@@ -4088,8 +4089,31 @@ https://dl-cdn.alpinelinux.org/alpine/latest-stable/main
https://dl-cdn.alpinelinux.org/alpine/latest-stable/community https://dl-cdn.alpinelinux.org/alpine/latest-stable/community
EOF' EOF'
pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq" >>"$BUILD_LOG" 2>&1 || { pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq" >>"$BUILD_LOG" 2>&1 || {
msg_error "Failed to install base packages in Alpine container" msg_warn "apk install failed (dl-cdn.alpinelinux.org), trying alternate mirrors..."
install_exit_code=1 local alpine_exit=0
pct exec "$CTID" -- ash -c '
ALPINE_MIRRORS="mirror.init7.net ftp.halifax.rwth-aachen.de mirrors.edge.kernel.org alpine.mirror.wearetriple.com mirror.leaseweb.com uk.alpinelinux.org dl-2.alpinelinux.org dl-4.alpinelinux.org"
for m in $(printf "%s\n" $ALPINE_MIRRORS | shuf); do
if wget -q --spider --timeout=2 "http://$m/alpine/latest-stable/main/" 2>/dev/null; then
echo " Attempting mirror: $m"
cat <<EOF >/etc/apk/repositories
http://$m/alpine/latest-stable/main
http://$m/alpine/latest-stable/community
EOF
if apk update >/dev/null 2>&1 && apk add bash newt curl openssh nano mc ncurses jq >/dev/null 2>&1; then
echo " CDN set to $m: tests passed"
exit 0
else
echo " Mirror $m failed"
fi
fi
done
exit 2
' && alpine_exit=0 || alpine_exit=$?
if [[ $alpine_exit -ne 0 ]]; then
msg_error "Failed to install base packages in Alpine container"
install_exit_code=1
fi
} }
else else
sleep 3 sleep 3
@@ -4115,9 +4139,140 @@ EOF'
msg_warn "Skipping timezone setup zone '$tz' not found in container" msg_warn "Skipping timezone setup zone '$tz' not found in container"
fi fi
# Detect broken DNS resolver (e.g. Tailscale MagicDNS) and inject public DNS
if ! pct exec "$CTID" -- bash -c "getent hosts deb.debian.org >/dev/null 2>&1 && getent hosts archive.ubuntu.com >/dev/null 2>&1"; then
msg_warn "APT repository DNS resolution failed in container, injecting public DNS servers"
pct exec "$CTID" -- bash -c "echo -e 'nameserver 8.8.8.8\nnameserver 1.1.1.1' >/etc/resolv.conf"
fi
pct exec "$CTID" -- bash -c "apt-get update 2>&1 && apt-get install -y sudo curl mc gnupg2 jq 2>&1" >>"$BUILD_LOG" 2>&1 || { pct exec "$CTID" -- bash -c "apt-get update 2>&1 && apt-get install -y sudo curl mc gnupg2 jq 2>&1" >>"$BUILD_LOG" 2>&1 || {
msg_error "apt-get base packages installation failed" local failed_mirror
install_exit_code=1 failed_mirror=$(pct exec "$CTID" -- bash -c "grep -m1 -oP '(?<=URIs: https?://)[^/]+' /etc/apt/sources.list.d/debian.sources 2>/dev/null || grep -m1 -oP '(?<=deb https?://)[^/]+' /etc/apt/sources.list 2>/dev/null" 2>/dev/null || echo "unknown")
msg_warn "apt-get update failed (${failed_mirror}), trying alternate mirrors..."
local mirror_exit=0
pct exec "$CTID" -- bash -c '
APT_BASE="sudo curl mc gnupg2 jq"
DISTRO=$(. /etc/os-release 2>/dev/null && echo "$ID" || echo "debian")
if [ "$DISTRO" = "ubuntu" ]; then
EU_MIRRORS="de.archive.ubuntu.com fr.archive.ubuntu.com se.archive.ubuntu.com nl.archive.ubuntu.com it.archive.ubuntu.com ch.archive.ubuntu.com mirrors.xtom.de"
US_MIRRORS="us.archive.ubuntu.com archive.ubuntu.com mirrors.edge.kernel.org mirror.csclub.uwaterloo.ca mirrors.ocf.berkeley.edu mirror.math.princeton.edu"
AP_MIRRORS="au.archive.ubuntu.com jp.archive.ubuntu.com kr.archive.ubuntu.com tw.archive.ubuntu.com mirror.aarnet.edu.au"
else
EU_MIRRORS="ftp.de.debian.org ftp.fr.debian.org ftp.nl.debian.org ftp.uk.debian.org ftp.ch.debian.org ftp.se.debian.org ftp.it.debian.org ftp.fau.de ftp.halifax.rwth-aachen.de debian.mirror.lrz.de mirror.init7.net debian.ethz.ch mirrors.dotsrc.org debian.mirrors.ovh.net"
US_MIRRORS="ftp.us.debian.org ftp.ca.debian.org debian.csail.mit.edu mirrors.ocf.berkeley.edu mirrors.wikimedia.org debian.osuosl.org mirror.cogentco.com"
AP_MIRRORS="ftp.au.debian.org ftp.jp.debian.org ftp.tw.debian.org ftp.kr.debian.org ftp.hk.debian.org ftp.sg.debian.org mirror.aarnet.edu.au mirror.nitc.ac.in"
fi
TZ=$(cat /etc/timezone 2>/dev/null || echo "UTC")
case "$TZ" in
Europe/*|Arctic/*) REGIONAL="$EU_MIRRORS"; OTHERS="$US_MIRRORS $AP_MIRRORS" ;;
America/*) REGIONAL="$US_MIRRORS"; OTHERS="$EU_MIRRORS $AP_MIRRORS" ;;
Asia/*|Australia/*|Pacific/*) REGIONAL="$AP_MIRRORS"; OTHERS="$EU_MIRRORS $US_MIRRORS" ;;
*) REGIONAL=""; OTHERS="$EU_MIRRORS $US_MIRRORS $AP_MIRRORS" ;;
esac
echo "Acquire::By-Hash \"no\";" >/etc/apt/apt.conf.d/99no-by-hash
try_mirrors() {
for src in /etc/apt/sources.list.d/debian.sources /etc/apt/sources.list; do
[ -f "$src" ] && sed -i "s|URIs: http[s]*://[^/]*/|URIs: http://${1}/|g; s|deb http[s]*://[^/]*/|deb http://${1}/|g" "$src"
done
rm -rf /var/lib/apt/lists/*
APT_OUT=$(apt-get update 2>&1)
APT_RC=$?
if echo "$APT_OUT" | grep -qi "hashsum\|hash sum"; then
echo " Mirror $1 failed (hash mismatch)"
return 1
elif echo "$APT_OUT" | grep -qi "SSL\|certificate"; then
echo " Mirror $1 failed (SSL/certificate error)"
return 1
elif [ $APT_RC -ne 0 ]; then
echo " Mirror $1 failed (apt-get update error)"
return 1
elif apt-get install -y $APT_BASE >/dev/null 2>&1; then
echo " CDN set to $1: tests passed"
return 0
else
echo " Mirror $1 failed (package install error)"
return 1
fi
}
scan_reachable() {
local result=""
for m in $1; do
if timeout 2 bash -c "echo >/dev/tcp/$m/80" 2>/dev/null; then
result="$result $m"
fi
done
echo "$result" | xargs
}
# Phase 1: Scan global mirrors first (independent of local CDN issues)
OTHERS_OK=$(scan_reachable "$OTHERS")
OTHERS_PICK=$(printf "%s\n" $OTHERS_OK | shuf | head -3 | xargs)
for mirror in $OTHERS_PICK; do
echo " Attempting mirror: $mirror"
try_mirrors "$mirror" && exit 0
done
# Phase 2: Try primary mirror
if [ "$DISTRO" = "ubuntu" ]; then
PRIMARY="archive.ubuntu.com"
else
PRIMARY="ftp.debian.org"
fi
if timeout 2 bash -c "echo >/dev/tcp/$PRIMARY/80" 2>/dev/null; then
echo " Attempting mirror: $PRIMARY"
try_mirrors "$PRIMARY" && exit 0
fi
# Phase 3: Fall back to regional mirrors
REGIONAL_OK=$(scan_reachable "$REGIONAL")
REGIONAL_PICK=$(printf "%s\n" $REGIONAL_OK | shuf | head -3 | xargs)
for mirror in $REGIONAL_PICK; do
echo " Attempting mirror: $mirror"
try_mirrors "$mirror" && exit 0
done
exit 2
' && mirror_exit=0 || mirror_exit=$?
if [[ $mirror_exit -eq 2 ]]; then
msg_warn "Multiple mirrors failed (possible CDN synchronization issue)."
if [[ "$var_os" == "ubuntu" ]]; then
msg_warn "Find Ubuntu mirrors at: https://launchpad.net/ubuntu/+archivemirrors"
else
msg_warn "Find Debian mirrors at: https://www.debian.org/mirror/list"
fi
local custom_mirror=""
while true; do
read -rp " Enter a mirror hostname (or 'skip' to abort): " custom_mirror </dev/tty
[[ -z "$custom_mirror" ]] && continue
[[ "$custom_mirror" == "skip" ]] && break
[[ ! "$custom_mirror" =~ ^[a-zA-Z0-9._-]+$ ]] && {
msg_warn "Invalid hostname format."
continue
}
pct exec "$CTID" -- bash -c "
for src in /etc/apt/sources.list.d/debian.sources /etc/apt/sources.list; do
[ -f \"\$src\" ] && sed -i \"s|URIs: http[s]*://[^/]*/|URIs: http://${custom_mirror}/|g; s|deb http[s]*://[^/]*/|deb http://${custom_mirror}/|g\" \"\$src\"
done
rm -rf /var/lib/apt/lists/*
apt-get update >/dev/null 2>&1 && apt-get install -y sudo curl mc gnupg2 jq >/dev/null 2>&1
" && break
msg_warn "Mirror '${custom_mirror}' also failed. Try another or type 'skip'."
done
if [[ "$custom_mirror" == "skip" ]]; then
msg_error "apt-get base packages installation failed"
install_exit_code=1
fi
elif [[ $mirror_exit -ne 0 ]]; then
msg_error "apt-get base packages installation failed"
install_exit_code=1
fi
} }
fi fi
@@ -4690,7 +4845,7 @@ EOF'
destroy_lxc() { destroy_lxc() {
if [[ -z "$CT_ID" ]]; then if [[ -z "$CT_ID" ]]; then
msg_error "No CT_ID found. Nothing to remove." msg_error "No CT_ID found. Nothing to remove."
return 1 return 65
fi fi
# Abort on Ctrl-C / Ctrl-D / ESC # Abort on Ctrl-C / Ctrl-D / ESC
@@ -4729,12 +4884,12 @@ resolve_storage_preselect() {
case "$class" in case "$class" in
template) required_content="vztmpl" ;; template) required_content="vztmpl" ;;
container) required_content="rootdir" ;; container) required_content="rootdir" ;;
*) return 1 ;; *) return 65 ;;
esac esac
[[ -z "$preselect" ]] && return 1 [[ -z "$preselect" ]] && return 1
if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then
msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)"
return 1 return 238
fi fi
local line total used free local line total used free
@@ -4858,7 +5013,7 @@ select_storage() {
;; ;;
*) *)
msg_error "Invalid storage class '$CLASS'" msg_error "Invalid storage class '$CLASS'"
return 1 return 65
;; ;;
esac esac
@@ -4940,7 +5095,7 @@ validate_storage_space() {
# Check if storage exists and is active # Check if storage exists and is active
if [[ -z "$storage_line" ]]; then if [[ -z "$storage_line" ]]; then
[[ "$show_dialog" == "yes" ]] && whiptail --msgbox "⚠️ Warning: Storage '$storage' not found!\n\nThe storage may be unavailable or disabled." 10 60 [[ "$show_dialog" == "yes" ]] && whiptail --msgbox "⚠️ Warning: Storage '$storage' not found!\n\nThe storage may be unavailable or disabled." 10 60
return 2 return 236
fi fi
# Check storage status (column 3) # Check storage status (column 3)
@@ -4948,7 +5103,7 @@ validate_storage_space() {
status=$(awk '{print $3}' <<<"$storage_line") status=$(awk '{print $3}' <<<"$storage_line")
if [[ "$status" == "disabled" ]]; then if [[ "$status" == "disabled" ]]; then
[[ "$show_dialog" == "yes" ]] && whiptail --msgbox "⚠️ Warning: Storage '$storage' is disabled!\n\nPlease enable the storage first." 10 60 [[ "$show_dialog" == "yes" ]] && whiptail --msgbox "⚠️ Warning: Storage '$storage' is disabled!\n\nPlease enable the storage first." 10 60
return 2 return 236
fi fi
# Get storage type and free space (column 6) # Get storage type and free space (column 6)
@@ -4971,7 +5126,7 @@ validate_storage_space() {
if [[ "$show_dialog" == "yes" ]]; then if [[ "$show_dialog" == "yes" ]]; then
whiptail --msgbox "⚠️ Warning: Storage '$storage' may not have enough space!\n\nStorage Type: ${storage_type}\nRequired: ${required_gb}GB\nAvailable: ${free_gb_fmt}\n\nYou can continue, but creation might fail." 14 70 whiptail --msgbox "⚠️ Warning: Storage '$storage' may not have enough space!\n\nStorage Type: ${storage_type}\nRequired: ${required_gb}GB\nAvailable: ${free_gb_fmt}\n\nYou can continue, but creation might fail." 14 70
fi fi
return 1 return 236
fi fi
return 0 return 0
@@ -5142,7 +5297,7 @@ create_lxc_container() {
fi fi
msg_info "Validating storage '$CONTAINER_STORAGE'" msg_info "Validating storage '$CONTAINER_STORAGE'"
STORAGE_TYPE=$(grep -E "^[^:]+: $CONTAINER_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 | head -1) STORAGE_TYPE=$(grep -E "^[^:]+: $CONTAINER_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 | head -1 || true)
if [[ -z "$STORAGE_TYPE" ]]; then if [[ -z "$STORAGE_TYPE" ]]; then
msg_error "Storage '$CONTAINER_STORAGE' not found in /etc/pve/storage.cfg" msg_error "Storage '$CONTAINER_STORAGE' not found in /etc/pve/storage.cfg"
@@ -5181,7 +5336,7 @@ create_lxc_container() {
msg_ok "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) validated" msg_ok "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) validated"
msg_info "Validating template storage '$TEMPLATE_STORAGE'" msg_info "Validating template storage '$TEMPLATE_STORAGE'"
TEMPLATE_TYPE=$(grep -E "^[^:]+: $TEMPLATE_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1) TEMPLATE_TYPE=$(grep -E "^[^:]+: $TEMPLATE_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 || true)
if ! pvesm status -content vztmpl 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$TEMPLATE_STORAGE"; then if ! pvesm status -content vztmpl 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$TEMPLATE_STORAGE"; then
msg_warn "Template storage '$TEMPLATE_STORAGE' may not support 'vztmpl'" msg_warn "Template storage '$TEMPLATE_STORAGE' may not support 'vztmpl'"
@@ -5704,7 +5859,7 @@ description() {
DESCRIPTION=$( DESCRIPTION=$(
cat <<EOF cat <<EOF
<div align='center'> <div align='center'>
<a href='https://community-scripts.com' target='_blank' rel='noopener noreferrer'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a> </a>

View File

@@ -319,11 +319,11 @@ function setup_cloud_init() {
if [ "$network_mode" = "static" ]; then if [ "$network_mode" = "static" ]; then
if [ -n "$static_ip" ] && ! validate_ip_cidr "$static_ip"; then if [ -n "$static_ip" ] && ! validate_ip_cidr "$static_ip"; then
_ci_msg_error "Invalid static IP format: $static_ip (expected: x.x.x.x/xx)" _ci_msg_error "Invalid static IP format: $static_ip (expected: x.x.x.x/xx)"
return 1 return 65
fi fi
if [ -n "$gateway" ] && ! validate_ip "$gateway"; then if [ -n "$gateway" ] && ! validate_ip "$gateway"; then
_ci_msg_error "Invalid gateway IP format: $gateway" _ci_msg_error "Invalid gateway IP format: $gateway"
return 1 return 65
fi fi
fi fi
@@ -433,7 +433,7 @@ function configure_cloud_init_interactive() {
if ! command -v whiptail >/dev/null 2>&1; then if ! command -v whiptail >/dev/null 2>&1; then
echo "Warning: whiptail not available, skipping interactive configuration" echo "Warning: whiptail not available, skipping interactive configuration"
export CLOUDINIT_ENABLE="no" export CLOUDINIT_ENABLE="no"
return 1 return 127
fi fi
# Ask if user wants to enable Cloud-Init # Ask if user wants to enable Cloud-Init
@@ -603,7 +603,7 @@ function get_vm_ip() {
elapsed=$((elapsed + 2)) elapsed=$((elapsed + 2))
done done
return 1 return 7
} }
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@@ -621,7 +621,7 @@ function wait_for_cloud_init() {
if [ -z "$vm_ip" ]; then if [ -z "$vm_ip" ]; then
_ci_msg_warn "Unable to determine VM IP address" _ci_msg_warn "Unable to determine VM IP address"
return 1 return 7
fi fi
_ci_msg_info "Waiting for Cloud-Init to complete on ${vm_ip}" _ci_msg_info "Waiting for Cloud-Init to complete on ${vm_ip}"
@@ -638,7 +638,7 @@ function wait_for_cloud_init() {
done done
_ci_msg_warn "Cloud-Init did not complete within ${timeout}s" _ci_msg_warn "Cloud-Init did not complete within ${timeout}s"
return 1 return 150
} }
# ============================================================================== # ==============================================================================

View File

@@ -858,7 +858,7 @@ get_header() {
if [ ! -s "$local_header_path" ]; then if [ ! -s "$local_header_path" ]; then
if ! curl -fsSL "$header_url" -o "$local_header_path"; then if ! curl -fsSL "$header_url" -o "$local_header_path"; then
msg_warn "Failed to download header: $header_url" msg_warn "Failed to download header: $header_url"
return 1 return 250
fi fi
fi fi
@@ -1358,7 +1358,7 @@ prompt_select() {
if [[ $num_options -eq 0 ]]; then if [[ $num_options -eq 0 ]]; then
msg_warn "prompt_select called with no options" msg_warn "prompt_select called with no options"
echo "" >&2 echo "" >&2
return 1 return 65
fi fi
# Validate default # Validate default
@@ -1600,7 +1600,7 @@ check_or_create_swap() {
swap_size_mb=$(prompt_input "Enter swap size in MB (e.g., 2048 for 2GB):" "2048" 60) swap_size_mb=$(prompt_input "Enter swap size in MB (e.g., 2048 for 2GB):" "2048" 60)
if ! [[ "$swap_size_mb" =~ ^[0-9]+$ ]]; then if ! [[ "$swap_size_mb" =~ ^[0-9]+$ ]]; then
msg_error "Invalid swap size: '${swap_size_mb}' (must be a number in MB)" msg_error "Invalid swap size: '${swap_size_mb}' (must be a number in MB)"
return 1 return 65
fi fi
local swap_file="/swapfile" local swap_file="/swapfile"
@@ -1608,19 +1608,19 @@ check_or_create_swap() {
msg_info "Creating ${swap_size_mb}MB swap file at $swap_file" msg_info "Creating ${swap_size_mb}MB swap file at $swap_file"
if ! dd if=/dev/zero of="$swap_file" bs=1M count="$swap_size_mb" status=progress; then if ! dd if=/dev/zero of="$swap_file" bs=1M count="$swap_size_mb" status=progress; then
msg_error "Failed to allocate swap file (dd failed)" msg_error "Failed to allocate swap file (dd failed)"
return 1 return 150
fi fi
if ! chmod 600 "$swap_file"; then if ! chmod 600 "$swap_file"; then
msg_error "Failed to set permissions on $swap_file" msg_error "Failed to set permissions on $swap_file"
return 1 return 150
fi fi
if ! mkswap "$swap_file"; then if ! mkswap "$swap_file"; then
msg_error "Failed to format swap file (mkswap failed)" msg_error "Failed to format swap file (mkswap failed)"
return 1 return 150
fi fi
if ! swapon "$swap_file"; then if ! swapon "$swap_file"; then
msg_error "Failed to activate swap (swapon failed)" msg_error "Failed to activate swap (swapon failed)"
return 1 return 150
fi fi
msg_ok "Swap file created and activated successfully" msg_ok "Swap file created and activated successfully"
} }
@@ -1699,13 +1699,13 @@ function get_lxc_ip() {
fi fi
done done
return 1 return 6
} }
LOCAL_IP="$(get_current_ip || true)" LOCAL_IP="$(get_current_ip || true)"
if [[ -z "$LOCAL_IP" ]]; then if [[ -z "$LOCAL_IP" ]]; then
msg_error "Could not determine LOCAL_IP (checked: eth0, hostname -I, ip route, IPv6 targets)" msg_error "Could not determine LOCAL_IP (checked: eth0, hostname -I, ip route, IPv6 targets)"
return 1 return 6
fi fi
fi fi

View File

@@ -210,6 +210,173 @@ network_check() {
# SECTION 3: OS UPDATE & PACKAGE MANAGEMENT # SECTION 3: OS UPDATE & PACKAGE MANAGEMENT
# ============================================================================== # ==============================================================================
# ------------------------------------------------------------------------------
# apt_update_safe()
#
# - Runs apt-get update with CDN mirror fallback
# - On failure, detects distro (Debian/Ubuntu) and tries alternate mirrors
# - Three-phase approach: global mirrors → primary mirror → regional mirrors
# - Falls back to manual user prompt if all auto mirrors fail
# - Detects hash mismatch, SSL errors, and generic apt failures
# ------------------------------------------------------------------------------
apt_update_safe() {
if $STD apt-get update; then
return 0
fi
local failed_mirror
failed_mirror=$(grep -m1 -oP '(?<=URIs: https?://)[^/]+' /etc/apt/sources.list.d/debian.sources 2>/dev/null || grep -m1 -oP '(?<=deb https?://)[^/]+' /etc/apt/sources.list 2>/dev/null || echo "unknown")
msg_warn "apt-get update failed (${failed_mirror}), trying alternate mirrors..."
local distro
distro=$(. /etc/os-release 2>/dev/null && echo "$ID" || echo "debian")
local eu_mirrors us_mirrors ap_mirrors
if [[ "$distro" == "ubuntu" ]]; then
eu_mirrors="de.archive.ubuntu.com fr.archive.ubuntu.com se.archive.ubuntu.com nl.archive.ubuntu.com it.archive.ubuntu.com ch.archive.ubuntu.com mirrors.xtom.de"
us_mirrors="us.archive.ubuntu.com archive.ubuntu.com mirrors.edge.kernel.org mirror.csclub.uwaterloo.ca mirrors.ocf.berkeley.edu mirror.math.princeton.edu"
ap_mirrors="au.archive.ubuntu.com jp.archive.ubuntu.com kr.archive.ubuntu.com tw.archive.ubuntu.com mirror.aarnet.edu.au"
else
eu_mirrors="ftp.de.debian.org ftp.fr.debian.org ftp.nl.debian.org ftp.uk.debian.org ftp.ch.debian.org ftp.se.debian.org ftp.it.debian.org ftp.fau.de ftp.halifax.rwth-aachen.de debian.mirror.lrz.de mirror.init7.net debian.ethz.ch mirrors.dotsrc.org debian.mirrors.ovh.net"
us_mirrors="ftp.us.debian.org ftp.ca.debian.org debian.csail.mit.edu mirrors.ocf.berkeley.edu mirrors.wikimedia.org debian.osuosl.org mirror.cogentco.com"
ap_mirrors="ftp.au.debian.org ftp.jp.debian.org ftp.tw.debian.org ftp.kr.debian.org ftp.hk.debian.org ftp.sg.debian.org mirror.aarnet.edu.au mirror.nitc.ac.in"
fi
local tz regional others
tz=$(cat /etc/timezone 2>/dev/null || echo "UTC")
case "$tz" in
Europe/* | Arctic/*)
regional="$eu_mirrors"
others="$us_mirrors $ap_mirrors"
;;
America/*)
regional="$us_mirrors"
others="$eu_mirrors $ap_mirrors"
;;
Asia/* | Australia/* | Pacific/*)
regional="$ap_mirrors"
others="$eu_mirrors $us_mirrors"
;;
*)
regional=""
others="$eu_mirrors $us_mirrors $ap_mirrors"
;;
esac
echo 'Acquire::By-Hash "no";' >/etc/apt/apt.conf.d/99no-by-hash
_try_apt_mirror() {
local m=$1
for src in /etc/apt/sources.list.d/debian.sources /etc/apt/sources.list; do
[[ -f "$src" ]] && sed -i "s|URIs: http[s]*://[^/]*/|URIs: http://${m}/|g; s|deb http[s]*://[^/]*/|deb http://${m}/|g" "$src"
done
rm -rf /var/lib/apt/lists/*
local out
out=$(apt-get update 2>&1)
if echo "$out" | grep -qi "hashsum\|hash sum"; then
msg_warn "Mirror ${m} failed (hash mismatch)"
return 1
elif echo "$out" | grep -qi "SSL\|certificate"; then
msg_warn "Mirror ${m} failed (SSL/certificate error)"
return 1
elif echo "$out" | grep -q "^E:"; then
msg_warn "Mirror ${m} failed (apt-get update error)"
return 1
else
msg_ok "CDN set to ${m}: tests passed"
return 0
fi
}
_scan_reachable() {
local result=""
for m in $1; do
if timeout 2 bash -c "echo >/dev/tcp/$m/80" 2>/dev/null; then
result="$result $m"
fi
done
echo "$result" | xargs
}
local apt_ok=false
# Phase 1: Scan global mirrors first (independent of local CDN issues)
local others_ok
others_ok=$(_scan_reachable "$others")
local others_pick
others_pick=$(printf '%s\n' $others_ok | shuf | head -3 | xargs)
for mirror in $others_pick; do
msg_custom "${INFO}" "${YW}" "Attempting mirror: ${mirror}"
if _try_apt_mirror "$mirror"; then
apt_ok=true
break
fi
done
# Phase 2: Try primary mirror
if [[ "$apt_ok" != true ]]; then
local primary
if [[ "$distro" == "ubuntu" ]]; then
primary="archive.ubuntu.com"
else
primary="ftp.debian.org"
fi
if timeout 2 bash -c "echo >/dev/tcp/$primary/80" 2>/dev/null; then
msg_custom "${INFO}" "${YW}" "Attempting mirror: ${primary}"
if _try_apt_mirror "$primary"; then
apt_ok=true
fi
fi
fi
# Phase 3: Fall back to regional mirrors
if [[ "$apt_ok" != true ]]; then
local regional_ok
regional_ok=$(_scan_reachable "$regional")
local regional_pick
regional_pick=$(printf '%s\n' $regional_ok | shuf | head -3 | xargs)
for mirror in $regional_pick; do
msg_custom "${INFO}" "${YW}" "Attempting mirror: ${mirror}"
if _try_apt_mirror "$mirror"; then
apt_ok=true
break
fi
done
fi
# Phase 4: All auto mirrors failed, prompt user
if [[ "$apt_ok" != true ]]; then
msg_warn "Multiple mirrors failed (possible CDN synchronization issue)."
if [[ "$distro" == "ubuntu" ]]; then
msg_warn "Find Ubuntu mirrors at: https://launchpad.net/ubuntu/+archivemirrors"
else
msg_warn "Find Debian mirrors at: https://www.debian.org/mirror/list"
fi
local custom_mirror
while true; do
read -rp " Enter a mirror hostname (or 'skip' to abort): " custom_mirror </dev/tty
[[ -z "$custom_mirror" ]] && continue
[[ "$custom_mirror" == "skip" ]] && break
[[ ! "$custom_mirror" =~ ^[a-zA-Z0-9._-]+$ ]] && {
msg_warn "Invalid hostname format."
continue
}
if _try_apt_mirror "$custom_mirror"; then
apt_ok=true
break
fi
msg_warn "Mirror '${custom_mirror}' also failed. Try another or type 'skip'."
done
fi
if [[ "$apt_ok" != true ]]; then
msg_error "All mirrors failed. Check network or try again later."
return 1
fi
}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# update_os() # update_os()
# #

File diff suppressed because it is too large Load Diff

View File

@@ -42,7 +42,7 @@ get_header() {
if [ ! -s "$local_header_path" ]; then if [ ! -s "$local_header_path" ]; then
if ! curl -fsSL "$header_url" -o "$local_header_path"; then if ! curl -fsSL "$header_url" -o "$local_header_path"; then
return 1 return 250
fi fi
fi fi
@@ -594,7 +594,7 @@ set_description() {
DESCRIPTION=$( DESCRIPTION=$(
cat <<EOF cat <<EOF
<div align='center'> <div align='center'>
<a href='https://community-scripts.com' target='_blank' rel='noopener noreferrer'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a> </a>

View File

@@ -165,9 +165,9 @@ function install() {
else else
read -rp "${TAB}Set admin username [admin]: " admin_user read -rp "${TAB}Set admin username [admin]: " admin_user
admin_user=${admin_user:-admin} admin_user=${admin_user:-admin}
read -rsp "${TAB}Set admin password [community-scripts.com]: " admin_pass read -rsp "${TAB}Set admin password [community-scripts.org]: " admin_pass
echo "" echo ""
admin_pass=${admin_pass:-community-scripts.com} admin_pass=${admin_pass:-community-scripts.org}
msg_ok "Configured with admin user: ${admin_user}" msg_ok "Configured with admin user: ${admin_user}"
fi fi

View File

@@ -201,9 +201,9 @@ server:
- neverWatchPath: "/lost+found" - neverWatchPath: "/lost+found"
auth: auth:
adminUsername: admin adminUsername: admin
adminPassword: community-scripts.com adminPassword: community-scripts.org
EOF EOF
msg_ok "Configured with default admin (admin / community-scripts.com)" msg_ok "Configured with default admin (admin / community-scripts.org)"
fi fi
msg_info "Creating service" msg_info "Creating service"

View File

@@ -131,17 +131,18 @@ if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then
cd /usr/local/community-scripts cd /usr/local/community-scripts
filebrowser config init -a '0.0.0.0' -p "$PORT" -d "$DB_PATH" &>/dev/null filebrowser config init -a '0.0.0.0' -p "$PORT" -d "$DB_PATH" &>/dev/null
filebrowser config set -a '0.0.0.0' -p "$PORT" -d "$DB_PATH" &>/dev/null filebrowser config set -a '0.0.0.0' -p "$PORT" -d "$DB_PATH" &>/dev/null
filebrowser config init --auth.method=noauth &>/dev/null filebrowser config set --auth.method=noauth --database "$DB_PATH" &>/dev/null
filebrowser config set --auth.method=noauth &>/dev/null if ! filebrowser users update 1 --perm.admin --database "$DB_PATH" &>/dev/null; then
filebrowser users add ID 1 --perm.admin &>/dev/null filebrowser users add admin community-scripts.org --perm.admin --database "$DB_PATH" &>/dev/null
fi
msg_ok "No Authentication configured" msg_ok "No Authentication configured"
else else
msg_info "Setting up default authentication" msg_info "Setting up default authentication"
cd /usr/local/community-scripts cd /usr/local/community-scripts
filebrowser config init -a '0.0.0.0' -p "$PORT" -d "$DB_PATH" &>/dev/null filebrowser config init -a '0.0.0.0' -p "$PORT" -d "$DB_PATH" &>/dev/null
filebrowser config set -a '0.0.0.0' -p "$PORT" -d "$DB_PATH" &>/dev/null filebrowser config set -a '0.0.0.0' -p "$PORT" -d "$DB_PATH" &>/dev/null
filebrowser users add admin community-scripts.com --perm.admin --database "$DB_PATH" &>/dev/null filebrowser users add admin community-scripts.org --perm.admin --database "$DB_PATH" &>/dev/null
msg_ok "Default authentication configured (admin:community-scripts.com)" msg_ok "Default authentication configured (admin:community-scripts.org)"
fi fi
msg_info "Creating service" msg_info "Creating service"

View File

@@ -0,0 +1,173 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: Tom Frenzel (tomfrenzel)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/CodeWithCJ/SparkyFitness
if ! command -v curl &>/dev/null; then
printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2
apt-get update >/dev/null 2>&1
apt-get install -y curl >/dev/null 2>&1
fi
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)
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "sparkyfitness-garmin" "addon"
# Enable error handling
set -Eeuo pipefail
trap 'error_handler' ERR
load_functions
# ==============================================================================
# CONFIGURATION
# ==============================================================================
APP="SparkyFitness-Garmin"
APP_TYPE="addon"
INSTALL_PATH="/opt/sparkyfitness-garmin"
CONFIG_PATH="/etc/sparkyfitness-garmin/.env"
SERVICE_PATH="/etc/systemd/system/sparkyfitness-garmin.service"
DEFAULT_PORT=8000
# ==============================================================================
# OS DETECTION
# ==============================================================================
if ! grep -qE 'ID=debian|ID=ubuntu' /etc/os-release 2>/dev/null; then
echo -e "${CROSS} Unsupported OS detected. This script only supports Debian and Ubuntu."
exit 238
fi
# ==============================================================================
# SparkyFitness LXC DETECTION
# ==============================================================================
if [[ ! -d /opt/sparkyfitness ]]; then
echo -e "${CROSS} No SparkyFitness installation detected. This addon must be installed within a container that already has SparkyFitness installed."
exit 238
fi
# ==============================================================================
# UNINSTALL
# ==============================================================================
function uninstall() {
msg_info "Uninstalling ${APP}"
systemctl disable --now sparkyfitness-garmin.service &>/dev/null || true
rm -rf "$SERVICE_PATH" "$CONFIG_PATH" "$INSTALL_PATH" ~/.sparkyfitness-garmin
msg_ok "${APP} has been uninstalled"
}
# ==============================================================================
# UPDATE
# ==============================================================================
function update() {
if check_for_gh_release "sparkyfitness-garmin" "CodeWithCJ/SparkyFitness"; then
PYTHON_VERSION="3.13" setup_uv
msg_info "Stopping service"
systemctl stop sparkyfitness-garmin.service &>/dev/null || true
msg_ok "Stopped service"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "sparkyfitness-garmin" "CodeWithCJ/SparkyFitness" "tarball" "latest" $INSTALL_PATH
msg_info "Starting service"
systemctl start sparkyfitness-garmin
msg_ok "Started service"
msg_ok "Updated successfully"
exit
fi
}
# ==============================================================================
# INSTALL
# ==============================================================================
function install() {
PYTHON_VERSION="3.13" setup_uv
fetch_and_deploy_gh_release "sparkyfitness-garmin" "CodeWithCJ/SparkyFitness" "tarball" "latest" $INSTALL_PATH
msg_info "Setting up ${APP}"
mkdir -p "/etc/sparkyfitness-garmin"
cp "/opt/sparkyfitness-garmin/docker/.env.example" $CONFIG_PATH
cd $INSTALL_PATH/SparkyFitnessGarmin
$STD uv venv --clear .venv
$STD uv pip install -r requirements.txt
sed -i -e "s|^#\?GARMIN_MICROSERVICE_URL=.*|GARMIN_MICROSERVICE_URL=http://${LOCAL_IP}:${DEFAULT_PORT}|" $CONFIG_PATH
cat <<EOF >/etc/systemd/system/sparkyfitness-garmin.service
[Unit]
Description=${APP}
After=network.target sparkyfitness-server.service
Requires=sparkyfitness-server.service
[Service]
Type=simple
WorkingDirectory=$INSTALL_PATH/SparkyFitnessGarmin
EnvironmentFile=$CONFIG_PATH
ExecStart=$INSTALL_PATH/SparkyFitnessGarmin/.venv/bin/python3 -m uvicorn main:app --host 0.0.0.0 --port ${DEFAULT_PORT}
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now sparkyfitness-garmin
msg_ok "Set up ${APP} - reachable at http://${LOCAL_IP}:${DEFAULT_PORT}"
msg_ok "You might need to update the GARMIN_MICROSERVICE_URL in your SparkyFitness .env file to http://${LOCAL_IP}:${DEFAULT_PORT}"
}
# ==============================================================================
# MAIN
# ==============================================================================
header_info
ensure_usr_local_bin_persist
get_lxc_ip
# Handle type=update (called from update script)
if [[ "${type:-}" == "update" ]]; then
if [[ -d "$INSTALL_PATH" ]]; then
update
else
msg_error "${APP} is not installed. Nothing to update."
exit 233
fi
exit 0
fi
# Check if already installed
if [[ -d "$INSTALL_PATH" && -n "$(ls -A "$INSTALL_PATH" 2>/dev/null)" ]]; 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} - UV (Python Version Manager)"
echo -e "${TAB} - SparkyFitness Garmin Microservice"
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

View File

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

View File

@@ -1,11 +1,24 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Copyright (c) 2021-2026 tteck # Copyright (c) 2021-2026 community-scripts ORG
# Author: tteck (tteckster) # Author: MickLesk (CanbiZ)
# License: MIT # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE #
# This script manages a local cron job for automatic LXC container OS updates.
# The update script is downloaded once, displayed for review, and installed
# locally. Cron runs the local copy — no remote code execution at runtime.
#
# bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/cron-update-lxcs.sh)" # bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/cron-update-lxcs.sh)"
set -euo pipefail
REPO_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main"
SCRIPT_URL="${REPO_URL}/tools/pve/update-lxcs-cron.sh"
LOCAL_SCRIPT="/usr/local/bin/update-lxcs.sh"
CONF_FILE="/etc/update-lxcs.conf"
LOG_FILE="/var/log/update-lxcs-cron.log"
CRON_ENTRY="0 0 * * 0 ${LOCAL_SCRIPT} >>${LOG_FILE} 2>&1"
clear clear
cat <<"EOF" cat <<"EOF"
______ __ __ __ __ __ _ ________ ______ __ __ __ __ __ _ ________
@@ -16,41 +29,322 @@ cat <<"EOF"
/_/ /_/
EOF EOF
add() { info() { echo -e "\n \e[36m[Info]\e[0m $1"; }
ok() { echo -e " \e[32m[OK]\e[0m $1"; }
err() { echo -e " \e[31m[Error]\e[0m $1" >&2; }
confirm() {
local prompt="${1:-Proceed?}"
while true; do while true; do
read -p "This script will add a crontab schedule that updates all LXCs every Sunday at midnight. Proceed(y/n)?" yn read -rp " ${prompt} (y/n): " yn
case $yn in case $yn in
[Yy]*) break ;; [Yy]*) return 0 ;;
[Nn]*) exit ;; [Nn]*) return 1 ;;
*) echo "Please answer yes or no." ;; *) echo " Please answer yes or no." ;;
esac esac
done done
sh -c '(crontab -l -u root 2>/dev/null; echo "0 0 * * 0 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/update-lxcs-cron.sh)\" >>/var/log/update-lxcs-cron.log 2>/dev/null") | crontab -u root -' }
clear
echo -e "\n To view Cron Update LXCs logs: cat /var/log/update-lxcs-cron.log" download_script() {
local tmp
tmp=$(mktemp)
if ! curl -fsSL -o "$tmp" "$SCRIPT_URL"; then
err "Failed to download script from:\n ${SCRIPT_URL}"
rm -f "$tmp"
return 1
fi
echo "$tmp"
}
review_script() {
local file="$1"
local hash
hash=$(sha256sum "$file" | awk '{print $1}')
echo ""
echo -e " \e[1;33m─── Script Content ───────────────────────────────────────────\e[0m"
cat "$file"
echo -e " \e[1;33m──────────────────────────────────────────────────────────────\e[0m"
echo -e " \e[36mSHA256:\e[0m ${hash}"
echo -e " \e[36mSource:\e[0m ${SCRIPT_URL}"
echo ""
}
remove_legacy_cron() {
if crontab -l -u root 2>/dev/null | grep -q "update-lxcs-cron.sh"; then
(crontab -l -u root 2>/dev/null | grep -v "update-lxcs-cron.sh") | crontab -u root -
ok "Removed legacy curl-based cron entry"
fi
}
add() {
info "Downloading update script..."
local tmp
tmp=$(download_script) || exit 1
local hash
hash=$(sha256sum "$tmp" | awk '{print $1}')
echo ""
echo -e " \e[1;33m─── Installation Summary ─────────────────────────────────────\e[0m"
echo -e " \e[36mSource:\e[0m ${SCRIPT_URL}"
echo -e " \e[36mSHA256:\e[0m ${hash}"
echo -e " \e[36mInstall to:\e[0m ${LOCAL_SCRIPT}"
echo -e " \e[36mConfig:\e[0m ${CONF_FILE}"
echo -e " \e[36mLog file:\e[0m ${LOG_FILE}"
echo -e " \e[36mCron schedule:\e[0m Every Sunday at midnight (0 0 * * 0)"
echo -e " \e[1;33m──────────────────────────────────────────────────────────────\e[0m"
echo ""
if confirm "Review script content before installing?"; then
review_script "$tmp"
fi
if ! confirm "Install this script and activate cron schedule?"; then
rm -f "$tmp"
echo " Aborted."
exit 0
fi
remove_legacy_cron
install -m 0755 "$tmp" "$LOCAL_SCRIPT"
rm -f "$tmp"
ok "Installed script to ${LOCAL_SCRIPT}"
if [[ ! -f "$CONF_FILE" ]]; then
cat >"$CONF_FILE" <<'CONF'
# Configuration for automatic LXC container OS updates.
# Add container IDs to exclude from updates (comma-separated):
# EXCLUDE=100,101,102
EXCLUDE=
CONF
ok "Created config ${CONF_FILE}"
fi
(
crontab -l -u root 2>/dev/null | grep -v "${LOCAL_SCRIPT}" || true
echo "${CRON_ENTRY}"
) | crontab -u root -
ok "Added cron schedule: Every Sunday at midnight"
echo ""
echo -e " \e[36mLocal script:\e[0m ${LOCAL_SCRIPT}"
echo -e " \e[36mConfig:\e[0m ${CONF_FILE}"
echo -e " \e[36mLog file:\e[0m ${LOG_FILE}"
echo ""
} }
remove() { remove() {
(crontab -l | grep -v "update-lxcs-cron.sh") | crontab - if crontab -l -u root 2>/dev/null | grep -q "${LOCAL_SCRIPT}"; then
rm -rf /var/log/update-lxcs-cron.log (crontab -l -u root 2>/dev/null | grep -v "${LOCAL_SCRIPT}") | crontab -u root -
echo "Removed Crontab Schedule from Proxmox VE" ok "Removed cron schedule"
fi
remove_legacy_cron
[[ -f "$LOCAL_SCRIPT" ]] && rm -f "$LOCAL_SCRIPT" && ok "Removed ${LOCAL_SCRIPT}"
[[ -f "$LOG_FILE" ]] && rm -f "$LOG_FILE" && ok "Removed ${LOG_FILE}"
echo -e "\n Cron Update LXCs has been fully removed."
echo -e " \e[90mNote: ${CONF_FILE} was kept (remove manually if desired).\e[0m"
} }
OPTIONS=(Add "Add Crontab Schedule" update_script() {
Remove "Remove Crontab Schedule") if [[ ! -f "$LOCAL_SCRIPT" ]]; then
err "No local script found at ${LOCAL_SCRIPT}. Use 'Add' first."
exit 1
fi
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Cron Update LXCs" --menu "Select an option:" 10 58 2 \ info "Downloading latest version..."
"${OPTIONS[@]}" 3>&1 1>&2 2>&3) local tmp
tmp=$(download_script) || exit 1
if command -v diff &>/dev/null; then
local changes
changes=$(diff --color=auto "$LOCAL_SCRIPT" "$tmp" 2>/dev/null || true)
if [[ -z "$changes" ]]; then
ok "Script is already up-to-date (no changes)."
rm -f "$tmp"
return
fi
echo ""
echo -e " \e[1;33m─── Changes ──────────────────────────────────────────────────\e[0m"
echo "$changes"
echo -e " \e[1;33m──────────────────────────────────────────────────────────────\e[0m"
else
review_script "$tmp"
fi
local new_hash old_hash
new_hash=$(sha256sum "$tmp" | awk '{print $1}')
old_hash=$(sha256sum "$LOCAL_SCRIPT" | awk '{print $1}')
echo -e " \e[36mCurrent SHA256:\e[0m ${old_hash}"
echo -e " \e[36mNew SHA256:\e[0m ${new_hash}"
echo ""
if ! confirm "Apply update?"; then
rm -f "$tmp"
echo " Aborted."
return
fi
install -m 0755 "$tmp" "$LOCAL_SCRIPT"
rm -f "$tmp"
ok "Updated ${LOCAL_SCRIPT}"
}
view_script() {
if [[ ! -f "$LOCAL_SCRIPT" ]]; then
err "No local script found at ${LOCAL_SCRIPT}. Use 'Add' first."
exit 1
fi
local view_choice
view_choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "View Script" --menu "What do you want to view?" 12 60 3 \
"Worker" "Installed update script (${LOCAL_SCRIPT##*/})" \
"Cron" "Cron schedule & configuration" \
"Both" "Show everything" \
3>&1 1>&2 2>&3) || return 0
case "$view_choice" in
"Worker") view_worker_script ;;
"Cron") view_cron_config ;;
"Both") view_cron_config && echo "" && view_worker_script ;;
esac
}
view_worker_script() {
local hash
hash=$(sha256sum "$LOCAL_SCRIPT" | awk '{print $1}')
echo ""
echo -e " \e[1;33m─── ${LOCAL_SCRIPT} ───\e[0m"
cat "$LOCAL_SCRIPT"
echo -e " \e[1;33m──────────────────────────────────────────────────────────────\e[0m"
echo -e " \e[36mSHA256:\e[0m ${hash}"
echo -e " \e[36mInstalled:\e[0m $(stat -c '%y' "$LOCAL_SCRIPT" 2>/dev/null | cut -d. -f1)"
echo ""
}
view_cron_config() {
echo ""
echo -e " \e[1;33m─── Cron Configuration ───────────────────────────────────────\e[0m"
if crontab -l -u root 2>/dev/null | grep -q "${LOCAL_SCRIPT}"; then
local entry
entry=$(crontab -l -u root 2>/dev/null | grep "${LOCAL_SCRIPT}")
echo -e " \e[36mCron entry:\e[0m ${entry}"
local schedule
schedule=$(echo "$entry" | awk '{print $1,$2,$3,$4,$5}')
echo -e " \e[36mSchedule:\e[0m ${schedule} ($(cron_to_human "$schedule"))"
else
echo -e " \e[31mCron:\e[0m Not configured"
fi
if [[ -f "$CONF_FILE" ]]; then
echo -e " \e[36mConfig file:\e[0m ${CONF_FILE}"
local excludes
excludes=$(grep -oP '^\s*EXCLUDE\s*=\s*\K.*' "$CONF_FILE" 2>/dev/null || true)
echo -e " \e[36mExcluded:\e[0m ${excludes:-(none)}"
echo ""
echo -e " \e[90m--- ${CONF_FILE} ---\e[0m"
cat "$CONF_FILE"
else
echo -e " \e[36mConfig file:\e[0m (not created yet)"
fi
if [[ -f "$LOG_FILE" ]]; then
local log_size
log_size=$(du -h "$LOG_FILE" | awk '{print $1}')
echo -e " \e[36mLog file:\e[0m ${LOG_FILE} (${log_size})"
fi
echo -e " \e[1;33m──────────────────────────────────────────────────────────────\e[0m"
echo ""
}
cron_to_human() {
local schedule="$1"
case "$schedule" in
"0 0 * * 0") echo "Every Sunday at midnight" ;;
"0 0 * * *") echo "Daily at midnight" ;;
"0 * * * *") echo "Every hour" ;;
*) echo "Custom schedule" ;;
esac
}
show_status() {
echo ""
if [[ -f "$LOCAL_SCRIPT" ]]; then
local hash
hash=$(sha256sum "$LOCAL_SCRIPT" | awk '{print $1}')
ok "Script installed: ${LOCAL_SCRIPT}"
echo -e " \e[36mSHA256:\e[0m ${hash}"
echo -e " \e[36mInstalled:\e[0m $(stat -c '%y' "$LOCAL_SCRIPT" 2>/dev/null | cut -d. -f1)"
else
err "Script not installed"
fi
if crontab -l -u root 2>/dev/null | grep -q "${LOCAL_SCRIPT}"; then
local schedule
schedule=$(crontab -l -u root 2>/dev/null | grep "${LOCAL_SCRIPT}" | awk '{print $1,$2,$3,$4,$5}')
ok "Cron active: ${schedule}"
else
err "Cron not configured"
fi
if [[ -f "$CONF_FILE" ]]; then
local excludes
excludes=$(grep -oP '^\s*EXCLUDE\s*=\s*\K.*' "$CONF_FILE" 2>/dev/null || echo "(none)")
echo -e " \e[36mExcluded:\e[0m ${excludes:-"(none)"}"
fi
if [[ -f "$LOG_FILE" ]]; then
local log_size last_run
log_size=$(du -h "$LOG_FILE" | awk '{print $1}')
last_run=$(grep -oP '^\s+\K\w.*' "$LOG_FILE" | tail -1)
echo -e " \e[36mLog file:\e[0m ${LOG_FILE} (${log_size})"
[[ -n "${last_run:-}" ]] && echo -e " \e[36mLast run:\e[0m ${last_run}"
else
echo -e " \e[36mLog file:\e[0m (no runs yet)"
fi
echo ""
}
run_now() {
if [[ ! -f "$LOCAL_SCRIPT" ]]; then
err "No local script found at ${LOCAL_SCRIPT}. Use 'Add' first."
exit 1
fi
info "Running update script now..."
bash "$LOCAL_SCRIPT" | tee -a "$LOG_FILE"
ok "Run completed. Log appended to ${LOG_FILE}"
}
rotate_log() {
if [[ ! -f "$LOG_FILE" ]]; then
info "No log file to rotate."
return
fi
local log_size
log_size=$(stat -c '%s' "$LOG_FILE" 2>/dev/null || echo 0)
local log_size_h
log_size_h=$(du -h "$LOG_FILE" | awk '{print $1}')
if confirm "Rotate log file? (current size: ${log_size_h})"; then
mv "$LOG_FILE" "${LOG_FILE}.old"
ok "Rotated: ${LOG_FILE}${LOG_FILE}.old"
fi
}
OPTIONS=(
Add "Download, review & install cron schedule"
Remove "Remove cron schedule & local script"
Update "Update local script from repository"
Status "Show installation status & last run"
Run "Run update script now (manual trigger)"
View "View cron config & installed script"
Rotate "Rotate log file"
)
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Cron Update LXCs" --menu "Select an option:" 16 68 7 \
"${OPTIONS[@]}" 3>&1 1>&2 2>&3) || exit 0
case $CHOICE in case $CHOICE in
"Add") "Add") add ;;
add "Remove") remove ;;
;; "Update") update_script ;;
"Remove") "Status") show_status ;;
remove "Run") run_now ;;
;; "View") view_script ;;
*) "Rotate") rotate_log ;;
echo "Exiting..."
exit 0
;;
esac esac

View File

@@ -1,23 +1,43 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Copyright (c) 2021-2026 tteck # Copyright (c) 2021-2026 community-scripts ORG
# Author: tteck (tteckster) # Author: MickLesk (CanbiZ)
# License: MIT # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE #
# This script is installed locally by cron-update-lxcs.sh and executed
# by cron. It updates all LXC containers using their native package manager.
CONF_FILE="/etc/update-lxcs.conf"
echo -e "\n $(date)" echo -e "\n $(date)"
# Collect excluded containers from arguments
excluded_containers=("$@") excluded_containers=("$@")
# Merge exclusions from config file if it exists
if [[ -f "$CONF_FILE" ]]; then
conf_exclude=$(grep -oP '^\s*EXCLUDE\s*=\s*\K[0-9,]+' "$CONF_FILE" 2>/dev/null || true)
IFS=',' read -ra conf_ids <<<"$conf_exclude"
for id in "${conf_ids[@]}"; do
id="${id// /}"
[[ -n "$id" ]] && excluded_containers+=("$id")
done
fi
function update_container() { function update_container() {
container=$1 local container=$1
name=$(pct exec "$container" hostname) local name
echo -e "\n [Info] Updating $container : $name \n" name=$(pct exec "$container" hostname 2>/dev/null || echo "unknown")
local os
os=$(pct config "$container" | awk '/^ostype/ {print $2}') os=$(pct config "$container" | awk '/^ostype/ {print $2}')
echo -e "\n [Info] Updating $container : $name (os: $os)"
case "$os" in case "$os" in
alpine) pct exec "$container" -- ash -c "apk -U upgrade" ;; alpine) pct exec "$container" -- ash -c "apk -U upgrade" ;;
archlinux) pct exec "$container" -- bash -c "pacman -Syyu --noconfirm" ;; archlinux) pct exec "$container" -- bash -c "pacman -Syyu --noconfirm" ;;
fedora | rocky | centos | alma) pct exec "$container" -- bash -c "dnf -y update && dnf -y upgrade" ;; fedora | rocky | centos | alma) pct exec "$container" -- bash -c "dnf -y update && dnf -y upgrade" ;;
ubuntu | debian | devuan) pct exec "$container" -- bash -c "apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confold" dist-upgrade -y; rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED" ;; ubuntu | debian | devuan) pct exec "$container" -- bash -c "apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::='--force-confold' dist-upgrade -y; rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED" ;;
opensuse) pct exec "$container" -- bash -c "zypper ref && zypper --non-interactive dup" ;; opensuse) pct exec "$container" -- bash -c "zypper ref && zypper --non-interactive dup" ;;
*) echo " [Warn] Unknown OS type '$os' for container $container, skipping" ;;
esac esac
} }
@@ -34,16 +54,19 @@ for container in $(pct list | awk '{if(NR>1) print $1}'); do
sleep 1 sleep 1
else else
status=$(pct status "$container") status=$(pct status "$container")
template=$(pct config "$container" | grep -q "template:" && echo "true" || echo "false") if pct config "$container" 2>/dev/null | grep -q "^template:"; then
if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then echo -e "[Info] Skipping template $container"
continue
fi
if [ "$status" == "status: stopped" ]; then
echo -e "[Info] Starting $container" echo -e "[Info] Starting $container"
pct start "$container" pct start "$container"
sleep 5 sleep 5
update_container "$container" update_container "$container" || echo " [Error] Update failed for $container"
echo -e "[Info] Shutting down $container" echo -e "[Info] Shutting down $container"
pct shutdown "$container" & pct shutdown "$container" --timeout 60 &
elif [ "$status" == "status: running" ]; then elif [ "$status" == "status: running" ]; then
update_container "$container" update_container "$container" || echo " [Error] Update failed for $container"
fi fi
fi fi
done done

View File

@@ -551,7 +551,7 @@ qm set $VMID \
DESCRIPTION=$( DESCRIPTION=$(
cat <<EOF cat <<EOF
<div align='center'> <div align='center'>
<a href='https://community-scripts.com' target='_blank' rel='noopener noreferrer'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a> </a>

View File

@@ -631,7 +631,7 @@ rm -f "$WORK_FILE"
DESCRIPTION=$( DESCRIPTION=$(
cat <<EOF cat <<EOF
<div align='center'> <div align='center'>
<a href='https://community-scripts.com' target='_blank' rel='noopener noreferrer'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a> </a>

View File

@@ -568,7 +568,7 @@ fi
DESCRIPTION=$( DESCRIPTION=$(
cat <<EOF cat <<EOF
<div align='center'> <div align='center'>
<a href='https://community-scripts.com' target='_blank' rel='noopener noreferrer'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a> </a>

View File

@@ -639,7 +639,7 @@ msg_ok "Resized disk"
DESCRIPTION=$( DESCRIPTION=$(
cat <<EOF cat <<EOF
<div align='center'> <div align='center'>
<a href='https://community-scripts.com' target='_blank' rel='noopener noreferrer'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a> </a>

View File

@@ -622,7 +622,7 @@ qm set $VMID \
DESCRIPTION=$( DESCRIPTION=$(
cat <<EOF cat <<EOF
<div align='center'> <div align='center'>
<a href='https://community-scripts.com' target='_blank' rel='noopener noreferrer'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a> </a>

View File

@@ -546,7 +546,7 @@ qm set $VMID \
DESCRIPTION=$( DESCRIPTION=$(
cat <<EOF cat <<EOF
<div align='center'> <div align='center'>
<a href='https://community-scripts.com' target='_blank' rel='noopener noreferrer'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a> </a>

View File

@@ -605,7 +605,7 @@ msg_ok "Resized disk to ${DISK_SIZE}"
DESCRIPTION=$( DESCRIPTION=$(
cat <<EOF cat <<EOF
<div align='center'> <div align='center'>
<a href='https://community-scripts.com' target='_blank' rel='noopener noreferrer'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a> </a>

View File

@@ -750,7 +750,7 @@ qm resize $VMID scsi0 20G >/dev/null
DESCRIPTION=$( DESCRIPTION=$(
cat <<EOF cat <<EOF
<div align='center'> <div align='center'>
<a href='https://community-scripts.com' target='_blank' rel='noopener noreferrer'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/michelroegl-brunner/ProxmoxVE/refs/heads/develop/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> <img src='https://raw.githubusercontent.com/michelroegl-brunner/ProxmoxVE/refs/heads/develop/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a> </a>

View File

@@ -560,7 +560,7 @@ qm set $VMID \
DESCRIPTION=$( DESCRIPTION=$(
cat <<EOF cat <<EOF
<div align='center'> <div align='center'>
<a href='https://community-scripts.com' target='_blank' rel='noopener noreferrer'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a> </a>

View File

@@ -462,7 +462,7 @@ qm set $VMID \
DESCRIPTION=$( DESCRIPTION=$(
cat <<EOF cat <<EOF
<div align='center'> <div align='center'>
<a href='https://community-scripts.com' target='_blank' rel='noopener noreferrer'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a> </a>

View File

@@ -610,7 +610,7 @@ fi
DESCRIPTION=$( DESCRIPTION=$(
cat <<EOF cat <<EOF
<div align='center'> <div align='center'>
<a href='https://community-scripts.com' target='_blank' rel='noopener noreferrer'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a> </a>

View File

@@ -542,7 +542,7 @@ qm set $VMID \
DESCRIPTION=$( DESCRIPTION=$(
cat <<EOF cat <<EOF
<div align='center'> <div align='center'>
<a href='https://community-scripts.com' target='_blank' rel='noopener noreferrer'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a> </a>

View File

@@ -544,7 +544,7 @@ qm set $VMID \
DESCRIPTION=$( DESCRIPTION=$(
cat <<EOF cat <<EOF
<div align='center'> <div align='center'>
<a href='https://community-scripts.com' target='_blank' rel='noopener noreferrer'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a> </a>

View File

@@ -543,7 +543,7 @@ qm set $VMID \
DESCRIPTION=$( DESCRIPTION=$(
cat <<EOF cat <<EOF
<div align='center'> <div align='center'>
<a href='https://community-scripts.com' target='_blank' rel='noopener noreferrer'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a> </a>

View File

@@ -590,7 +590,7 @@ qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null
DESCRIPTION=$( DESCRIPTION=$(
cat <<EOF cat <<EOF
<div align='center'> <div align='center'>
<a href='https://community-scripts.com' target='_blank' rel='noopener noreferrer'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a> </a>