mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-02-04 20:33:24 +01:00
Compare commits
5 Commits
automated/
...
fix/vaultw
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b159ee173 | ||
|
|
82a07fe06e | ||
|
|
378e791bfd | ||
|
|
ddfabdee47 | ||
|
|
133c198fcc |
310
.github/changelogs/2026/01.md
generated
vendored
310
.github/changelogs/2026/01.md
generated
vendored
@@ -1,156 +1,14 @@
|
|||||||
## 2026-01-31
|
## 2026-01-27
|
||||||
|
|
||||||
### 🆕 New Scripts
|
|
||||||
|
|
||||||
- shelfmark ([#11371](https://github.com/community-scripts/ProxmoxVE/pull/11371))
|
|
||||||
|
|
||||||
### 🚀 Updated Scripts
|
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
|
||||||
|
|
||||||
- fix: yubal: add git [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11394](https://github.com/community-scripts/ProxmoxVE/pull/11394))
|
|
||||||
|
|
||||||
## 2026-01-30
|
|
||||||
|
|
||||||
### 🆕 New Scripts
|
|
||||||
|
|
||||||
- languagetool ([#11370](https://github.com/community-scripts/ProxmoxVE/pull/11370))
|
|
||||||
- Ampache ([#11369](https://github.com/community-scripts/ProxmoxVE/pull/11369))
|
|
||||||
|
|
||||||
### 🚀 Updated Scripts
|
|
||||||
|
|
||||||
- #### 🔧 Refactor
|
|
||||||
|
|
||||||
- Refactor: remove redundant PHP_MODULE entries in several scripts [@MickLesk](https://github.com/MickLesk) ([#11362](https://github.com/community-scripts/ProxmoxVE/pull/11362))
|
|
||||||
- Refactor: Koillection [@MickLesk](https://github.com/MickLesk) ([#11361](https://github.com/community-scripts/ProxmoxVE/pull/11361))
|
|
||||||
|
|
||||||
### 💾 Core
|
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
|
||||||
|
|
||||||
- core: meilisearch - add data migration for version upgrades [@MickLesk](https://github.com/MickLesk) ([#11356](https://github.com/community-scripts/ProxmoxVE/pull/11356))
|
|
||||||
|
|
||||||
- #### ✨ New Features
|
|
||||||
|
|
||||||
- [tools] Add `fetch_and_deploy_from_url()` [@tremor021](https://github.com/tremor021) ([#11376](https://github.com/community-scripts/ProxmoxVE/pull/11376))
|
|
||||||
- core: php - improve module handling and prevent installation failures [@MickLesk](https://github.com/MickLesk) ([#11358](https://github.com/community-scripts/ProxmoxVE/pull/11358))
|
|
||||||
|
|
||||||
## 2026-01-29
|
|
||||||
|
|
||||||
### 🆕 New Scripts
|
|
||||||
|
|
||||||
- Alpine-Valkey [@MickLesk](https://github.com/MickLesk) ([#11320](https://github.com/community-scripts/ProxmoxVE/pull/11320))
|
|
||||||
|
|
||||||
### 🚀 Updated Scripts
|
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
|
||||||
|
|
||||||
- Immich: Pin version to 2.5.2 [@vhsdream](https://github.com/vhsdream) ([#11335](https://github.com/community-scripts/ProxmoxVE/pull/11335))
|
|
||||||
- Kollection: Update to php 8.5 [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11315](https://github.com/community-scripts/ProxmoxVE/pull/11315))
|
|
||||||
- Notifiarr: change installation check from apt to systemd service [@MickLesk](https://github.com/MickLesk) ([#11319](https://github.com/community-scripts/ProxmoxVE/pull/11319))
|
|
||||||
|
|
||||||
- #### ✨ New Features
|
|
||||||
|
|
||||||
- [FEAT] Immich: Enable Maintenance Mode before update [@vhsdream](https://github.com/vhsdream) ([#11342](https://github.com/community-scripts/ProxmoxVE/pull/11342))
|
|
||||||
- jellyfin: add logrotate instead of reducing log level [@MickLesk](https://github.com/MickLesk) ([#11326](https://github.com/community-scripts/ProxmoxVE/pull/11326))
|
|
||||||
- core: Add config file handling options | Fix Vikunja update with interactive overwrite [@MickLesk](https://github.com/MickLesk) ([#11317](https://github.com/community-scripts/ProxmoxVE/pull/11317))
|
|
||||||
- Immich: v2.5.0 [@vhsdream](https://github.com/vhsdream) ([#11240](https://github.com/community-scripts/ProxmoxVE/pull/11240))
|
|
||||||
|
|
||||||
- #### 💥 Breaking Changes
|
|
||||||
|
|
||||||
- fix: vikunja v1 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11308](https://github.com/community-scripts/ProxmoxVE/pull/11308))
|
|
||||||
|
|
||||||
- #### 🔧 Refactor
|
|
||||||
|
|
||||||
- Refactor: Byparr [@vhsdream](https://github.com/vhsdream) ([#11338](https://github.com/community-scripts/ProxmoxVE/pull/11338))
|
|
||||||
- cloudflare: Remove deprecated DNS-over-HTTPS proxy option [@MickLesk](https://github.com/MickLesk) ([#11068](https://github.com/community-scripts/ProxmoxVE/pull/11068))
|
|
||||||
|
|
||||||
### 💾 Core
|
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
|
||||||
|
|
||||||
- build.func: Replace storage variable with searchdomain variable [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11322](https://github.com/community-scripts/ProxmoxVE/pull/11322))
|
|
||||||
|
|
||||||
### 📂 Github
|
|
||||||
|
|
||||||
- Add workflow to lock closed issues [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11316](https://github.com/community-scripts/ProxmoxVE/pull/11316))
|
|
||||||
|
|
||||||
## 2026-01-28
|
|
||||||
|
|
||||||
### 🆕 New Scripts
|
|
||||||
|
|
||||||
- nodecast-tv ([#11287](https://github.com/community-scripts/ProxmoxVE/pull/11287))
|
|
||||||
|
|
||||||
### 🚀 Updated Scripts
|
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
|
||||||
|
|
||||||
- Ubuntu 25.04 VM - Change default start from yes to no [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11292](https://github.com/community-scripts/ProxmoxVE/pull/11292))
|
|
||||||
|
|
||||||
- #### ✨ New Features
|
|
||||||
|
|
||||||
- various scripts: use setup_meilisearch function [@MickLesk](https://github.com/MickLesk) ([#11259](https://github.com/community-scripts/ProxmoxVE/pull/11259))
|
|
||||||
|
|
||||||
- #### 🔧 Refactor
|
|
||||||
|
|
||||||
- Refactor: NPMPlus / Default Login [@MickLesk](https://github.com/MickLesk) ([#11262](https://github.com/community-scripts/ProxmoxVE/pull/11262))
|
|
||||||
|
|
||||||
### 💾 Core
|
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
|
||||||
|
|
||||||
- core: sed patch for ram [@lavacano](https://github.com/lavacano) ([#11285](https://github.com/community-scripts/ProxmoxVE/pull/11285))
|
|
||||||
- Fix installer loop caused by invalid whiptail menu separator [@Mesteriis](https://github.com/Mesteriis) ([#11237](https://github.com/community-scripts/ProxmoxVE/pull/11237))
|
|
||||||
- core: fix Debian 13 LXC template root ownership bug [@MickLesk](https://github.com/MickLesk) ([#11277](https://github.com/community-scripts/ProxmoxVE/pull/11277))
|
|
||||||
- tools.func: prevent systemd-tmpfiles failure in unprivileged LXC during deb install [@MickLesk](https://github.com/MickLesk) ([#11271](https://github.com/community-scripts/ProxmoxVE/pull/11271))
|
|
||||||
- tools.func: fix php "wait_for" hint [@MickLesk](https://github.com/MickLesk) ([#11254](https://github.com/community-scripts/ProxmoxVE/pull/11254))
|
|
||||||
|
|
||||||
- #### ✨ New Features
|
|
||||||
|
|
||||||
- core: update dynamic values in LXC profile on update_motd_ip [@MickLesk](https://github.com/MickLesk) ([#11268](https://github.com/community-scripts/ProxmoxVE/pull/11268))
|
|
||||||
- tools.func: add new function - setup_meilisearch [@MickLesk](https://github.com/MickLesk) ([#11258](https://github.com/community-scripts/ProxmoxVE/pull/11258))
|
|
||||||
|
|
||||||
### 📂 Github
|
|
||||||
|
|
||||||
- github: add GitHub-based versions.json updater [@MickLesk](https://github.com/MickLesk) ([#10021](https://github.com/community-scripts/ProxmoxVE/pull/10021))
|
|
||||||
|
|
||||||
### 🌐 Website
|
|
||||||
|
|
||||||
- #### ✨ New Features
|
|
||||||
|
|
||||||
- Frontend: use github-versions.json for version display [@MickLesk](https://github.com/MickLesk) ([#11281](https://github.com/community-scripts/ProxmoxVE/pull/11281))
|
|
||||||
|
|
||||||
- #### 📝 Script Information
|
|
||||||
|
|
||||||
- fix: homarr: conf location [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11253](https://github.com/community-scripts/ProxmoxVE/pull/11253))
|
|
||||||
|
|
||||||
## 2026-01-27
|
|
||||||
|
|
||||||
### 🚀 Updated Scripts
|
### 🚀 Updated Scripts
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
- #### 🐞 Bug Fixes
|
||||||
|
|
||||||
- [FIX] Jotty: backup and restore custom config [@vhsdream](https://github.com/vhsdream) ([#11212](https://github.com/community-scripts/ProxmoxVE/pull/11212))
|
- [FIX] Jotty: backup and restore custom config [@vhsdream](https://github.com/vhsdream) ([#11212](https://github.com/community-scripts/ProxmoxVE/pull/11212))
|
||||||
- Immich: update libraw [@vhsdream](https://github.com/vhsdream) ([#11233](https://github.com/community-scripts/ProxmoxVE/pull/11233))
|
|
||||||
|
|
||||||
- #### ✨ New Features
|
|
||||||
|
|
||||||
- grist: enable optional enterprise features toggle [@MickLesk](https://github.com/MickLesk) ([#11239](https://github.com/community-scripts/ProxmoxVE/pull/11239))
|
|
||||||
|
|
||||||
- #### 🔧 Refactor
|
|
||||||
|
|
||||||
- Termix: use nginx.conf from upstream repo [@MickLesk](https://github.com/MickLesk) ([#11228](https://github.com/community-scripts/ProxmoxVE/pull/11228))
|
|
||||||
|
|
||||||
### 💾 Core
|
|
||||||
|
|
||||||
- #### ✨ New Features
|
|
||||||
|
|
||||||
- feat: add NVIDIA driver install prompt for GPU-enabled containers [@devdecrux](https://github.com/devdecrux) ([#11184](https://github.com/community-scripts/ProxmoxVE/pull/11184))
|
|
||||||
|
|
||||||
### 📚 Documentation
|
### 📚 Documentation
|
||||||
|
|
||||||
- doc setup_deb822_repo arg order [@chrnie](https://github.com/chrnie) ([#11215](https://github.com/community-scripts/ProxmoxVE/pull/11215))
|
- doc setup_deb822_repo arg order [@chrnie](https://github.com/chrnie) ([#11215](https://github.com/community-scripts/ProxmoxVE/pull/11215))
|
||||||
- changelog: archive old entries to year/month files [@MickLesk](https://github.com/MickLesk) ([#11225](https://github.com/community-scripts/ProxmoxVE/pull/11225))
|
|
||||||
|
|
||||||
## 2026-01-26
|
## 2026-01-26
|
||||||
|
|
||||||
@@ -242,7 +100,7 @@
|
|||||||
### 🆕 New Scripts
|
### 🆕 New Scripts
|
||||||
|
|
||||||
- Tracearr ([#11079](https://github.com/community-scripts/ProxmoxVE/pull/11079))
|
- Tracearr ([#11079](https://github.com/community-scripts/ProxmoxVE/pull/11079))
|
||||||
- Dawarich ([#11075](https://github.com/community-scripts/ProxmoxVE/pull/11075))
|
- Dawarich ([#11075](https://github.com/community-scripts/ProxmoxVE/pull/11075))
|
||||||
|
|
||||||
### 🚀 Updated Scripts
|
### 🚀 Updated Scripts
|
||||||
|
|
||||||
@@ -392,7 +250,7 @@
|
|||||||
### 🆕 New Scripts
|
### 🆕 New Scripts
|
||||||
|
|
||||||
- Termix ([#10887](https://github.com/community-scripts/ProxmoxVE/pull/10887))
|
- Termix ([#10887](https://github.com/community-scripts/ProxmoxVE/pull/10887))
|
||||||
- ThingsBoard ([#10904](https://github.com/community-scripts/ProxmoxVE/pull/10904))
|
- ThingsBoard ([#10904](https://github.com/community-scripts/ProxmoxVE/pull/10904))
|
||||||
|
|
||||||
### 🚀 Updated Scripts
|
### 🚀 Updated Scripts
|
||||||
|
|
||||||
@@ -461,7 +319,7 @@
|
|||||||
### 🆕 New Scripts
|
### 🆕 New Scripts
|
||||||
|
|
||||||
- Flatnotes ([#10857](https://github.com/community-scripts/ProxmoxVE/pull/10857))
|
- Flatnotes ([#10857](https://github.com/community-scripts/ProxmoxVE/pull/10857))
|
||||||
- Unifi OS Server ([#10856](https://github.com/community-scripts/ProxmoxVE/pull/10856))
|
- Unifi OS Server ([#10856](https://github.com/community-scripts/ProxmoxVE/pull/10856))
|
||||||
|
|
||||||
### 🚀 Updated Scripts
|
### 🚀 Updated Scripts
|
||||||
|
|
||||||
@@ -527,7 +385,7 @@
|
|||||||
### 🆕 New Scripts
|
### 🆕 New Scripts
|
||||||
|
|
||||||
- Investbrain ([#10774](https://github.com/community-scripts/ProxmoxVE/pull/10774))
|
- Investbrain ([#10774](https://github.com/community-scripts/ProxmoxVE/pull/10774))
|
||||||
- Fladder ([#10768](https://github.com/community-scripts/ProxmoxVE/pull/10768))
|
- Fladder ([#10768](https://github.com/community-scripts/ProxmoxVE/pull/10768))
|
||||||
|
|
||||||
### 🚀 Updated Scripts
|
### 🚀 Updated Scripts
|
||||||
|
|
||||||
@@ -716,7 +574,7 @@
|
|||||||
### 📚 Documentation
|
### 📚 Documentation
|
||||||
|
|
||||||
- [gh] New Script template update [@tremor021](https://github.com/tremor021) ([#10607](https://github.com/community-scripts/ProxmoxVE/pull/10607))
|
- [gh] New Script template update [@tremor021](https://github.com/tremor021) ([#10607](https://github.com/community-scripts/ProxmoxVE/pull/10607))
|
||||||
- chore: bump copyright to 2026 - happy new year [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10585](https://github.com/community-scripts/ProxmoxVE/pull/10585))
|
- chore: bump copyright to 2026 - happy new year [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10585](https://github.com/community-scripts/ProxmoxVE/pull/10585))
|
||||||
|
|
||||||
### 📂 Github
|
### 📂 Github
|
||||||
|
|
||||||
@@ -797,7 +655,7 @@
|
|||||||
### ❔ Uncategorized
|
### ❔ Uncategorized
|
||||||
|
|
||||||
- Wireguard: Update WGDashboard notes URL to the new link [@tremor021](https://github.com/tremor021) ([#10496](https://github.com/community-scripts/ProxmoxVE/pull/10496))
|
- Wireguard: Update WGDashboard notes URL to the new link [@tremor021](https://github.com/tremor021) ([#10496](https://github.com/community-scripts/ProxmoxVE/pull/10496))
|
||||||
- InvoiceNinja: Update database credentias information [@tremor021](https://github.com/tremor021) ([#10497](https://github.com/community-scripts/ProxmoxVE/pull/10497))
|
- InvoiceNinja: Update database credentias information [@tremor021](https://github.com/tremor021) ([#10497](https://github.com/community-scripts/ProxmoxVE/pull/10497))
|
||||||
|
|
||||||
## 2026-01-02
|
## 2026-01-02
|
||||||
|
|
||||||
@@ -826,3 +684,157 @@
|
|||||||
- #### 🐞 Bug Fixes
|
- #### 🐞 Bug Fixes
|
||||||
|
|
||||||
- Fix MariaDB runtime directory persistence on container reboot [@Copilot](https://github.com/Copilot) ([#10468](https://github.com/community-scripts/ProxmoxVE/pull/10468))
|
- Fix MariaDB runtime directory persistence on container reboot [@Copilot](https://github.com/Copilot) ([#10468](https://github.com/community-scripts/ProxmoxVE/pull/10468))
|
||||||
|
|
||||||
|
|
||||||
|
## 2026-01-31
|
||||||
|
|
||||||
|
### 🆕 New Scripts
|
||||||
|
|
||||||
|
- shelfmark ([#11371](https://github.com/community-scripts/ProxmoxVE/pull/11371))
|
||||||
|
|
||||||
|
### 🚀 Updated Scripts
|
||||||
|
|
||||||
|
- #### 🐞 Bug Fixes
|
||||||
|
|
||||||
|
- fix: yubal: add git [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11394](https://github.com/community-scripts/ProxmoxVE/pull/11394))
|
||||||
|
|
||||||
|
## 2026-01-30
|
||||||
|
|
||||||
|
### 🆕 New Scripts
|
||||||
|
|
||||||
|
- languagetool ([#11370](https://github.com/community-scripts/ProxmoxVE/pull/11370))
|
||||||
|
- Ampache ([#11369](https://github.com/community-scripts/ProxmoxVE/pull/11369))
|
||||||
|
|
||||||
|
### 🚀 Updated Scripts
|
||||||
|
|
||||||
|
- #### 🔧 Refactor
|
||||||
|
|
||||||
|
- Refactor: remove redundant PHP_MODULE entries in several scripts [@MickLesk](https://github.com/MickLesk) ([#11362](https://github.com/community-scripts/ProxmoxVE/pull/11362))
|
||||||
|
- Refactor: Koillection [@MickLesk](https://github.com/MickLesk) ([#11361](https://github.com/community-scripts/ProxmoxVE/pull/11361))
|
||||||
|
|
||||||
|
### 💾 Core
|
||||||
|
|
||||||
|
- #### 🐞 Bug Fixes
|
||||||
|
|
||||||
|
- core: meilisearch - add data migration for version upgrades [@MickLesk](https://github.com/MickLesk) ([#11356](https://github.com/community-scripts/ProxmoxVE/pull/11356))
|
||||||
|
|
||||||
|
- #### ✨ New Features
|
||||||
|
|
||||||
|
- [tools] Add `fetch_and_deploy_from_url()` [@tremor021](https://github.com/tremor021) ([#11376](https://github.com/community-scripts/ProxmoxVE/pull/11376))
|
||||||
|
- core: php - improve module handling and prevent installation failures [@MickLesk](https://github.com/MickLesk) ([#11358](https://github.com/community-scripts/ProxmoxVE/pull/11358))
|
||||||
|
|
||||||
|
## 2026-01-29
|
||||||
|
|
||||||
|
### 🆕 New Scripts
|
||||||
|
|
||||||
|
- Alpine-Valkey [@MickLesk](https://github.com/MickLesk) ([#11320](https://github.com/community-scripts/ProxmoxVE/pull/11320))
|
||||||
|
|
||||||
|
### 🚀 Updated Scripts
|
||||||
|
|
||||||
|
- #### 🐞 Bug Fixes
|
||||||
|
|
||||||
|
- Immich: Pin version to 2.5.2 [@vhsdream](https://github.com/vhsdream) ([#11335](https://github.com/community-scripts/ProxmoxVE/pull/11335))
|
||||||
|
- Kollection: Update to php 8.5 [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11315](https://github.com/community-scripts/ProxmoxVE/pull/11315))
|
||||||
|
- Notifiarr: change installation check from apt to systemd service [@MickLesk](https://github.com/MickLesk) ([#11319](https://github.com/community-scripts/ProxmoxVE/pull/11319))
|
||||||
|
|
||||||
|
- #### ✨ New Features
|
||||||
|
|
||||||
|
- [FEAT] Immich: Enable Maintenance Mode before update [@vhsdream](https://github.com/vhsdream) ([#11342](https://github.com/community-scripts/ProxmoxVE/pull/11342))
|
||||||
|
- jellyfin: add logrotate instead of reducing log level [@MickLesk](https://github.com/MickLesk) ([#11326](https://github.com/community-scripts/ProxmoxVE/pull/11326))
|
||||||
|
- core: Add config file handling options | Fix Vikunja update with interactive overwrite [@MickLesk](https://github.com/MickLesk) ([#11317](https://github.com/community-scripts/ProxmoxVE/pull/11317))
|
||||||
|
- Immich: v2.5.0 [@vhsdream](https://github.com/vhsdream) ([#11240](https://github.com/community-scripts/ProxmoxVE/pull/11240))
|
||||||
|
|
||||||
|
- #### 💥 Breaking Changes
|
||||||
|
|
||||||
|
- fix: vikunja v1 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11308](https://github.com/community-scripts/ProxmoxVE/pull/11308))
|
||||||
|
|
||||||
|
- #### 🔧 Refactor
|
||||||
|
|
||||||
|
- Refactor: Byparr [@vhsdream](https://github.com/vhsdream) ([#11338](https://github.com/community-scripts/ProxmoxVE/pull/11338))
|
||||||
|
- cloudflare: Remove deprecated DNS-over-HTTPS proxy option [@MickLesk](https://github.com/MickLesk) ([#11068](https://github.com/community-scripts/ProxmoxVE/pull/11068))
|
||||||
|
|
||||||
|
### 💾 Core
|
||||||
|
|
||||||
|
- #### 🐞 Bug Fixes
|
||||||
|
|
||||||
|
- build.func: Replace storage variable with searchdomain variable [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11322](https://github.com/community-scripts/ProxmoxVE/pull/11322))
|
||||||
|
|
||||||
|
### 📂 Github
|
||||||
|
|
||||||
|
- Add workflow to lock closed issues [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11316](https://github.com/community-scripts/ProxmoxVE/pull/11316))
|
||||||
|
|
||||||
|
## 2026-01-28
|
||||||
|
|
||||||
|
### 🆕 New Scripts
|
||||||
|
|
||||||
|
- nodecast-tv ([#11287](https://github.com/community-scripts/ProxmoxVE/pull/11287))
|
||||||
|
|
||||||
|
### 🚀 Updated Scripts
|
||||||
|
|
||||||
|
- #### 🐞 Bug Fixes
|
||||||
|
|
||||||
|
- Ubuntu 25.04 VM - Change default start from yes to no [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11292](https://github.com/community-scripts/ProxmoxVE/pull/11292))
|
||||||
|
|
||||||
|
- #### ✨ New Features
|
||||||
|
|
||||||
|
- various scripts: use setup_meilisearch function [@MickLesk](https://github.com/MickLesk) ([#11259](https://github.com/community-scripts/ProxmoxVE/pull/11259))
|
||||||
|
|
||||||
|
- #### 🔧 Refactor
|
||||||
|
|
||||||
|
- Refactor: NPMPlus / Default Login [@MickLesk](https://github.com/MickLesk) ([#11262](https://github.com/community-scripts/ProxmoxVE/pull/11262))
|
||||||
|
|
||||||
|
### 💾 Core
|
||||||
|
|
||||||
|
- #### 🐞 Bug Fixes
|
||||||
|
|
||||||
|
- core: sed patch for ram [@lavacano](https://github.com/lavacano) ([#11285](https://github.com/community-scripts/ProxmoxVE/pull/11285))
|
||||||
|
- Fix installer loop caused by invalid whiptail menu separator [@Mesteriis](https://github.com/Mesteriis) ([#11237](https://github.com/community-scripts/ProxmoxVE/pull/11237))
|
||||||
|
- core: fix Debian 13 LXC template root ownership bug [@MickLesk](https://github.com/MickLesk) ([#11277](https://github.com/community-scripts/ProxmoxVE/pull/11277))
|
||||||
|
- tools.func: prevent systemd-tmpfiles failure in unprivileged LXC during deb install [@MickLesk](https://github.com/MickLesk) ([#11271](https://github.com/community-scripts/ProxmoxVE/pull/11271))
|
||||||
|
- tools.func: fix php "wait_for" hint [@MickLesk](https://github.com/MickLesk) ([#11254](https://github.com/community-scripts/ProxmoxVE/pull/11254))
|
||||||
|
|
||||||
|
- #### ✨ New Features
|
||||||
|
|
||||||
|
- core: update dynamic values in LXC profile on update_motd_ip [@MickLesk](https://github.com/MickLesk) ([#11268](https://github.com/community-scripts/ProxmoxVE/pull/11268))
|
||||||
|
- tools.func: add new function - setup_meilisearch [@MickLesk](https://github.com/MickLesk) ([#11258](https://github.com/community-scripts/ProxmoxVE/pull/11258))
|
||||||
|
|
||||||
|
### 📂 Github
|
||||||
|
|
||||||
|
- github: add GitHub-based versions.json updater [@MickLesk](https://github.com/MickLesk) ([#10021](https://github.com/community-scripts/ProxmoxVE/pull/10021))
|
||||||
|
|
||||||
|
### 🌐 Website
|
||||||
|
|
||||||
|
- #### ✨ New Features
|
||||||
|
|
||||||
|
- Frontend: use github-versions.json for version display [@MickLesk](https://github.com/MickLesk) ([#11281](https://github.com/community-scripts/ProxmoxVE/pull/11281))
|
||||||
|
|
||||||
|
- #### 📝 Script Information
|
||||||
|
|
||||||
|
- fix: homarr: conf location [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11253](https://github.com/community-scripts/ProxmoxVE/pull/11253))
|
||||||
|
|
||||||
|
## 2026-01-27
|
||||||
|
|
||||||
|
### 🚀 Updated Scripts
|
||||||
|
|
||||||
|
- #### 🐞 Bug Fixes
|
||||||
|
|
||||||
|
- Immich: update libraw [@vhsdream](https://github.com/vhsdream) ([#11233](https://github.com/community-scripts/ProxmoxVE/pull/11233))
|
||||||
|
|
||||||
|
- #### ✨ New Features
|
||||||
|
|
||||||
|
- grist: enable optional enterprise features toggle [@MickLesk](https://github.com/MickLesk) ([#11239](https://github.com/community-scripts/ProxmoxVE/pull/11239))
|
||||||
|
|
||||||
|
- #### 🔧 Refactor
|
||||||
|
|
||||||
|
- Termix: use nginx.conf from upstream repo [@MickLesk](https://github.com/MickLesk) ([#11228](https://github.com/community-scripts/ProxmoxVE/pull/11228))
|
||||||
|
|
||||||
|
### 💾 Core
|
||||||
|
|
||||||
|
- #### ✨ New Features
|
||||||
|
|
||||||
|
- feat: add NVIDIA driver install prompt for GPU-enabled containers [@devdecrux](https://github.com/devdecrux) ([#11184](https://github.com/community-scripts/ProxmoxVE/pull/11184))
|
||||||
|
|
||||||
|
### 📚 Documentation
|
||||||
|
|
||||||
|
- doc setup_deb822_repo arg order [@chrnie](https://github.com/chrnie) ([#11215](https://github.com/community-scripts/ProxmoxVE/pull/11215))
|
||||||
|
- changelog: archive old entries to year/month files [@MickLesk](https://github.com/MickLesk) ([#11225](https://github.com/community-scripts/ProxmoxVE/pull/11225))
|
||||||
121
.github/changelogs/2026/02.md
generated
vendored
121
.github/changelogs/2026/02.md
generated
vendored
@@ -1,121 +0,0 @@
|
|||||||
## 2026-02-04
|
|
||||||
|
|
||||||
### 🆕 New Scripts
|
|
||||||
|
|
||||||
- Wishlist ([#11527](https://github.com/community-scripts/ProxmoxVE/pull/11527))
|
|
||||||
- WriteFreely ([#11524](https://github.com/community-scripts/ProxmoxVE/pull/11524))
|
|
||||||
|
|
||||||
### 💾 Core
|
|
||||||
|
|
||||||
- #### ✨ New Features
|
|
||||||
|
|
||||||
- core: create vm-core.func from dev [@MickLesk](https://github.com/MickLesk) ([#11528](https://github.com/community-scripts/ProxmoxVE/pull/11528))
|
|
||||||
|
|
||||||
### 🌐 Website
|
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
|
||||||
|
|
||||||
- fix(frontend): implement weighted search scoring for command menu [@ls-root](https://github.com/ls-root) ([#11534](https://github.com/community-scripts/ProxmoxVE/pull/11534))
|
|
||||||
|
|
||||||
## 2026-02-03
|
|
||||||
|
|
||||||
### 🆕 New Scripts
|
|
||||||
|
|
||||||
- Wealthfolio ([#11511](https://github.com/community-scripts/ProxmoxVE/pull/11511))
|
|
||||||
|
|
||||||
### 🚀 Updated Scripts
|
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
|
||||||
|
|
||||||
- [FIX] Shelfmark: unpin Chromium version [@vhsdream](https://github.com/vhsdream) ([#11505](https://github.com/community-scripts/ProxmoxVE/pull/11505))
|
|
||||||
|
|
||||||
- #### ✨ New Features
|
|
||||||
|
|
||||||
- [FEAT] Scanopy: automatically update integrated daemon [@vhsdream](https://github.com/vhsdream) ([#11506](https://github.com/community-scripts/ProxmoxVE/pull/11506))
|
|
||||||
|
|
||||||
### 💾 Core
|
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
|
||||||
|
|
||||||
- [FIX] tools.func: trim spaces in app_lc when checking for gh release [@vhsdream](https://github.com/vhsdream) ([#11512](https://github.com/community-scripts/ProxmoxVE/pull/11512))
|
|
||||||
|
|
||||||
### 🌐 Website
|
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
|
||||||
|
|
||||||
- fix(frontend): decouple table pagination from summary fetching [@ls-root](https://github.com/ls-root) ([#11495](https://github.com/community-scripts/ProxmoxVE/pull/11495))
|
|
||||||
|
|
||||||
## 2026-02-02
|
|
||||||
|
|
||||||
### 🆕 New Scripts
|
|
||||||
|
|
||||||
- rustypaste | Alpine-rustypaste ([#11457](https://github.com/community-scripts/ProxmoxVE/pull/11457))
|
|
||||||
- KitchenOwl ([#11453](https://github.com/community-scripts/ProxmoxVE/pull/11453))
|
|
||||||
|
|
||||||
### 🚀 Updated Scripts
|
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
|
||||||
|
|
||||||
- Grist: Update dependencies [@tremor021](https://github.com/tremor021) ([#11489](https://github.com/community-scripts/ProxmoxVE/pull/11489))
|
|
||||||
- Allow "downgrade" of libigdgmm12 [@vhsdream](https://github.com/vhsdream) ([#11478](https://github.com/community-scripts/ProxmoxVE/pull/11478))
|
|
||||||
- Disable NPM install and update due to OpenResty SHA-1 signature issues [@MickLesk](https://github.com/MickLesk) ([#11471](https://github.com/community-scripts/ProxmoxVE/pull/11471))
|
|
||||||
|
|
||||||
- #### ✨ New Features
|
|
||||||
|
|
||||||
- Refactor: Forgejo & readeck - migrate to codeberg functions [@MickLesk](https://github.com/MickLesk) ([#11460](https://github.com/community-scripts/ProxmoxVE/pull/11460))
|
|
||||||
|
|
||||||
- #### 💥 Breaking Changes
|
|
||||||
|
|
||||||
- [FIX] Scanopy: remove daemon build [@vhsdream](https://github.com/vhsdream) ([#11444](https://github.com/community-scripts/ProxmoxVE/pull/11444))
|
|
||||||
|
|
||||||
- #### 🔧 Refactor
|
|
||||||
|
|
||||||
- Refactor: Vaultwarden [@MickLesk](https://github.com/MickLesk) ([#11445](https://github.com/community-scripts/ProxmoxVE/pull/11445))
|
|
||||||
- various scripts: use ensure_dependencies instead of apt [@MickLesk](https://github.com/MickLesk) ([#11463](https://github.com/community-scripts/ProxmoxVE/pull/11463))
|
|
||||||
|
|
||||||
### 🌐 Website
|
|
||||||
|
|
||||||
- cleanup(frontend): remove unused /category-view route [@ls-root](https://github.com/ls-root) ([#11461](https://github.com/community-scripts/ProxmoxVE/pull/11461))
|
|
||||||
|
|
||||||
- #### ✨ New Features
|
|
||||||
|
|
||||||
- feat(frontend): preview tab [@ls-root](https://github.com/ls-root) ([#11475](https://github.com/community-scripts/ProxmoxVE/pull/11475))
|
|
||||||
|
|
||||||
## 2026-02-01
|
|
||||||
|
|
||||||
### 🚀 Updated Scripts
|
|
||||||
|
|
||||||
- fix headers [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11422](https://github.com/community-scripts/ProxmoxVE/pull/11422))
|
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
|
||||||
|
|
||||||
- 2fauth: export PHP_VERSION for nginx config [@MickLesk](https://github.com/MickLesk) ([#11441](https://github.com/community-scripts/ProxmoxVE/pull/11441))
|
|
||||||
- Prometheus Paperless NGX Exporter: Set correct binary path in systemd unit file [@andygrunwald](https://github.com/andygrunwald) ([#11438](https://github.com/community-scripts/ProxmoxVE/pull/11438))
|
|
||||||
- tracearr: install/update new prestart script from upstream [@durzo](https://github.com/durzo) ([#11433](https://github.com/community-scripts/ProxmoxVE/pull/11433))
|
|
||||||
- n8n: Fix dependencies [@tremor021](https://github.com/tremor021) ([#11429](https://github.com/community-scripts/ProxmoxVE/pull/11429))
|
|
||||||
- [Hotfix] Bunkerweb update [@vhsdream](https://github.com/vhsdream) ([#11402](https://github.com/community-scripts/ProxmoxVE/pull/11402))
|
|
||||||
- [Hotfix] Immich: revert healthcheck feature [@vhsdream](https://github.com/vhsdream) ([#11427](https://github.com/community-scripts/ProxmoxVE/pull/11427))
|
|
||||||
|
|
||||||
- #### ✨ New Features
|
|
||||||
|
|
||||||
- tools.func: add codeberg functions & autocaliweb: migrate from GitHub to Codeberg [@MickLesk](https://github.com/MickLesk) ([#11440](https://github.com/community-scripts/ProxmoxVE/pull/11440))
|
|
||||||
- Immich Refactor #2 [@vhsdream](https://github.com/vhsdream) ([#11375](https://github.com/community-scripts/ProxmoxVE/pull/11375))
|
|
||||||
|
|
||||||
- #### 🔧 Refactor
|
|
||||||
|
|
||||||
- WordPress: Refactor [@tremor021](https://github.com/tremor021) ([#11408](https://github.com/community-scripts/ProxmoxVE/pull/11408))
|
|
||||||
- Refactor: Whisparr [@tremor021](https://github.com/tremor021) ([#11411](https://github.com/community-scripts/ProxmoxVE/pull/11411))
|
|
||||||
|
|
||||||
### 💾 Core
|
|
||||||
|
|
||||||
- #### ✨ New Features
|
|
||||||
|
|
||||||
- [tools]: Update `fetch_and_deply_from_url()` [@tremor021](https://github.com/tremor021) ([#11410](https://github.com/community-scripts/ProxmoxVE/pull/11410))
|
|
||||||
|
|
||||||
### 🌐 Website
|
|
||||||
|
|
||||||
- feat(frontend): implement UX refinements and syntax highlighting [@ls-root](https://github.com/ls-root) ([#11423](https://github.com/community-scripts/ProxmoxVE/pull/11423))
|
|
||||||
|
|
||||||
- #### ✨ New Features
|
|
||||||
|
|
||||||
- feat(frontend): add contribution CTA to empty search state [@ls-root](https://github.com/ls-root) ([#11412](https://github.com/community-scripts/ProxmoxVE/pull/11412))
|
|
||||||
97
.github/workflows/changelog-archive.yml
generated
vendored
97
.github/workflows/changelog-archive.yml
generated
vendored
@@ -54,9 +54,8 @@ jobs:
|
|||||||
|
|
||||||
console.log(`Cutoff date: ${cutoffDate.toISOString().split('T')[0]}`);
|
console.log(`Cutoff date: ${cutoffDate.toISOString().split('T')[0]}`);
|
||||||
|
|
||||||
// Read changelog and normalize line endings
|
// Read changelog
|
||||||
let content = await fs.readFile(CHANGELOG_PATH, 'utf-8');
|
const content = await fs.readFile(CHANGELOG_PATH, 'utf-8');
|
||||||
content = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
||||||
const lines = content.split('\n');
|
const lines = content.split('\n');
|
||||||
|
|
||||||
// Parse entries
|
// Parse entries
|
||||||
@@ -67,39 +66,9 @@ jobs:
|
|||||||
let currentDate = null;
|
let currentDate = null;
|
||||||
let currentContent = [];
|
let currentContent = [];
|
||||||
let inHeader = true;
|
let inHeader = true;
|
||||||
let inOldHistory = false;
|
|
||||||
let historyDetailsDepth = 0;
|
|
||||||
|
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (const line of lines) {
|
||||||
const line = lines[i];
|
|
||||||
const match = line.match(datePattern);
|
const match = line.match(datePattern);
|
||||||
|
|
||||||
// Detect the start of History section: <details> followed by line with 📜 History
|
|
||||||
if (inHeader && !inOldHistory && line.trim() === '<details>') {
|
|
||||||
// Look ahead to see if this is the History section
|
|
||||||
const nextLine = lines[i + 1] || '';
|
|
||||||
if (nextLine.includes('📜 History')) {
|
|
||||||
inOldHistory = true;
|
|
||||||
historyDetailsDepth = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Track nested details tags to find the end of History section
|
|
||||||
if (inOldHistory) {
|
|
||||||
if (line.trim() === '<details>') {
|
|
||||||
historyDetailsDepth++;
|
|
||||||
}
|
|
||||||
if (line.trim() === '</details>') {
|
|
||||||
historyDetailsDepth--;
|
|
||||||
if (historyDetailsDepth === 0) {
|
|
||||||
// We've closed the main History details tag
|
|
||||||
inOldHistory = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
inHeader = false;
|
inHeader = false;
|
||||||
|
|
||||||
@@ -179,55 +148,30 @@ jobs:
|
|||||||
let existingContent = '';
|
let existingContent = '';
|
||||||
try {
|
try {
|
||||||
existingContent = await fs.readFile(monthPath, 'utf-8');
|
existingContent = await fs.readFile(monthPath, 'utf-8');
|
||||||
// Normalize line endings to prevent regex issues
|
|
||||||
existingContent = existingContent.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// File doesn't exist
|
// File doesn't exist
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse existing entries into a Map (date -> content) for deduplication
|
// Merge new entries with existing (avoid duplicates)
|
||||||
const allEntries = new Map();
|
const existingDates = new Set();
|
||||||
|
const existingDatePattern = /^## (\d{4}-\d{2}-\d{2})$/gm;
|
||||||
// Helper function to parse entries from content
|
let match;
|
||||||
const parseEntries = (content) => {
|
while ((match = existingDatePattern.exec(existingContent)) !== null) {
|
||||||
const entries = new Map();
|
existingDates.add(match[1]);
|
||||||
const parts = content.split(/(?=^## \d{4}-\d{2}-\d{2}$)/m);
|
|
||||||
for (const part of parts) {
|
|
||||||
const trimmed = part.trim();
|
|
||||||
if (!trimmed) continue;
|
|
||||||
const dateMatch = trimmed.match(/^## (\d{4}-\d{2}-\d{2})/);
|
|
||||||
if (dateMatch) {
|
|
||||||
entries.set(dateMatch[1], trimmed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return entries;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse existing content
|
|
||||||
if (existingContent) {
|
|
||||||
const existingEntries = parseEntries(existingContent);
|
|
||||||
for (const [date, content] of existingEntries) {
|
|
||||||
allEntries.set(date, content);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new entries (existing entries take precedence to avoid overwriting)
|
const newEntries = archiveData[year][month].filter(entry => {
|
||||||
let addedCount = 0;
|
|
||||||
for (const entry of archiveData[year][month]) {
|
|
||||||
const dateMatch = entry.match(/^## (\d{4}-\d{2}-\d{2})/);
|
const dateMatch = entry.match(/^## (\d{4}-\d{2}-\d{2})/);
|
||||||
if (dateMatch && !allEntries.has(dateMatch[1])) {
|
return dateMatch && !existingDates.has(dateMatch[1]);
|
||||||
allEntries.set(dateMatch[1], entry.trim());
|
});
|
||||||
addedCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort entries by date (newest first) and write
|
if (newEntries.length > 0) {
|
||||||
const sortedDates = [...allEntries.keys()].sort().reverse();
|
const allContent = existingContent
|
||||||
const sortedContent = sortedDates.map(date => allEntries.get(date)).join('\n\n');
|
? existingContent + '\n\n' + newEntries.join('\n\n')
|
||||||
|
: newEntries.join('\n\n');
|
||||||
if (addedCount > 0 || !existingContent) {
|
|
||||||
await fs.writeFile(monthPath, sortedContent + '\n', 'utf-8');
|
await fs.writeFile(monthPath, allContent, 'utf-8');
|
||||||
console.log(`Updated: ${monthPath} (${allEntries.size} total entries, +${addedCount} new)`);
|
console.log(`Updated: ${monthPath} (+${newEntries.length} entries)`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -274,8 +218,7 @@ jobs:
|
|||||||
// Count entries in month file
|
// Count entries in month file
|
||||||
let entryCount = 0;
|
let entryCount = 0;
|
||||||
try {
|
try {
|
||||||
let monthContent = await fs.readFile(monthPath, 'utf-8');
|
const monthContent = await fs.readFile(monthPath, 'utf-8');
|
||||||
monthContent = monthContent.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
||||||
entryCount = (monthContent.match(/^## \d{4}-\d{2}-\d{2}$/gm) || []).length;
|
entryCount = (monthContent.match(/^## \d{4}-\d{2}-\d{2}$/gm) || []).length;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
entryCount = (archiveData[year]?.[month] || []).length;
|
entryCount = (archiveData[year]?.[month] || []).length;
|
||||||
|
|||||||
83
.github/workflows/lock-issue.yaml
generated
vendored
83
.github/workflows/lock-issue.yaml
generated
vendored
@@ -18,6 +18,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const daysBeforeLock = 3;
|
const daysBeforeLock = 3;
|
||||||
|
const cutoffDate = new Date('2026-01-27T00:00:00Z');
|
||||||
const lockDate = new Date();
|
const lockDate = new Date();
|
||||||
lockDate.setDate(lockDate.getDate() - daysBeforeLock);
|
lockDate.setDate(lockDate.getDate() - daysBeforeLock);
|
||||||
|
|
||||||
@@ -28,50 +29,48 @@ jobs:
|
|||||||
/dependabot/i
|
/dependabot/i
|
||||||
];
|
];
|
||||||
|
|
||||||
// Search for closed, unlocked issues older than 3 days (paginated, oldest first)
|
// Search for closed, unlocked issues older than 3 days
|
||||||
let page = 1;
|
const issues = await github.rest.search.issuesAndPullRequests({
|
||||||
let totalLocked = 0;
|
q: `repo:${context.repo.owner}/${context.repo.repo} is:closed is:unlocked updated:<${lockDate.toISOString().split('T')[0]}`,
|
||||||
|
per_page: 50
|
||||||
|
});
|
||||||
|
|
||||||
while (true) {
|
console.log(`Found ${issues.data.items.length} issues/PRs to process`);
|
||||||
const issues = await github.rest.search.issuesAndPullRequests({
|
|
||||||
q: `repo:${context.repo.owner}/${context.repo.repo} is:closed is:unlocked updated:<${lockDate.toISOString().split('T')[0]}`,
|
for (const item of issues.data.items) {
|
||||||
sort: 'updated',
|
// Skip excluded items
|
||||||
order: 'asc',
|
const shouldExclude = excludePatterns.some(pattern => pattern.test(item.title));
|
||||||
per_page: 100,
|
if (shouldExclude) {
|
||||||
page: page
|
console.log(`Skipped #${item.number}: "${item.title}" (matches exclude pattern)`);
|
||||||
});
|
continue;
|
||||||
|
|
||||||
if (issues.data.items.length === 0) break;
|
|
||||||
|
|
||||||
console.log(`Page ${page}: ${issues.data.items.length} items (total available: ${issues.data.total_count})`);
|
|
||||||
|
|
||||||
for (const item of issues.data.items) {
|
|
||||||
// Skip excluded items
|
|
||||||
const shouldExclude = excludePatterns.some(pattern => pattern.test(item.title));
|
|
||||||
if (shouldExclude) {
|
|
||||||
console.log(`Skipped #${item.number}: "${item.title}" (matches exclude pattern)`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Lock the issue/PR silently
|
|
||||||
await github.rest.issues.lock({
|
|
||||||
...context.repo,
|
|
||||||
issue_number: item.number,
|
|
||||||
lock_reason: 'resolved'
|
|
||||||
});
|
|
||||||
|
|
||||||
totalLocked++;
|
|
||||||
console.log(`Locked #${item.number} (${item.pull_request ? 'PR' : 'Issue'})`);
|
|
||||||
} catch (error) {
|
|
||||||
console.log(`Failed to lock #${item.number}: ${error.message}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
page++;
|
const createdAt = new Date(item.created_at);
|
||||||
|
const isNew = createdAt >= cutoffDate;
|
||||||
|
|
||||||
// GitHub search API limit: max 10000 results (100 pages * 100) - temporarily increased
|
try {
|
||||||
if (page > 100) break;
|
// Add comment only for new issues (created after 2026-01-27)
|
||||||
|
if (isNew) {
|
||||||
|
const comment = item.pull_request
|
||||||
|
? 'This pull request has been automatically locked. Please open a new issue for related bugs.'
|
||||||
|
: 'This issue has been automatically locked. Please open a new issue for related bugs and reference this issue if needed.';
|
||||||
|
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
...context.repo,
|
||||||
|
issue_number: item.number,
|
||||||
|
body: comment
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock the issue/PR
|
||||||
|
await github.rest.issues.lock({
|
||||||
|
...context.repo,
|
||||||
|
issue_number: item.number,
|
||||||
|
lock_reason: 'resolved'
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Locked #${item.number} (${item.pull_request ? 'PR' : 'Issue'})`);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(`Failed to lock #${item.number}: ${error.message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Total locked: ${totalLocked} issues/PRs`);
|
|
||||||
|
|||||||
458
CHANGELOG.md
458
CHANGELOG.md
@@ -21,14 +21,7 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
|
|||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><h4>February (4 entries)</h4></summary>
|
<summary><h4>January - 27 entries</h4></summary>
|
||||||
|
|
||||||
[View February 2026 Changelog](.github/changelogs/2026/02.md)
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><h4>January (31 entries)</h4></summary>
|
|
||||||
|
|
||||||
[View January 2026 Changelog](.github/changelogs/2026/01.md)
|
[View January 2026 Changelog](.github/changelogs/2026/01.md)
|
||||||
|
|
||||||
@@ -398,100 +391,386 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## 2026-02-04
|
|
||||||
|
|
||||||
### 🆕 New Scripts
|
|
||||||
|
|
||||||
- Wishlist ([#11527](https://github.com/community-scripts/ProxmoxVE/pull/11527))
|
<details>
|
||||||
- WriteFreely ([#11524](https://github.com/community-scripts/ProxmoxVE/pull/11524))
|
<summary><h2>📜 History</h2></summary>
|
||||||
|
|
||||||
### 🚀 Updated Scripts
|
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
<details>
|
||||||
|
<summary><h3>2026</h3></summary>
|
||||||
|
|
||||||
- Immich: pin version to 2.5.3 [@vhsdream](https://github.com/vhsdream) ([#11515](https://github.com/community-scripts/ProxmoxVE/pull/11515))
|
|
||||||
|
|
||||||
### 💾 Core
|
<details>
|
||||||
|
<summary><h4>January (31 entries)</h4></summary>
|
||||||
|
|
||||||
- #### ✨ New Features
|
[View January 2026 Changelog](.github/changelogs/2026/01.md)
|
||||||
|
|
||||||
- core: create vm-core.func from dev [@MickLesk](https://github.com/MickLesk) ([#11528](https://github.com/community-scripts/ProxmoxVE/pull/11528))
|
</details>
|
||||||
|
|
||||||
### 🧰 Tools
|
</details>
|
||||||
|
|
||||||
- #### ✨ New Features
|
<details>
|
||||||
|
<summary><h3>2025</h3></summary>
|
||||||
|
|
||||||
- [ADDON] Immich Public Proxy addon [@vhsdream](https://github.com/vhsdream) ([#11518](https://github.com/community-scripts/ProxmoxVE/pull/11518))
|
|
||||||
|
|
||||||
### 🌐 Website
|
<details>
|
||||||
|
<summary><h4>December (31 entries)</h4></summary>
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
[View December 2025 Changelog](.github/changelogs/2025/12.md)
|
||||||
|
|
||||||
- fix(frontend): implement weighted search scoring for command menu [@ls-root](https://github.com/ls-root) ([#11534](https://github.com/community-scripts/ProxmoxVE/pull/11534))
|
</details>
|
||||||
|
|
||||||
## 2026-02-03
|
<details>
|
||||||
|
<summary><h4>November (29 entries)</h4></summary>
|
||||||
|
|
||||||
### 🆕 New Scripts
|
[View November 2025 Changelog](.github/changelogs/2025/11.md)
|
||||||
|
|
||||||
- Wealthfolio ([#11511](https://github.com/community-scripts/ProxmoxVE/pull/11511))
|
</details>
|
||||||
|
|
||||||
### 🚀 Updated Scripts
|
<details>
|
||||||
|
<summary><h4>October (30 entries)</h4></summary>
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
[View October 2025 Changelog](.github/changelogs/2025/10.md)
|
||||||
|
|
||||||
- [FIX] Shelfmark: unpin Chromium version [@vhsdream](https://github.com/vhsdream) ([#11505](https://github.com/community-scripts/ProxmoxVE/pull/11505))
|
</details>
|
||||||
|
|
||||||
- #### ✨ New Features
|
<details>
|
||||||
|
<summary><h4>September (29 entries)</h4></summary>
|
||||||
|
|
||||||
- [FEAT] Scanopy: automatically update integrated daemon [@vhsdream](https://github.com/vhsdream) ([#11506](https://github.com/community-scripts/ProxmoxVE/pull/11506))
|
[View September 2025 Changelog](.github/changelogs/2025/09.md)
|
||||||
|
|
||||||
### 💾 Core
|
</details>
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
<details>
|
||||||
|
<summary><h4>August (30 entries)</h4></summary>
|
||||||
|
|
||||||
- [FIX] tools.func: trim spaces in app_lc when checking for gh release [@vhsdream](https://github.com/vhsdream) ([#11512](https://github.com/community-scripts/ProxmoxVE/pull/11512))
|
[View August 2025 Changelog](.github/changelogs/2025/08.md)
|
||||||
|
|
||||||
### 🌐 Website
|
</details>
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
<details>
|
||||||
|
<summary><h4>July (29 entries)</h4></summary>
|
||||||
|
|
||||||
- fix(frontend): decouple table pagination from summary fetching [@ls-root](https://github.com/ls-root) ([#11495](https://github.com/community-scripts/ProxmoxVE/pull/11495))
|
[View July 2025 Changelog](.github/changelogs/2025/07.md)
|
||||||
|
|
||||||
## 2026-02-02
|
</details>
|
||||||
|
|
||||||
### 🆕 New Scripts
|
<details>
|
||||||
|
<summary><h4>June (29 entries)</h4></summary>
|
||||||
|
|
||||||
- rustypaste | Alpine-rustypaste ([#11457](https://github.com/community-scripts/ProxmoxVE/pull/11457))
|
[View June 2025 Changelog](.github/changelogs/2025/06.md)
|
||||||
- KitchenOwl ([#11453](https://github.com/community-scripts/ProxmoxVE/pull/11453))
|
|
||||||
|
|
||||||
### 🚀 Updated Scripts
|
</details>
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
<details>
|
||||||
|
<summary><h4>May (30 entries)</h4></summary>
|
||||||
|
|
||||||
- Grist: Update dependencies [@tremor021](https://github.com/tremor021) ([#11489](https://github.com/community-scripts/ProxmoxVE/pull/11489))
|
[View May 2025 Changelog](.github/changelogs/2025/05.md)
|
||||||
- Allow "downgrade" of libigdgmm12 [@vhsdream](https://github.com/vhsdream) ([#11478](https://github.com/community-scripts/ProxmoxVE/pull/11478))
|
|
||||||
- Disable NPM install and update due to OpenResty SHA-1 signature issues [@MickLesk](https://github.com/MickLesk) ([#11471](https://github.com/community-scripts/ProxmoxVE/pull/11471))
|
|
||||||
|
|
||||||
- #### ✨ New Features
|
</details>
|
||||||
|
|
||||||
- Refactor: Forgejo & readeck - migrate to codeberg functions [@MickLesk](https://github.com/MickLesk) ([#11460](https://github.com/community-scripts/ProxmoxVE/pull/11460))
|
<details>
|
||||||
|
<summary><h4>April (25 entries)</h4></summary>
|
||||||
|
|
||||||
- #### 💥 Breaking Changes
|
[View April 2025 Changelog](.github/changelogs/2025/04.md)
|
||||||
|
|
||||||
- [FIX] Scanopy: remove daemon build [@vhsdream](https://github.com/vhsdream) ([#11444](https://github.com/community-scripts/ProxmoxVE/pull/11444))
|
</details>
|
||||||
|
|
||||||
- #### 🔧 Refactor
|
<details>
|
||||||
|
<summary><h4>March (30 entries)</h4></summary>
|
||||||
|
|
||||||
- Refactor: Vaultwarden [@MickLesk](https://github.com/MickLesk) ([#11445](https://github.com/community-scripts/ProxmoxVE/pull/11445))
|
[View March 2025 Changelog](.github/changelogs/2025/03.md)
|
||||||
- various scripts: use ensure_dependencies instead of apt [@MickLesk](https://github.com/MickLesk) ([#11463](https://github.com/community-scripts/ProxmoxVE/pull/11463))
|
|
||||||
|
|
||||||
### 🌐 Website
|
</details>
|
||||||
|
|
||||||
- cleanup(frontend): remove unused /category-view route [@ls-root](https://github.com/ls-root) ([#11461](https://github.com/community-scripts/ProxmoxVE/pull/11461))
|
<details>
|
||||||
|
<summary><h4>February (26 entries)</h4></summary>
|
||||||
|
|
||||||
- #### ✨ New Features
|
[View February 2025 Changelog](.github/changelogs/2025/02.md)
|
||||||
|
|
||||||
- feat(frontend): preview tab [@ls-root](https://github.com/ls-root) ([#11475](https://github.com/community-scripts/ProxmoxVE/pull/11475))
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>January (27 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View January 2025 Changelog](.github/changelogs/2025/01.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h3>2024</h3></summary>
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>December (22 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View December 2024 Changelog](.github/changelogs/2024/12.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>November (15 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View November 2024 Changelog](.github/changelogs/2024/11.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>October (9 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View October 2024 Changelog](.github/changelogs/2024/10.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>September (1 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View September 2024 Changelog](.github/changelogs/2024/09.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>August (2 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View August 2024 Changelog](.github/changelogs/2024/08.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>July (0 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View July 2024 Changelog](.github/changelogs/2024/07.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>June (8 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View June 2024 Changelog](.github/changelogs/2024/06.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>May (16 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View May 2024 Changelog](.github/changelogs/2024/05.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>April (14 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View April 2024 Changelog](.github/changelogs/2024/04.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>March (5 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View March 2024 Changelog](.github/changelogs/2024/03.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>February (9 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View February 2024 Changelog](.github/changelogs/2024/02.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>January (9 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View January 2024 Changelog](.github/changelogs/2024/01.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h3>2023</h3></summary>
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>December (3 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View December 2023 Changelog](.github/changelogs/2023/12.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>November (3 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View November 2023 Changelog](.github/changelogs/2023/11.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>October (7 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View October 2023 Changelog](.github/changelogs/2023/10.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>September (10 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View September 2023 Changelog](.github/changelogs/2023/09.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>August (7 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View August 2023 Changelog](.github/changelogs/2023/08.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>July (5 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View July 2023 Changelog](.github/changelogs/2023/07.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>June (5 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View June 2023 Changelog](.github/changelogs/2023/06.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>May (8 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View May 2023 Changelog](.github/changelogs/2023/05.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>April (8 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View April 2023 Changelog](.github/changelogs/2023/04.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>March (8 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View March 2023 Changelog](.github/changelogs/2023/03.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>February (6 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View February 2023 Changelog](.github/changelogs/2023/02.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>January (15 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View January 2023 Changelog](.github/changelogs/2023/01.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h3>2022</h3></summary>
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>December (7 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View December 2022 Changelog](.github/changelogs/2022/12.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>November (7 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View November 2022 Changelog](.github/changelogs/2022/11.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>October (2 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View October 2022 Changelog](.github/changelogs/2022/10.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>September (9 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View September 2022 Changelog](.github/changelogs/2022/09.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>August (7 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View August 2022 Changelog](.github/changelogs/2022/08.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>July (10 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View July 2022 Changelog](.github/changelogs/2022/07.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>June (1 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View June 2022 Changelog](.github/changelogs/2022/06.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>May (8 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View May 2022 Changelog](.github/changelogs/2022/05.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>April (13 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View April 2022 Changelog](.github/changelogs/2022/04.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>March (20 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View March 2022 Changelog](.github/changelogs/2022/03.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>February (15 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View February 2022 Changelog](.github/changelogs/2022/02.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>January (3 entries)</h4></summary>
|
||||||
|
|
||||||
|
[View January 2022 Changelog](.github/changelogs/2022/01.md)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
## 2026-02-01
|
## 2026-02-01
|
||||||
|
|
||||||
@@ -501,7 +780,6 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
|
|||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
- #### 🐞 Bug Fixes
|
||||||
|
|
||||||
- 2fauth: export PHP_VERSION for nginx config [@MickLesk](https://github.com/MickLesk) ([#11441](https://github.com/community-scripts/ProxmoxVE/pull/11441))
|
|
||||||
- Prometheus Paperless NGX Exporter: Set correct binary path in systemd unit file [@andygrunwald](https://github.com/andygrunwald) ([#11438](https://github.com/community-scripts/ProxmoxVE/pull/11438))
|
- Prometheus Paperless NGX Exporter: Set correct binary path in systemd unit file [@andygrunwald](https://github.com/andygrunwald) ([#11438](https://github.com/community-scripts/ProxmoxVE/pull/11438))
|
||||||
- tracearr: install/update new prestart script from upstream [@durzo](https://github.com/durzo) ([#11433](https://github.com/community-scripts/ProxmoxVE/pull/11433))
|
- tracearr: install/update new prestart script from upstream [@durzo](https://github.com/durzo) ([#11433](https://github.com/community-scripts/ProxmoxVE/pull/11433))
|
||||||
- n8n: Fix dependencies [@tremor021](https://github.com/tremor021) ([#11429](https://github.com/community-scripts/ProxmoxVE/pull/11429))
|
- n8n: Fix dependencies [@tremor021](https://github.com/tremor021) ([#11429](https://github.com/community-scripts/ProxmoxVE/pull/11429))
|
||||||
@@ -510,7 +788,6 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
|
|||||||
|
|
||||||
- #### ✨ New Features
|
- #### ✨ New Features
|
||||||
|
|
||||||
- tools.func: add codeberg functions & autocaliweb: migrate from GitHub to Codeberg [@MickLesk](https://github.com/MickLesk) ([#11440](https://github.com/community-scripts/ProxmoxVE/pull/11440))
|
|
||||||
- Immich Refactor #2 [@vhsdream](https://github.com/vhsdream) ([#11375](https://github.com/community-scripts/ProxmoxVE/pull/11375))
|
- Immich Refactor #2 [@vhsdream](https://github.com/vhsdream) ([#11375](https://github.com/community-scripts/ProxmoxVE/pull/11375))
|
||||||
|
|
||||||
- #### 🔧 Refactor
|
- #### 🔧 Refactor
|
||||||
@@ -1282,3 +1559,64 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
|
|||||||
- #### 🔧 Refactor
|
- #### 🔧 Refactor
|
||||||
|
|
||||||
- Refactor: IP-Tag (Multiple IP / Performance / Execution Time) [@MickLesk](https://github.com/MickLesk) ([#10558](https://github.com/community-scripts/ProxmoxVE/pull/10558))
|
- Refactor: IP-Tag (Multiple IP / Performance / Execution Time) [@MickLesk](https://github.com/MickLesk) ([#10558](https://github.com/community-scripts/ProxmoxVE/pull/10558))
|
||||||
|
|
||||||
|
## 2026-01-04
|
||||||
|
|
||||||
|
### 🚀 Updated Scripts
|
||||||
|
|
||||||
|
- #### 🐞 Bug Fixes
|
||||||
|
|
||||||
|
- PocketID: Update PocketID for 2.x [@tremor021](https://github.com/tremor021) ([#10506](https://github.com/community-scripts/ProxmoxVE/pull/10506))
|
||||||
|
- fix: reitti: nginx [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10511](https://github.com/community-scripts/ProxmoxVE/pull/10511))
|
||||||
|
- MagicMirror: bump to nodejs 24 [@MickLesk](https://github.com/MickLesk) ([#10534](https://github.com/community-scripts/ProxmoxVE/pull/10534))
|
||||||
|
|
||||||
|
- #### 🔧 Refactor
|
||||||
|
|
||||||
|
- Refactor: SFTPGo [@tremor021](https://github.com/tremor021) ([#10518](https://github.com/community-scripts/ProxmoxVE/pull/10518))
|
||||||
|
- Refactor: Pelican Wings [@tremor021](https://github.com/tremor021) ([#10517](https://github.com/community-scripts/ProxmoxVE/pull/10517))
|
||||||
|
- Refactor: Pelican Panel [@tremor021](https://github.com/tremor021) ([#10516](https://github.com/community-scripts/ProxmoxVE/pull/10516))
|
||||||
|
- Refactor: Audiobookshelf [@tremor021](https://github.com/tremor021) ([#10519](https://github.com/community-scripts/ProxmoxVE/pull/10519))
|
||||||
|
|
||||||
|
### 💾 Core
|
||||||
|
|
||||||
|
- #### 🐞 Bug Fixes
|
||||||
|
|
||||||
|
- Export IPV6_METHOD to trigger verb_ip6() function [@remz1337](https://github.com/remz1337) ([#10538](https://github.com/community-scripts/ProxmoxVE/pull/10538))
|
||||||
|
|
||||||
|
### 🌐 Website
|
||||||
|
|
||||||
|
- #### 📝 Script Information
|
||||||
|
|
||||||
|
- Prowlarr: Update config_path [@tremor021](https://github.com/tremor021) ([#10504](https://github.com/community-scripts/ProxmoxVE/pull/10504))
|
||||||
|
|
||||||
|
## 2026-01-03
|
||||||
|
|
||||||
|
### 🚀 Updated Scripts
|
||||||
|
|
||||||
|
- #### 🐞 Bug Fixes
|
||||||
|
|
||||||
|
- Fix ownership and permissions for InvoiceNinja setup [@twinzdragonz](https://github.com/twinzdragonz) ([#10298](https://github.com/community-scripts/ProxmoxVE/pull/10298))
|
||||||
|
- Fix headscale Caddyfile to pass non-API URLs [@IlyaSemenov](https://github.com/IlyaSemenov) ([#10493](https://github.com/community-scripts/ProxmoxVE/pull/10493))
|
||||||
|
|
||||||
|
### 💾 Core
|
||||||
|
|
||||||
|
- #### 🔧 Refactor
|
||||||
|
|
||||||
|
- [core]: Preserve log files [@tremor021](https://github.com/tremor021) ([#10509](https://github.com/community-scripts/ProxmoxVE/pull/10509))
|
||||||
|
|
||||||
|
### ❔ Uncategorized
|
||||||
|
|
||||||
|
- Wireguard: Update WGDashboard notes URL to the new link [@tremor021](https://github.com/tremor021) ([#10496](https://github.com/community-scripts/ProxmoxVE/pull/10496))
|
||||||
|
- InvoiceNinja: Update database credentias information [@tremor021](https://github.com/tremor021) ([#10497](https://github.com/community-scripts/ProxmoxVE/pull/10497))
|
||||||
|
|
||||||
|
## 2026-01-02
|
||||||
|
|
||||||
|
### 🚀 Updated Scripts
|
||||||
|
|
||||||
|
- #### 🐞 Bug Fixes
|
||||||
|
|
||||||
|
- Fix Intel Level Zero package conflict on Debian 13 [@Copilot](https://github.com/Copilot) ([#10467](https://github.com/community-scripts/ProxmoxVE/pull/10467))
|
||||||
|
|
||||||
|
### ❔ Uncategorized
|
||||||
|
|
||||||
|
- Extend guidance for changing the immich upload location for #10447 [@jshprentz](https://github.com/jshprentz) ([#10475](https://github.com/community-scripts/ProxmoxVE/pull/10475))
|
||||||
@@ -27,7 +27,10 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
ensure_dependencies memcached libmemcached-tools
|
if ! command -v memcached >/dev/null 2>&1; then
|
||||||
|
$STD apt update
|
||||||
|
$STD apt install -y memcached libmemcached-tools
|
||||||
|
fi
|
||||||
if check_for_gh_release "adventurelog" "seanmorley15/adventurelog"; then
|
if check_for_gh_release "adventurelog" "seanmorley15/adventurelog"; then
|
||||||
msg_info "Stopping Services"
|
msg_info "Stopping Services"
|
||||||
systemctl stop adventurelog-backend
|
systemctl stop adventurelog-backend
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
#!/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/orhun/rustypaste
|
|
||||||
|
|
||||||
APP="Alpine-RustyPaste"
|
|
||||||
var_tags="${var_tags:-alpine;pastebin;storage}"
|
|
||||||
var_cpu="${var_cpu:-1}"
|
|
||||||
var_ram="${var_ram:-256}"
|
|
||||||
var_disk="${var_disk:-4}"
|
|
||||||
var_os="${var_os:-alpine}"
|
|
||||||
var_version="${var_version:-3.23}"
|
|
||||||
var_unprivileged="${var_unprivileged:-1}"
|
|
||||||
|
|
||||||
header_info "$APP"
|
|
||||||
variables
|
|
||||||
color
|
|
||||||
catch_errors
|
|
||||||
|
|
||||||
function update_script() {
|
|
||||||
header_info
|
|
||||||
check_container_storage
|
|
||||||
check_container_resources
|
|
||||||
|
|
||||||
if ! apk info -e rustypaste >/dev/null 2>&1; then
|
|
||||||
msg_error "No ${APP} Installation Found!"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
msg_info "Updating RustyPaste"
|
|
||||||
$STD apk update
|
|
||||||
$STD apk upgrade rustypaste --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community
|
|
||||||
msg_ok "Updated RustyPaste"
|
|
||||||
|
|
||||||
msg_info "Restarting Services"
|
|
||||||
$STD rc-service rustypaste restart
|
|
||||||
msg_ok "Restarted Services"
|
|
||||||
msg_ok "Updated successfully!"
|
|
||||||
exit
|
|
||||||
}
|
|
||||||
|
|
||||||
start
|
|
||||||
build_container
|
|
||||||
description
|
|
||||||
|
|
||||||
msg_ok "Completed successfully!\n"
|
|
||||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
|
||||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
|
||||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}"
|
|
||||||
@@ -31,7 +31,11 @@ function update_script() {
|
|||||||
NODE_VERSION="22" NODE_MODULE="@postlight/parser@latest,single-file-cli@latest" setup_nodejs
|
NODE_VERSION="22" NODE_MODULE="@postlight/parser@latest,single-file-cli@latest" setup_nodejs
|
||||||
PYTHON_VERSION="3.13" setup_uv
|
PYTHON_VERSION="3.13" setup_uv
|
||||||
|
|
||||||
ensure_dependencies chromium
|
if ! dpkg -l | grep -q "^ii chromium "; then
|
||||||
|
msg_info "Installing System Dependencies"
|
||||||
|
$STD apt-get install -y chromium
|
||||||
|
msg_ok "Installed System Dependencies"
|
||||||
|
fi
|
||||||
|
|
||||||
msg_info "Stopping Service"
|
msg_info "Stopping Service"
|
||||||
systemctl stop archivebox
|
systemctl stop archivebox
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
|
|||||||
# Copyright (c) 2021-2026 community-scripts ORG
|
# Copyright (c) 2021-2026 community-scripts ORG
|
||||||
# Author: vhsdream
|
# Author: vhsdream
|
||||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||||
# Source: https://codeberg.org/gelbphoenix/autocaliweb
|
# Source: https://github.com/gelbphoenix/autocaliweb
|
||||||
|
|
||||||
APP="Autocaliweb"
|
APP="Autocaliweb"
|
||||||
var_tags="${var_tags:-ebooks}"
|
var_tags="${var_tags:-ebooks}"
|
||||||
@@ -30,8 +30,8 @@ function update_script() {
|
|||||||
|
|
||||||
setup_uv
|
setup_uv
|
||||||
|
|
||||||
RELEASE=$(get_latest_codeberg_release "gelbphoenix/autocaliweb")
|
RELEASE=$(get_latest_github_release "gelbphoenix/autocaliweb")
|
||||||
if check_for_codeberg_release "autocaliweb" "gelbphoenix/autocaliweb"; then
|
if check_for_gh_release "autocaliweb" "gelbphoenix/autocaliweb"; then
|
||||||
msg_info "Stopping Services"
|
msg_info "Stopping Services"
|
||||||
systemctl stop autocaliweb metadata-change-detector acw-ingest-service acw-auto-zipper
|
systemctl stop autocaliweb metadata-change-detector acw-ingest-service acw-auto-zipper
|
||||||
msg_ok "Stopped Services"
|
msg_ok "Stopped Services"
|
||||||
@@ -39,7 +39,7 @@ function update_script() {
|
|||||||
INSTALL_DIR="/opt/autocaliweb"
|
INSTALL_DIR="/opt/autocaliweb"
|
||||||
export VIRTUAL_ENV="${INSTALL_DIR}/venv"
|
export VIRTUAL_ENV="${INSTALL_DIR}/venv"
|
||||||
$STD tar -cf ~/autocaliweb_bkp.tar "$INSTALL_DIR"/{metadata_change_logs,dirs.json,.env,scripts/ingest_watcher.sh,scripts/auto_zipper_wrapper.sh,scripts/metadata_change_detector_wrapper.sh}
|
$STD tar -cf ~/autocaliweb_bkp.tar "$INSTALL_DIR"/{metadata_change_logs,dirs.json,.env,scripts/ingest_watcher.sh,scripts/auto_zipper_wrapper.sh,scripts/metadata_change_detector_wrapper.sh}
|
||||||
fetch_and_deploy_codeberg_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb"
|
fetch_and_deploy_gh_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb"
|
||||||
|
|
||||||
msg_info "Updating Autocaliweb"
|
msg_info "Updating Autocaliweb"
|
||||||
cd "$INSTALL_DIR"
|
cd "$INSTALL_DIR"
|
||||||
|
|||||||
@@ -29,7 +29,12 @@ function update_script() {
|
|||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ensure_dependencies libjpeg-dev
|
if ! dpkg -s libjpeg-dev >/dev/null 2>&1; then
|
||||||
|
msg_info "Installing Dependencies"
|
||||||
|
$STD apt-get update
|
||||||
|
$STD apt-get install -y libjpeg-dev
|
||||||
|
msg_ok "Updated Dependencies"
|
||||||
|
fi
|
||||||
|
|
||||||
NODE_VERSION="24" setup_nodejs
|
NODE_VERSION="24" setup_nodejs
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,12 @@ function update_script() {
|
|||||||
systemctl stop commafeed
|
systemctl stop commafeed
|
||||||
msg_ok "Stopped Service"
|
msg_ok "Stopped Service"
|
||||||
|
|
||||||
ensure_dependencies rsync
|
if ! [[ $(dpkg -s rsync 2>/dev/null) ]]; then
|
||||||
|
msg_info "Installing Dependencies"
|
||||||
|
$STD apt update
|
||||||
|
$STD apt install -y rsync
|
||||||
|
msg_ok "Installed Dependencies"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -d /opt/commafeed/data ] && [ "$(ls -A /opt/commafeed/data)" ]; then
|
if [ -d /opt/commafeed/data ] && [ "$(ls -A /opt/commafeed/data)" ]; then
|
||||||
msg_info "Backing up existing data"
|
msg_info "Backing up existing data"
|
||||||
|
|||||||
@@ -44,7 +44,10 @@ function update_script() {
|
|||||||
NODE_VERSION="22" setup_nodejs
|
NODE_VERSION="22" setup_nodejs
|
||||||
if check_for_gh_release "cronicle" "jhuckaby/Cronicle"; then
|
if check_for_gh_release "cronicle" "jhuckaby/Cronicle"; then
|
||||||
msg_info "Installing Dependencies"
|
msg_info "Installing Dependencies"
|
||||||
ensure_dependencies git build-essential ca-certificates
|
$STD apt install -y \
|
||||||
|
git \
|
||||||
|
build-essential \
|
||||||
|
ca-certificates
|
||||||
msg_ok "Installed Dependencies"
|
msg_ok "Installed Dependencies"
|
||||||
|
|
||||||
NODE_VERSION="22" setup_nodejs
|
NODE_VERSION="22" setup_nodejs
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ function update_script() {
|
|||||||
systemctl reload nginx
|
systemctl reload nginx
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ensure_dependencies vlc-bin vlc-plugin-base
|
if ! dpkg -s vlc-bin vlc-plugin-base &>/dev/null; then
|
||||||
|
$STD apt update && $STD apt install -y vlc-bin vlc-plugin-base
|
||||||
|
fi
|
||||||
|
|
||||||
if check_for_gh_release "Dispatcharr" "Dispatcharr/Dispatcharr"; then
|
if check_for_gh_release "Dispatcharr" "Dispatcharr/Dispatcharr"; then
|
||||||
msg_info "Stopping Services"
|
msg_info "Stopping Services"
|
||||||
|
|||||||
@@ -27,28 +27,35 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
if check_for_codeberg_release "forgejo" "forgejo/forgejo"; then
|
msg_info "Stopping Service"
|
||||||
msg_info "Stopping Service"
|
systemctl stop forgejo
|
||||||
systemctl stop forgejo
|
msg_ok "Stopped Service"
|
||||||
msg_ok "Stopped Service"
|
|
||||||
|
|
||||||
fetch_and_deploy_codeberg_release "forgejo" "forgejo/forgejo" "singlefile" "latest" "/opt/forgejo" "forgejo-*-linux-amd64"
|
msg_info "Updating ${APP}"
|
||||||
ln -sf /opt/forgejo/forgejo /usr/local/bin/forgejo
|
RELEASE=$(curl -fsSL https://codeberg.org/api/v1/repos/forgejo/forgejo/releases/latest | grep -oP '"tag_name":\s*"\K[^"]+' | sed 's/^v//')
|
||||||
|
curl -fsSL "https://codeberg.org/forgejo/forgejo/releases/download/v${RELEASE}/forgejo-${RELEASE}-linux-amd64" -o "forgejo-$RELEASE-linux-amd64"
|
||||||
|
rm -rf /opt/forgejo/*
|
||||||
|
cp -r forgejo-$RELEASE-linux-amd64 /opt/forgejo/forgejo-$RELEASE-linux-amd64
|
||||||
|
chmod +x /opt/forgejo/forgejo-$RELEASE-linux-amd64
|
||||||
|
ln -sf /opt/forgejo/forgejo-$RELEASE-linux-amd64 /usr/local/bin/forgejo
|
||||||
|
msg_ok "Updated ${APP}"
|
||||||
|
|
||||||
if grep -q "GITEA_WORK_DIR" /etc/systemd/system/forgejo.service; then
|
msg_info "Cleaning"
|
||||||
msg_info "Updating Service File"
|
rm -rf forgejo-$RELEASE-linux-amd64
|
||||||
sed -i "s/GITEA_WORK_DIR/FORGEJO_WORK_DIR/g" /etc/systemd/system/forgejo.service
|
msg_ok "Cleaned"
|
||||||
systemctl daemon-reload
|
|
||||||
msg_ok "Updated Service File"
|
|
||||||
fi
|
|
||||||
|
|
||||||
msg_info "Starting Service"
|
# Fix env var from older version of community script
|
||||||
systemctl start forgejo
|
if grep -q "GITEA_WORK_DIR" /etc/systemd/system/forgejo.service; then
|
||||||
msg_ok "Started Service"
|
msg_info "Updating Service File"
|
||||||
msg_ok "Updated successfully!"
|
sed -i "s/GITEA_WORK_DIR/FORGEJO_WORK_DIR/g" /etc/systemd/system/forgejo.service
|
||||||
else
|
systemctl daemon-reload
|
||||||
msg_ok "No update required. ${APP} is already at the latest version."
|
msg_ok "Updated Service File"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
msg_info "Starting Service"
|
||||||
|
systemctl start forgejo
|
||||||
|
msg_ok "Started Service"
|
||||||
|
msg_ok "Updated successfully!"
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ function update_script() {
|
|||||||
msg_error "Project directory does not exist: $PROJECT_DIR"
|
msg_error "Project directory does not exist: $PROJECT_DIR"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
ensure_dependencies git
|
if ! command -v git &>/dev/null; then
|
||||||
|
$STD apt install -y git
|
||||||
|
fi
|
||||||
|
|
||||||
msg_info "Stopping service $SERVICE_NAME"
|
msg_info "Stopping service $SERVICE_NAME"
|
||||||
systemctl stop "$SERVICE_NAME"
|
systemctl stop "$SERVICE_NAME"
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ function update_script() {
|
|||||||
curl -fsSL "https://packages.graylog2.org/repo/packages/graylog-7.0-repository_latest.deb" -o "graylog-7.0-repository_latest.deb"
|
curl -fsSL "https://packages.graylog2.org/repo/packages/graylog-7.0-repository_latest.deb" -o "graylog-7.0-repository_latest.deb"
|
||||||
$STD dpkg -i graylog-7.0-repository_latest.deb
|
$STD dpkg -i graylog-7.0-repository_latest.deb
|
||||||
$STD apt update
|
$STD apt update
|
||||||
ensure_dependencies graylog-server graylog-datanode
|
$STD apt install -y graylog-server graylog-datanode
|
||||||
rm -f graylog-7.0-repository_latest.deb
|
rm -f graylog-7.0-repository_latest.deb
|
||||||
msg_ok "Updated Graylog"
|
msg_ok "Updated Graylog"
|
||||||
elif dpkg --compare-versions "$CURRENT_VERSION" ge "7.0"; then
|
elif dpkg --compare-versions "$CURRENT_VERSION" ge "7.0"; then
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ function update_script() {
|
|||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ensure_dependencies git
|
|
||||||
|
|
||||||
if check_for_gh_release "grist" "gristlabs/grist-core"; then
|
if check_for_gh_release "grist" "gristlabs/grist-core"; then
|
||||||
msg_info "Stopping Service"
|
msg_info "Stopping Service"
|
||||||
systemctl stop grist
|
systemctl stop grist
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
___ __ _ ____ __ ____ __
|
|
||||||
/ | / /___ (_)___ ___ / __ \__ _______/ /___ __/ __ \____ ______/ /____
|
|
||||||
/ /| | / / __ \/ / __ \/ _ \______/ /_/ / / / / ___/ __/ / / / /_/ / __ `/ ___/ __/ _ \
|
|
||||||
/ ___ |/ / /_/ / / / / / __/_____/ _, _/ /_/ (__ ) /_/ /_/ / ____/ /_/ (__ ) /_/ __/
|
|
||||||
/_/ |_/_/ .___/_/_/ /_/\___/ /_/ |_|\__,_/____/\__/\__, /_/ \__,_/____/\__/\___/
|
|
||||||
/_/ /____/
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
__ __ _ __ __ ____ __
|
|
||||||
/ //_/(_) /______/ /_ ___ ____ / __ \_ __/ /
|
|
||||||
/ ,< / / __/ ___/ __ \/ _ \/ __ \/ / / / | /| / / /
|
|
||||||
/ /| |/ / /_/ /__/ / / / __/ / / / /_/ /| |/ |/ / /
|
|
||||||
/_/ |_/_/\__/\___/_/ /_/\___/_/ /_/\____/ |__/|__/_/
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
__ __
|
|
||||||
_______ _______/ /___ ______ ____ ______/ /____
|
|
||||||
/ ___/ / / / ___/ __/ / / / __ \/ __ `/ ___/ __/ _ \
|
|
||||||
/ / / /_/ (__ ) /_/ /_/ / /_/ / /_/ (__ ) /_/ __/
|
|
||||||
/_/ \__,_/____/\__/\__, / .___/\__,_/____/\__/\___/
|
|
||||||
/____/_/
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
_ __ ____ __ ____ ___
|
|
||||||
| | / /__ ____ _/ / /_/ /_ / __/___ / (_)___
|
|
||||||
| | /| / / _ \/ __ `/ / __/ __ \/ /_/ __ \/ / / __ \
|
|
||||||
| |/ |/ / __/ /_/ / / /_/ / / / __/ /_/ / / / /_/ /
|
|
||||||
|__/|__/\___/\__,_/_/\__/_/ /_/_/ \____/_/_/\____/
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
_ ___ __ ___ __
|
|
||||||
| | / (_)____/ /_ / (_)____/ /_
|
|
||||||
| | /| / / / ___/ __ \/ / / ___/ __/
|
|
||||||
| |/ |/ / (__ ) / / / / (__ ) /_
|
|
||||||
|__/|__/_/____/_/ /_/_/_/____/\__/
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
_ __ _ __ ______ __
|
|
||||||
| | / /____(_) /____ / ____/_______ ___ / /_ __
|
|
||||||
| | /| / / ___/ / __/ _ \/ /_ / ___/ _ \/ _ \/ / / / /
|
|
||||||
| |/ |/ / / / / /_/ __/ __/ / / / __/ __/ / /_/ /
|
|
||||||
|__/|__/_/ /_/\__/\___/_/ /_/ \___/\___/_/\__, /
|
|
||||||
/____/
|
|
||||||
@@ -30,7 +30,14 @@ function update_script() {
|
|||||||
|
|
||||||
get_lxc_ip
|
get_lxc_ip
|
||||||
NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs
|
NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs
|
||||||
ensure_dependencies jq
|
if ! command -v jq &>/dev/null; then
|
||||||
|
$STD msg_info "Installing jq..."
|
||||||
|
$STD apt-get update -qq &>/dev/null
|
||||||
|
$STD apt-get install -y jq &>/dev/null || {
|
||||||
|
msg_error "Failed to install jq"
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
if check_for_gh_release "homepage" "gethomepage/homepage"; then
|
if check_for_gh_release "homepage" "gethomepage/homepage"; then
|
||||||
msg_info "Stopping service"
|
msg_info "Stopping service"
|
||||||
|
|||||||
23
ct/immich.sh
23
ct/immich.sh
@@ -36,6 +36,10 @@ function update_script() {
|
|||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
setup_uv
|
||||||
|
PNPM_VERSION="$(curl -fsSL "https://raw.githubusercontent.com/immich-app/immich/refs/heads/main/package.json" | jq -r '.packageManager | split("@")[1]')"
|
||||||
|
NODE_VERSION="24" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs
|
||||||
|
|
||||||
if [[ ! -f /etc/apt/preferences.d/preferences ]]; then
|
if [[ ! -f /etc/apt/preferences.d/preferences ]]; then
|
||||||
msg_info "Adding Debian Testing repo"
|
msg_info "Adding Debian Testing repo"
|
||||||
sed -i 's/ trixie-updates/ trixie-updates testing/g' /etc/apt/sources.list.d/debian.sources
|
sed -i 's/ trixie-updates/ trixie-updates testing/g' /etc/apt/sources.list.d/debian.sources
|
||||||
@@ -63,7 +67,8 @@ EOF
|
|||||||
msg_info "Installing Mise"
|
msg_info "Installing Mise"
|
||||||
curl -fSs https://mise.jdx.dev/gpg-key.pub | tee /etc/apt/keyrings/mise-archive-keyring.pub 1>/dev/null
|
curl -fSs https://mise.jdx.dev/gpg-key.pub | tee /etc/apt/keyrings/mise-archive-keyring.pub 1>/dev/null
|
||||||
echo "deb [signed-by=/etc/apt/keyrings/mise-archive-keyring.pub arch=amd64] https://mise.jdx.dev/deb stable main" >/etc/apt/sources.list.d/mise.list
|
echo "deb [signed-by=/etc/apt/keyrings/mise-archive-keyring.pub arch=amd64] https://mise.jdx.dev/deb stable main" >/etc/apt/sources.list.d/mise.list
|
||||||
ensure_dependencies mise
|
$STD apt update
|
||||||
|
$STD apt install -y mise
|
||||||
msg_ok "Installed Mise"
|
msg_ok "Installed Mise"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -84,7 +89,7 @@ EOF
|
|||||||
curl -fsSLO "$url"
|
curl -fsSLO "$url"
|
||||||
done
|
done
|
||||||
$STD apt-mark unhold libigdgmm12
|
$STD apt-mark unhold libigdgmm12
|
||||||
$STD apt install -y --allow-downgrades ./libigdgmm12*.deb
|
$STD apt install -y ./libigdgmm12*.deb
|
||||||
rm ./libigdgmm12*.deb
|
rm ./libigdgmm12*.deb
|
||||||
$STD apt install -y ./*.deb
|
$STD apt install -y ./*.deb
|
||||||
rm ./*.deb
|
rm ./*.deb
|
||||||
@@ -105,7 +110,7 @@ EOF
|
|||||||
msg_ok "Image-processing libraries up to date"
|
msg_ok "Image-processing libraries up to date"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
RELEASE="2.5.3"
|
RELEASE="2.5.2"
|
||||||
if check_for_gh_release "immich" "immich-app/immich" "${RELEASE}"; then
|
if check_for_gh_release "immich" "immich-app/immich" "${RELEASE}"; 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"
|
||||||
@@ -129,7 +134,9 @@ EOF
|
|||||||
$STD sudo -u postgres psql -d immich -c "REINDEX INDEX face_index;"
|
$STD sudo -u postgres psql -d immich -c "REINDEX INDEX face_index;"
|
||||||
$STD sudo -u postgres psql -d immich -c "REINDEX INDEX clip_index;"
|
$STD sudo -u postgres psql -d immich -c "REINDEX INDEX clip_index;"
|
||||||
fi
|
fi
|
||||||
ensure_dependencies ccache
|
if ! dpkg -l | grep -q ccache; then
|
||||||
|
$STD apt install -yqq ccache
|
||||||
|
fi
|
||||||
|
|
||||||
INSTALL_DIR="/opt/${APP}"
|
INSTALL_DIR="/opt/${APP}"
|
||||||
UPLOAD_DIR="$(sed -n '/^IMMICH_MEDIA_LOCATION/s/[^=]*=//p' /opt/immich/.env)"
|
UPLOAD_DIR="$(sed -n '/^IMMICH_MEDIA_LOCATION/s/[^=]*=//p' /opt/immich/.env)"
|
||||||
@@ -160,10 +167,7 @@ EOF
|
|||||||
rm -rf "${APP_DIR:?}"/*
|
rm -rf "${APP_DIR:?}"/*
|
||||||
)
|
)
|
||||||
|
|
||||||
setup_uv
|
|
||||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "immich" "immich-app/immich" "tarball" "v${RELEASE}" "$SRC_DIR"
|
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "immich" "immich-app/immich" "tarball" "v${RELEASE}" "$SRC_DIR"
|
||||||
PNPM_VERSION="$(jq -r '.packageManager | split("@")[1]' ${SRC_DIR}/package.json)"
|
|
||||||
NODE_VERSION="24" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs
|
|
||||||
|
|
||||||
msg_info "Updating Immich web and microservices"
|
msg_info "Updating Immich web and microservices"
|
||||||
cd "$SRC_DIR"/server
|
cd "$SRC_DIR"/server
|
||||||
@@ -300,7 +304,10 @@ function compile_libjxl() {
|
|||||||
|
|
||||||
function compile_libheif() {
|
function compile_libheif() {
|
||||||
SOURCE=${SOURCE_DIR}/libheif
|
SOURCE=${SOURCE_DIR}/libheif
|
||||||
ensure_dependencies libaom-dev
|
if ! dpkg -l | grep -q libaom; then
|
||||||
|
$STD apt install -y libaom-dev
|
||||||
|
local update="required"
|
||||||
|
fi
|
||||||
: "${LIBHEIF_REVISION:=$(jq -cr '.revision' "$BASE_DIR"/server/sources/libheif.json)}"
|
: "${LIBHEIF_REVISION:=$(jq -cr '.revision' "$BASE_DIR"/server/sources/libheif.json)}"
|
||||||
if [[ "${update:-}" ]] || [[ "$LIBHEIF_REVISION" != "$(grep 'libheif' ~/.immich_library_revisions | awk '{print $2}')" ]]; then
|
if [[ "${update:-}" ]] || [[ "$LIBHEIF_REVISION" != "$(grep 'libheif' ~/.immich_library_revisions | awk '{print $2}')" ]]; then
|
||||||
msg_info "Recompiling libheif"
|
msg_info "Recompiling libheif"
|
||||||
|
|||||||
@@ -40,7 +40,9 @@ function update_script() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
msg_info "Updating Jellyfin"
|
msg_info "Updating Jellyfin"
|
||||||
ensure_dependencies libjemalloc2
|
if ! dpkg -s libjemalloc2 >/dev/null 2>&1; then
|
||||||
|
$STD apt install -y libjemalloc2
|
||||||
|
fi
|
||||||
if [[ ! -f /usr/lib/libjemalloc.so ]]; then
|
if [[ ! -f /usr/lib/libjemalloc.so ]]; then
|
||||||
ln -sf /usr/lib/x86_64-linux-gnu/libjemalloc.so.2 /usr/lib/libjemalloc.so
|
ln -sf /usr/lib/x86_64-linux-gnu/libjemalloc.so.2 /usr/lib/libjemalloc.so
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ function update_script() {
|
|||||||
msg_ok "Updated yt-dlp"
|
msg_ok "Updated yt-dlp"
|
||||||
|
|
||||||
msg_info "Prepare update"
|
msg_info "Prepare update"
|
||||||
ensure_dependencies graphicsmagick ghostscript
|
$STD apt install -y graphicsmagick ghostscript
|
||||||
if [[ -f /opt/karakeep/.env ]] && [[ ! -f /etc/karakeep/karakeep.env ]]; then
|
if [[ -f /opt/karakeep/.env ]] && [[ ! -f /etc/karakeep/karakeep.env ]]; then
|
||||||
mkdir -p /etc/karakeep
|
mkdir -p /etc/karakeep
|
||||||
mv /opt/karakeep/.env /etc/karakeep/karakeep.env
|
mv /opt/karakeep/.env /etc/karakeep/karakeep.env
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ function update_script() {
|
|||||||
header_info
|
header_info
|
||||||
check_container_storage
|
check_container_storage
|
||||||
check_container_resources
|
check_container_resources
|
||||||
ensure_dependencies lsb-release
|
if ! command -v lsb_release; then
|
||||||
|
apt install -y lsb-release
|
||||||
|
fi
|
||||||
if [[ ! -d /opt/kimai ]]; then
|
if [[ ! -d /opt/kimai ]]; then
|
||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
#!/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: snazzybean
|
|
||||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
|
||||||
# Source: https://github.com/TomBursch/kitchenowl
|
|
||||||
|
|
||||||
APP="KitchenOwl"
|
|
||||||
var_tags="${var_tags:-food;recipes}"
|
|
||||||
var_cpu="${var_cpu:-1}"
|
|
||||||
var_ram="${var_ram:-2048}"
|
|
||||||
var_disk="${var_disk:-6}"
|
|
||||||
var_os="${var_os:-debian}"
|
|
||||||
var_version="${var_version:-13}"
|
|
||||||
var_unprivileged="${var_unprivileged:-1}"
|
|
||||||
|
|
||||||
header_info "$APP"
|
|
||||||
variables
|
|
||||||
color
|
|
||||||
catch_errors
|
|
||||||
|
|
||||||
function update_script() {
|
|
||||||
header_info
|
|
||||||
check_container_storage
|
|
||||||
check_container_resources
|
|
||||||
|
|
||||||
if [[ ! -d /opt/kitchenowl ]]; then
|
|
||||||
msg_error "No ${APP} Installation Found!"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
if check_for_gh_release "kitchenowl" "TomBursch/kitchenowl"; then
|
|
||||||
msg_info "Stopping Service"
|
|
||||||
systemctl stop kitchenowl
|
|
||||||
msg_ok "Stopped Service"
|
|
||||||
|
|
||||||
msg_info "Creating Backup"
|
|
||||||
mkdir -p /opt/kitchenowl_backup
|
|
||||||
cp -r /opt/kitchenowl/data /opt/kitchenowl_backup/
|
|
||||||
cp -f /opt/kitchenowl/kitchenowl.env /opt/kitchenowl_backup/
|
|
||||||
msg_ok "Created Backup"
|
|
||||||
|
|
||||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kitchenowl" "TomBursch/kitchenowl" "tarball" "latest" "/opt/kitchenowl"
|
|
||||||
rm -rf /opt/kitchenowl/web
|
|
||||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kitchenowl-web" "TomBursch/kitchenowl" "prebuild" "latest" "/opt/kitchenowl/web" "kitchenowl_Web.tar.gz"
|
|
||||||
|
|
||||||
msg_info "Restoring data"
|
|
||||||
sed -i 's/default=True/default=False/' /opt/kitchenowl/backend/wsgi.py
|
|
||||||
cp -r /opt/kitchenowl_backup/data /opt/kitchenowl/
|
|
||||||
cp -f /opt/kitchenowl_backup/kitchenowl.env /opt/kitchenowl/
|
|
||||||
rm -rf /opt/kitchenowl_backup
|
|
||||||
msg_ok "Restored data"
|
|
||||||
|
|
||||||
msg_info "Updating KitchenOwl"
|
|
||||||
cd /opt/kitchenowl/backend
|
|
||||||
$STD uv sync --frozen
|
|
||||||
cd /opt/kitchenowl/backend
|
|
||||||
set -a
|
|
||||||
source /opt/kitchenowl/kitchenowl.env
|
|
||||||
set +a
|
|
||||||
$STD uv run flask db upgrade
|
|
||||||
msg_ok "Updated KitchenOwl"
|
|
||||||
|
|
||||||
msg_info "Starting Service"
|
|
||||||
systemctl start kitchenowl
|
|
||||||
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}:80${CL}"
|
|
||||||
@@ -27,8 +27,9 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
JAVA_VERSION="21" setup_java
|
if ! dpkg -l | grep -q temurin-21-jre; then
|
||||||
|
JAVA_VERSION="21" setup_java
|
||||||
|
fi
|
||||||
msg_info "Updating ${APP}"
|
msg_info "Updating ${APP}"
|
||||||
$STD apt update
|
$STD apt update
|
||||||
$STD apt -y upgrade
|
$STD apt -y upgrade
|
||||||
|
|||||||
@@ -36,7 +36,12 @@ function update_script() {
|
|||||||
NODE_VERSION="24" setup_nodejs
|
NODE_VERSION="24" setup_nodejs
|
||||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "scanopy" "scanopy/scanopy" "tarball" "latest" "/opt/scanopy"
|
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "scanopy" "scanopy/scanopy" "tarball" "latest" "/opt/scanopy"
|
||||||
|
|
||||||
ensure_dependencies pkg-config libssl-dev
|
if ! dpkg -l | grep -q "pkg-config"; then
|
||||||
|
$STD apt install -y pkg-config
|
||||||
|
fi
|
||||||
|
if ! dpkg -l | grep -q "libssl-dev"; then
|
||||||
|
$STD apt install -y libssl-dev
|
||||||
|
fi
|
||||||
TOOLCHAIN="$(grep "channel" /opt/scanopy/backend/rust-toolchain.toml | awk -F\" '{print $2}')"
|
TOOLCHAIN="$(grep "channel" /opt/scanopy/backend/rust-toolchain.toml | awk -F\" '{print $2}')"
|
||||||
RUST_TOOLCHAIN=$TOOLCHAIN setup_rust
|
RUST_TOOLCHAIN=$TOOLCHAIN setup_rust
|
||||||
|
|
||||||
|
|||||||
@@ -28,12 +28,6 @@ function update_script() {
|
|||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
msg_error "This script is currently disabled due to an external issue with the OpenResty APT repository."
|
|
||||||
msg_error "The repository's GPG key uses SHA-1 signatures, which are no longer accepted by Debian as of February 1, 2026."
|
|
||||||
msg_error "The issue is tracked in openresty/openresty#1097"
|
|
||||||
msg_error "For more details, see: https://github.com/community-scripts/ProxmoxVE/issues/11406"
|
|
||||||
exit 1
|
|
||||||
|
|
||||||
if [[ $(grep -E '^VERSION_ID=' /etc/os-release) == *"12"* ]]; then
|
if [[ $(grep -E '^VERSION_ID=' /etc/os-release) == *"12"* ]]; then
|
||||||
msg_error "Wrong Debian version detected!"
|
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"
|
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"
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
ensure_dependencies python3-lxml
|
|
||||||
if ! [[ $(dpkg -s python3-lxml-html-clean 2>/dev/null) ]]; then
|
if ! [[ $(dpkg -s python3-lxml-html-clean 2>/dev/null) ]]; then
|
||||||
|
$STD apt install python3-lxml
|
||||||
curl -fsSL "http://archive.ubuntu.com/ubuntu/pool/universe/l/lxml-html-clean/python3-lxml-html-clean_0.1.1-1_all.deb" -o /opt/python3-lxml-html-clean.deb
|
curl -fsSL "http://archive.ubuntu.com/ubuntu/pool/universe/l/lxml-html-clean/python3-lxml-html-clean_0.1.1-1_all.deb" -o /opt/python3-lxml-html-clean.deb
|
||||||
$STD dpkg -i /opt/python3-lxml-html-clean.deb
|
$STD dpkg -i /opt/python3-lxml-html-clean.deb
|
||||||
rm -f /opt/python3-lxml-html-clean.deb
|
rm -f /opt/python3-lxml-html-clean.deb
|
||||||
|
|||||||
@@ -32,7 +32,11 @@ function update_script() {
|
|||||||
if [[ ! -f /opt/Ollama_version.txt ]]; then
|
if [[ ! -f /opt/Ollama_version.txt ]]; then
|
||||||
touch /opt/Ollama_version.txt
|
touch /opt/Ollama_version.txt
|
||||||
fi
|
fi
|
||||||
ensure_dependencies zstd
|
if ! command -v zstd &>/dev/null; then
|
||||||
|
msg_info "Installing zstd"
|
||||||
|
$STD apt install -y zstd
|
||||||
|
msg_ok "Installed zstd"
|
||||||
|
fi
|
||||||
msg_info "Stopping Services"
|
msg_info "Stopping Services"
|
||||||
systemctl stop ollama
|
systemctl stop ollama
|
||||||
msg_ok "Services Stopped"
|
msg_ok "Services Stopped"
|
||||||
|
|||||||
@@ -92,7 +92,11 @@ EOF
|
|||||||
OLLAMA_VERSION=$(ollama -v | awk '{print $NF}')
|
OLLAMA_VERSION=$(ollama -v | awk '{print $NF}')
|
||||||
RELEASE=$(curl -s https://api.github.com/repos/ollama/ollama/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}')
|
RELEASE=$(curl -s https://api.github.com/repos/ollama/ollama/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}')
|
||||||
if [ "$OLLAMA_VERSION" != "$RELEASE" ]; then
|
if [ "$OLLAMA_VERSION" != "$RELEASE" ]; then
|
||||||
ensure_dependencies zstd
|
if ! command -v zstd &>/dev/null; then
|
||||||
|
msg_info "Installing zstd"
|
||||||
|
$STD apt install -y zstd
|
||||||
|
msg_ok "Installed zstd"
|
||||||
|
fi
|
||||||
msg_info "Ollama update available: v$OLLAMA_VERSION -> v$RELEASE"
|
msg_info "Ollama update available: v$OLLAMA_VERSION -> v$RELEASE"
|
||||||
msg_info "Downloading Ollama v$RELEASE \n"
|
msg_info "Downloading Ollama v$RELEASE \n"
|
||||||
curl -fS#LO https://github.com/ollama/ollama/releases/download/v${RELEASE}/ollama-linux-amd64.tar.zst
|
curl -fS#LO https://github.com/ollama/ollama/releases/download/v${RELEASE}/ollama-linux-amd64.tar.zst
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ function update_script() {
|
|||||||
if [ "$VERSION_CODENAME" = "bookworm" ]; then
|
if [ "$VERSION_CODENAME" = "bookworm" ]; then
|
||||||
setup_gs
|
setup_gs
|
||||||
else
|
else
|
||||||
ensure_dependencies ghostscript
|
$STD apt install -y ghostscript
|
||||||
fi
|
fi
|
||||||
|
|
||||||
msg_info "Updating Paperless-ngx"
|
msg_info "Updating Paperless-ngx"
|
||||||
@@ -145,7 +145,7 @@ function update_script() {
|
|||||||
setup_gs
|
setup_gs
|
||||||
else
|
else
|
||||||
msg_info "Installing Ghostscript"
|
msg_info "Installing Ghostscript"
|
||||||
ensure_dependencies ghostscript
|
$STD apt install -y ghostscript
|
||||||
msg_ok "Installed Ghostscript"
|
msg_ok "Installed Ghostscript"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ function update_script() {
|
|||||||
LIBHEIF_URL=$(curl -fsSL "https://dl.photoprism.app/dist/libheif/" | grep -oP "libheif-bookworm-amd64-v[0-9\.]+\.tar\.gz" | sort -V | tail -n 1)
|
LIBHEIF_URL=$(curl -fsSL "https://dl.photoprism.app/dist/libheif/" | grep -oP "libheif-bookworm-amd64-v[0-9\.]+\.tar\.gz" | sort -V | tail -n 1)
|
||||||
if [[ "${LIBHEIF_URL}" != "$(cat ~/.photoprism_libheif 2>/dev/null)" ]] || [[ ! -f ~/.photoprism_libheif ]]; then
|
if [[ "${LIBHEIF_URL}" != "$(cat ~/.photoprism_libheif 2>/dev/null)" ]] || [[ ! -f ~/.photoprism_libheif ]]; then
|
||||||
msg_info "Updating PhotoPrism LibHeif"
|
msg_info "Updating PhotoPrism LibHeif"
|
||||||
ensure_dependencies libvips42
|
$STD apt install -y libvips42
|
||||||
curl -fsSL "https://dl.photoprism.app/dist/libheif/$LIBHEIF_URL" -o /tmp/libheif.tar.gz
|
curl -fsSL "https://dl.photoprism.app/dist/libheif/$LIBHEIF_URL" -o /tmp/libheif.tar.gz
|
||||||
tar -xzf /tmp/libheif.tar.gz -C /usr/local
|
tar -xzf /tmp/libheif.tar.gz -C /usr/local
|
||||||
ldconfig
|
ldconfig
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ function update_script() {
|
|||||||
cp -R /opt/rdtc-backup/appsettings.json /opt/rdtc/
|
cp -R /opt/rdtc-backup/appsettings.json /opt/rdtc/
|
||||||
if dpkg-query -W dotnet-sdk-8.0 >/dev/null 2>&1; then
|
if dpkg-query -W dotnet-sdk-8.0 >/dev/null 2>&1; then
|
||||||
$STD apt remove --purge -y dotnet-sdk-8.0
|
$STD apt remove --purge -y dotnet-sdk-8.0
|
||||||
ensure_dependencies aspnetcore-runtime-9.0
|
$STD apt install -y aspnetcore-runtime-9.0
|
||||||
fi
|
fi
|
||||||
rm -rf /opt/rdtc-backup
|
rm -rf /opt/rdtc-backup
|
||||||
|
|
||||||
|
|||||||
@@ -27,20 +27,22 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
if check_for_codeberg_release "readeck" "readeck/readeck"; then
|
msg_info "Stopping Service"
|
||||||
msg_info "Stopping Service"
|
systemctl stop readeck
|
||||||
systemctl stop readeck
|
msg_ok "Stopped Service"
|
||||||
msg_ok "Stopped Service"
|
|
||||||
|
|
||||||
fetch_and_deploy_codeberg_release "readeck" "readeck/readeck" "singlefile" "latest" "/opt/readeck" "readeck-*-linux-amd64"
|
msg_info "Updating Readeck"
|
||||||
|
LATEST=$(curl -fsSL https://codeberg.org/readeck/readeck/releases/ | grep -oP '/releases/tag/\K\d+\.\d+\.\d+' | head -1)
|
||||||
|
rm -rf /opt/readeck/readeck
|
||||||
|
cd /opt/readeck
|
||||||
|
curl -fsSL "https://codeberg.org/readeck/readeck/releases/download/${LATEST}/readeck-${LATEST}-linux-amd64" -o "readeck"
|
||||||
|
chmod a+x readeck
|
||||||
|
msg_ok "Updated Readeck"
|
||||||
|
|
||||||
msg_info "Starting Service"
|
msg_info "Starting Service"
|
||||||
systemctl start readeck
|
systemctl start readeck
|
||||||
msg_ok "Started Service"
|
msg_ok "Started Service"
|
||||||
msg_ok "Updated successfully!"
|
msg_ok "Updated successfully!"
|
||||||
else
|
|
||||||
msg_ok "No update required. ${APP} is already at the latest version."
|
|
||||||
fi
|
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
|
|
||||||
# Copyright (c) 2021-2026 community-scripts ORG
|
|
||||||
# Author: GoldenSpringness
|
|
||||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
|
||||||
# Source: https://github.com/orhun/rustypaste
|
|
||||||
|
|
||||||
APP="rustypaste"
|
|
||||||
var_tags="${var_tags:-pastebin;storage}"
|
|
||||||
var_cpu="${var_cpu:-1}"
|
|
||||||
var_ram="${var_ram:-1024}"
|
|
||||||
var_disk="${var_disk:-20}"
|
|
||||||
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/rustypaste/rustypaste ]]; then
|
|
||||||
msg_error "No ${APP} Installation Found!"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
if check_for_gh_release "rustypaste" "orhun/rustypaste"; then
|
|
||||||
msg_info "Stopping Services"
|
|
||||||
systemctl stop rustypaste
|
|
||||||
msg_ok "Stopped Services"
|
|
||||||
|
|
||||||
msg_info "Creating Backup"
|
|
||||||
tar -czf "/opt/rustypaste_backup_$(date +%F).tar.gz" /opt/rustypaste/upload 2>/dev/null || true
|
|
||||||
cp /opt/rustypaste/config.toml /tmp/rustypaste_config.toml.bak
|
|
||||||
msg_ok "Backup Created"
|
|
||||||
|
|
||||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "prebuild" "latest" "/opt/rustypaste" "*x86_64-unknown-linux-gnu.tar.gz"
|
|
||||||
|
|
||||||
msg_info "Restoring Data"
|
|
||||||
mv /tmp/rustypaste_config.toml.bak /opt/rustypaste/config.toml
|
|
||||||
tar -xzf "/opt/rustypaste_backup_$(date +%F).tar.gz" -C /opt/rustypaste/upload 2>/dev/null || true
|
|
||||||
rm -rf /opt/rustypaste_backup_$(date +%F).tar.gz
|
|
||||||
msg_ok "Restored Data"
|
|
||||||
|
|
||||||
msg_info "Starting Services"
|
|
||||||
systemctl start rustypaste
|
|
||||||
msg_ok "Started Services"
|
|
||||||
msg_ok "Updated successfully!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if check_for_gh_release "rustypaste-cli" "orhun/rustypaste-cli"; then
|
|
||||||
fetch_and_deploy_gh_release "rustypaste-cli" "orhun/rustypaste-cli" "prebuild" "latest" "/usr/local/bin" "*x86_64-unknown-linux-gnu.tar.gz"
|
|
||||||
fi
|
|
||||||
exit
|
|
||||||
}
|
|
||||||
|
|
||||||
start
|
|
||||||
build_container
|
|
||||||
description
|
|
||||||
|
|
||||||
msg_ok "Completed successfully!\n"
|
|
||||||
echo -e "${CREATING}${GN}rustypaste 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}"
|
|
||||||
@@ -42,7 +42,12 @@ function update_script() {
|
|||||||
|
|
||||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "scanopy" "scanopy/scanopy" "tarball" "latest" "/opt/scanopy"
|
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "scanopy" "scanopy/scanopy" "tarball" "latest" "/opt/scanopy"
|
||||||
|
|
||||||
ensure_dependencies pkg-config libssl-dev
|
if ! dpkg -l | grep -q "pkg-config"; then
|
||||||
|
$STD apt install -y pkg-config
|
||||||
|
fi
|
||||||
|
if ! dpkg -l | grep -q "libssl-dev"; then
|
||||||
|
$STD apt install -y libssl-dev
|
||||||
|
fi
|
||||||
TOOLCHAIN="$(grep "channel" /opt/scanopy/backend/rust-toolchain.toml | awk -F\" '{print $2}')"
|
TOOLCHAIN="$(grep "channel" /opt/scanopy/backend/rust-toolchain.toml | awk -F\" '{print $2}')"
|
||||||
RUST_TOOLCHAIN=$TOOLCHAIN setup_rust
|
RUST_TOOLCHAIN=$TOOLCHAIN setup_rust
|
||||||
|
|
||||||
@@ -67,13 +72,10 @@ function update_script() {
|
|||||||
mv ./target/release/server /usr/bin/scanopy-server
|
mv ./target/release/server /usr/bin/scanopy-server
|
||||||
msg_ok "Built scanopy-server"
|
msg_ok "Built scanopy-server"
|
||||||
|
|
||||||
[[ -f /etc/systemd/system/scanopy-daemon.service ]] &&
|
msg_info "Building scanopy-daemon"
|
||||||
fetch_and_deploy_gh_release "scanopy" "scanopy/scanopy" "singlefile" "latest" "/usr/local/bin" "scanopy-daemon-linux-amd64" &&
|
$STD cargo build --release --bin daemon
|
||||||
rm -f /usr/bin/scanopy-daemon ~/configure_daemon.sh &&
|
cp ./target/release/daemon /usr/bin/scanopy-daemon
|
||||||
sed -i -e 's|usr/bin|usr/local/bin|' \
|
msg_ok "Built scanopy-daemon"
|
||||||
-e 's/push/daemon_poll/' \
|
|
||||||
-e 's/pull/server_poll/' /etc/systemd/system/scanopy-daemon.service &&
|
|
||||||
systemctl daemon-reload
|
|
||||||
|
|
||||||
msg_info "Starting services"
|
msg_info "Starting services"
|
||||||
systemctl start scanopy-server
|
systemctl start scanopy-server
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ function update_script() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
msg_info "Updating ${APP}"
|
msg_info "Updating ${APP}"
|
||||||
ensure_dependencies twingate-connector
|
$STD apt update
|
||||||
|
$STD apt install -yq twingate-connector
|
||||||
$STD systemctl restart twingate-connector
|
$STD systemctl restart twingate-connector
|
||||||
msg_ok "Updated successfully!"
|
msg_ok "Updated successfully!"
|
||||||
exit
|
exit
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ function update_script() {
|
|||||||
|
|
||||||
msg_info "Updating ${APP}"
|
msg_info "Updating ${APP}"
|
||||||
$STD apt update --allow-releaseinfo-change
|
$STD apt update --allow-releaseinfo-change
|
||||||
ensure_dependencies unifi
|
$STD apt install -y unifi
|
||||||
msg_ok "Updated successfully!"
|
msg_ok "Updated successfully!"
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,9 +30,12 @@ function update_script() {
|
|||||||
|
|
||||||
NODE_VERSION="22" setup_nodejs
|
NODE_VERSION="22" setup_nodejs
|
||||||
|
|
||||||
ensure_dependencies chromium
|
if ! dpkg -s chromium >/dev/null 2>&1; then
|
||||||
if [[ ! -L /opt/uptime-kuma/chromium ]]; then
|
msg_info "Installing Chromium"
|
||||||
|
$STD apt update
|
||||||
|
$STD apt install -y chromium
|
||||||
ln -s /usr/bin/chromium /opt/uptime-kuma/chromium
|
ln -s /usr/bin/chromium /opt/uptime-kuma/chromium
|
||||||
|
msg_ok "Installed Chromium"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if check_for_gh_release "uptime-kuma" "louislam/uptime-kuma"; then
|
if check_for_gh_release "uptime-kuma" "louislam/uptime-kuma"; then
|
||||||
|
|||||||
@@ -72,12 +72,10 @@ function update_script() {
|
|||||||
systemctl stop vaultwarden
|
systemctl stop vaultwarden
|
||||||
msg_ok "Stopped Service"
|
msg_ok "Stopped Service"
|
||||||
|
|
||||||
|
fetch_and_deploy_gh_release "vaultwarden_webvault" "dani-garcia/bw_web_builds" "prebuild" "latest" "/opt/vaultwarden" "bw_web_*.tar.gz"
|
||||||
|
|
||||||
msg_info "Updating Web-Vault to $WVRELEASE"
|
msg_info "Updating Web-Vault to $WVRELEASE"
|
||||||
rm -rf /opt/vaultwarden/web-vault
|
rm -rf /opt/vaultwarden/web-vault
|
||||||
mkdir -p /opt/vaultwarden/web-vault
|
|
||||||
|
|
||||||
fetch_and_deploy_gh_release "vaultwarden_webvault" "dani-garcia/bw_web_builds" "prebuild" "latest" "/opt/vaultwarden/web-vault" "bw_web_*.tar.gz"
|
|
||||||
|
|
||||||
chown -R root:root /opt/vaultwarden/web-vault/
|
chown -R root:root /opt/vaultwarden/web-vault/
|
||||||
msg_ok "Updated Web-Vault to ${WVRELEASE}"
|
msg_ok "Updated Web-Vault to ${WVRELEASE}"
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,10 @@ function update_script() {
|
|||||||
msg_error "No ${APP} Installation Found!"
|
msg_error "No ${APP} Installation Found!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
ensure_dependencies zstd
|
if ! [[ $(dpkg -s zstd 2>/dev/null) ]]; then
|
||||||
|
$STD apt update
|
||||||
|
$STD apt install -y zstd
|
||||||
|
fi
|
||||||
RELEASE=$(curl -fsSL https://api.github.com/repos/matze/wastebin/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }')
|
RELEASE=$(curl -fsSL https://api.github.com/repos/matze/wastebin/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }')
|
||||||
# Dirty-Fix 03/2025 for missing APP_version.txt on old installations, set to pre-latest release
|
# Dirty-Fix 03/2025 for missing APP_version.txt on old installations, set to pre-latest release
|
||||||
msg_info "Running Migration"
|
msg_info "Running Migration"
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
#!/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://wealthfolio.app/
|
|
||||||
|
|
||||||
APP="Wealthfolio"
|
|
||||||
var_tags="${var_tags:-finance;portfolio}"
|
|
||||||
var_cpu="${var_cpu:-4}"
|
|
||||||
var_ram="${var_ram:-4096}"
|
|
||||||
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/wealthfolio ]]; then
|
|
||||||
msg_error "No ${APP} Installation Found!"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
if check_for_gh_release "wealthfolio" "afadil/wealthfolio"; then
|
|
||||||
msg_info "Stopping Service"
|
|
||||||
systemctl stop wealthfolio
|
|
||||||
msg_ok "Stopped Service"
|
|
||||||
|
|
||||||
msg_info "Backing up Data"
|
|
||||||
cp -r /opt/wealthfolio_data /opt/wealthfolio_data_backup
|
|
||||||
cp /opt/wealthfolio/.env /opt/wealthfolio_env_backup
|
|
||||||
msg_ok "Backed up Data"
|
|
||||||
|
|
||||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wealthfolio" "afadil/wealthfolio" "tarball"
|
|
||||||
|
|
||||||
msg_info "Building Frontend (patience)"
|
|
||||||
cd /opt/wealthfolio
|
|
||||||
$STD pnpm install --frozen-lockfile
|
|
||||||
$STD pnpm tsc
|
|
||||||
$STD pnpm vite build
|
|
||||||
msg_ok "Built Frontend"
|
|
||||||
|
|
||||||
msg_info "Building Backend (patience)"
|
|
||||||
cd /opt/wealthfolio/src-server
|
|
||||||
source ~/.cargo/env
|
|
||||||
$STD cargo build --release --manifest-path Cargo.toml
|
|
||||||
cp /opt/wealthfolio/src-server/target/release/wealthfolio-server /usr/local/bin/wealthfolio-server
|
|
||||||
chmod +x /usr/local/bin/wealthfolio-server
|
|
||||||
msg_ok "Built Backend"
|
|
||||||
|
|
||||||
msg_info "Restoring Data"
|
|
||||||
cp -r /opt/wealthfolio_data_backup/. /opt/wealthfolio_data
|
|
||||||
cp /opt/wealthfolio_env_backup /opt/wealthfolio/.env
|
|
||||||
rm -rf /opt/wealthfolio_data_backup /opt/wealthfolio_env_backup
|
|
||||||
msg_ok "Restored Data"
|
|
||||||
|
|
||||||
msg_info "Cleaning Up"
|
|
||||||
rm -rf /opt/wealthfolio/src-server/target
|
|
||||||
rm -rf /root/.cargo/registry
|
|
||||||
rm -rf /opt/wealthfolio/node_modules
|
|
||||||
msg_ok "Cleaned Up"
|
|
||||||
|
|
||||||
msg_info "Starting Service"
|
|
||||||
systemctl start wealthfolio
|
|
||||||
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}"
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
#!/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: Dunky13
|
|
||||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
|
||||||
# Source: https://github.com/cmintey/wishlist
|
|
||||||
|
|
||||||
APP="Wishlist"
|
|
||||||
var_tags="${var_tags:-sharing}"
|
|
||||||
var_cpu="${var_cpu:-2}"
|
|
||||||
var_ram="${var_ram:-2048}"
|
|
||||||
var_disk="${var_disk:-5}"
|
|
||||||
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/wishlist ]]; then
|
|
||||||
msg_error "No ${APP} Installation Found!"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
if check_for_gh_release "wishlist" "cmintey/wishlist"; then
|
|
||||||
NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs
|
|
||||||
|
|
||||||
msg_info "Stopping Service"
|
|
||||||
systemctl stop wishlist
|
|
||||||
msg_ok "Stopped Service"
|
|
||||||
|
|
||||||
msg_info "Creating Backup"
|
|
||||||
mkdir -p /opt/wishlist-backup
|
|
||||||
cp /opt/wishlist/.env /opt/wishlist-backup/.env
|
|
||||||
cp -a /opt/wishlist/uploads /opt/wishlist-backup
|
|
||||||
cp -a /opt/wishlist/data /opt/wishlist-backup
|
|
||||||
msg_ok "Created Backup"
|
|
||||||
|
|
||||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wishlist" "cmintey/wishlist" "tarball"
|
|
||||||
LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist")
|
|
||||||
|
|
||||||
msg_info "Updating Wishlist"
|
|
||||||
cd /opt/wishlist
|
|
||||||
$STD pnpm install
|
|
||||||
$STD pnpm svelte-kit sync
|
|
||||||
$STD pnpm prisma generate
|
|
||||||
sed -i 's|/usr/src/app/|/opt/wishlist/|g' $(grep -rl '/usr/src/app/' /opt/wishlist)
|
|
||||||
export VERSION="v${LATEST_APP_VERSION}"
|
|
||||||
export SHA="v${LATEST_APP_VERSION}"
|
|
||||||
$STD pnpm run build
|
|
||||||
$STD pnpm prune --prod
|
|
||||||
chmod +x /opt/wishlist/entrypoint.sh
|
|
||||||
|
|
||||||
msg_info "Restoring Backup"
|
|
||||||
cp /opt/wishlist-backup/.env /opt/wishlist/.env
|
|
||||||
cp -a /opt/wishlist-backup/uploads /opt/wishlist
|
|
||||||
cp -a /opt/wishlist-backup/data /opt/wishlist
|
|
||||||
rm -rf /opt/wishlist-backup
|
|
||||||
msg_ok "Restored Backup"
|
|
||||||
|
|
||||||
msg_ok "Updated Wishlist"
|
|
||||||
msg_info "Starting Service"
|
|
||||||
systemctl start wishlist
|
|
||||||
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}:3280${CL}"
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
#!/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: StellaeAlis
|
|
||||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
|
||||||
# Source: https://github.com/writefreely/writefreely
|
|
||||||
|
|
||||||
APP="WriteFreely"
|
|
||||||
var_tags="${var_tags:-writing}"
|
|
||||||
var_cpu="${var_cpu:-2}"
|
|
||||||
var_ram="${var_ram:-1024}"
|
|
||||||
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/writefreely ]]; then
|
|
||||||
msg_error "No ${APP} Installation Found!"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
if check_for_gh_release "writefreely" "writefreely/writefreely"; then
|
|
||||||
msg_info "Stopping Services"
|
|
||||||
systemctl stop writefreely
|
|
||||||
msg_ok "Stopped Services"
|
|
||||||
|
|
||||||
msg_info "Creating Backup"
|
|
||||||
mkdir -p /tmp/writefreely_backup
|
|
||||||
cp /opt/writefreely/keys /tmp/writefreely_backup/ 2>/dev/null
|
|
||||||
cp /opt/writefreely/config.ini /tmp/writefreely_backup/ 2>/dev/null
|
|
||||||
msg_ok "Created Backup"
|
|
||||||
|
|
||||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "writefreely" "writefreely/writefreely" "prebuild" "latest" "/opt/writefreely" "writefreely_*_linux_amd64.tar.gz"
|
|
||||||
|
|
||||||
msg_info "Restoring Data"
|
|
||||||
cp /tmp/writefreely_backup/config.ini /opt/writefreely/ 2>/dev/null
|
|
||||||
cp /tmp/writefreely_backup/keys/* /opt/writefreely/keys/ 2>/dev/null
|
|
||||||
rm -rf /tmp/writefreely_backup
|
|
||||||
msg_ok "Restored Data"
|
|
||||||
|
|
||||||
msg_info "Running Post-Update Tasks"
|
|
||||||
cd /opt/writefreely
|
|
||||||
$STD ./writefreely db migrate
|
|
||||||
ln -s /opt/writefreely/writefreely /usr/local/bin/writefreely
|
|
||||||
msg_ok "Ran Post-Update Tasks"
|
|
||||||
|
|
||||||
msg_info "Starting Services"
|
|
||||||
systemctl start writefreely
|
|
||||||
msg_ok "Started Services"
|
|
||||||
msg_ok "Updated successfully!"
|
|
||||||
fi
|
|
||||||
exit
|
|
||||||
}
|
|
||||||
|
|
||||||
start
|
|
||||||
build_container
|
|
||||||
description
|
|
||||||
|
|
||||||
msg_ok "Completed successfully!\n"
|
|
||||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
|
||||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
|
||||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}"
|
|
||||||
@@ -9,9 +9,9 @@
|
|||||||
"updateable": true,
|
"updateable": true,
|
||||||
"privileged": false,
|
"privileged": false,
|
||||||
"interface_port": 8083,
|
"interface_port": 8083,
|
||||||
"documentation": "https://codeberg.org/gelbphoenix/autocaliweb/wiki",
|
"documentation": "https://github.com/gelbphoenix/autocaliweb/wiki",
|
||||||
"config_path": "/etc/autocaliweb",
|
"config_path": "/etc/autocaliweb",
|
||||||
"website": "https://codeberg.org/gelbphoenix/autocaliweb",
|
"website": "https://github.com/gelbphoenix/autocaliweb",
|
||||||
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/autocaliweb.webp",
|
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/autocaliweb.webp",
|
||||||
"description": "A modern web management system for eBooks, eComics and PDFs",
|
"description": "A modern web management system for eBooks, eComics and PDFs",
|
||||||
"install_methods": [
|
"install_methods": [
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"generated": "2026-02-04T18:17:33Z",
|
"generated": "2026-02-01T18:07:47Z",
|
||||||
"versions": [
|
"versions": [
|
||||||
{
|
{
|
||||||
"slug": "2fauth",
|
"slug": "2fauth",
|
||||||
@@ -95,9 +95,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "bar-assistant",
|
"slug": "bar-assistant",
|
||||||
"repo": "karlomikus/bar-assistant",
|
"repo": "karlomikus/bar-assistant",
|
||||||
"version": "v5.13.1",
|
"version": "v5.13.0",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-02T18:47:43Z"
|
"date": "2026-02-01T15:49:21Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "bazarr",
|
"slug": "bazarr",
|
||||||
@@ -109,16 +109,16 @@
|
|||||||
{
|
{
|
||||||
"slug": "bentopdf",
|
"slug": "bentopdf",
|
||||||
"repo": "alam00000/bentopdf",
|
"repo": "alam00000/bentopdf",
|
||||||
"version": "v2.1.0",
|
"version": "v2.0.0",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-02T14:30:55Z"
|
"date": "2026-01-31T10:13:47Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "beszel",
|
"slug": "beszel",
|
||||||
"repo": "henrygd/beszel",
|
"repo": "henrygd/beszel",
|
||||||
"version": "v0.18.3",
|
"version": "v0.18.2",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-01T19:02:42Z"
|
"date": "2026-01-12T23:58:00Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "bitmagnet",
|
"slug": "bitmagnet",
|
||||||
@@ -158,9 +158,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "bytestash",
|
"slug": "bytestash",
|
||||||
"repo": "jordan-dalby/ByteStash",
|
"repo": "jordan-dalby/ByteStash",
|
||||||
"version": "v1.5.11",
|
"version": "v1.5.10",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-03T22:12:19Z"
|
"date": "2026-01-26T14:07:59Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "caddy",
|
"slug": "caddy",
|
||||||
@@ -186,9 +186,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "comfyui",
|
"slug": "comfyui",
|
||||||
"repo": "comfyanonymous/ComfyUI",
|
"repo": "comfyanonymous/ComfyUI",
|
||||||
"version": "v0.12.2",
|
"version": "v0.11.1",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-04T06:09:31Z"
|
"date": "2026-01-29T07:52:21Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "commafeed",
|
"slug": "commafeed",
|
||||||
@@ -242,9 +242,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "discopanel",
|
"slug": "discopanel",
|
||||||
"repo": "nickheyer/discopanel",
|
"repo": "nickheyer/discopanel",
|
||||||
"version": "v1.0.35",
|
"version": "v1.0.32",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-02T05:20:12Z"
|
"date": "2026-01-31T22:24:53Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "dispatcharr",
|
"slug": "dispatcharr",
|
||||||
@@ -256,9 +256,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "docmost",
|
"slug": "docmost",
|
||||||
"repo": "docmost/docmost",
|
"repo": "docmost/docmost",
|
||||||
"version": "v0.25.1",
|
"version": "v0.24.1",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-04T15:19:51Z"
|
"date": "2025-12-14T13:49:16Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "domain-locker",
|
"slug": "domain-locker",
|
||||||
@@ -291,9 +291,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "elementsynapse",
|
"slug": "elementsynapse",
|
||||||
"repo": "etkecc/synapse-admin",
|
"repo": "etkecc/synapse-admin",
|
||||||
"version": "v0.11.1-etke53",
|
"version": "v0.11.1-etke52",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-03T20:38:15Z"
|
"date": "2026-01-09T08:41:29Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "emby",
|
"slug": "emby",
|
||||||
@@ -312,9 +312,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "ersatztv",
|
"slug": "ersatztv",
|
||||||
"repo": "ErsatzTV/ErsatzTV",
|
"repo": "ErsatzTV/ErsatzTV",
|
||||||
"version": "v26.2.0",
|
"version": "v26.1.1",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-02T20:54:26Z"
|
"date": "2026-01-08T22:02:15Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "excalidraw",
|
"slug": "excalidraw",
|
||||||
@@ -375,9 +375,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "ghostfolio",
|
"slug": "ghostfolio",
|
||||||
"repo": "ghostfolio/ghostfolio",
|
"repo": "ghostfolio/ghostfolio",
|
||||||
"version": "2.235.0",
|
"version": "2.234.0",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-03T19:27:17Z"
|
"date": "2026-01-30T19:00:22Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "gitea",
|
"slug": "gitea",
|
||||||
@@ -487,9 +487,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "homebox",
|
"slug": "homebox",
|
||||||
"repo": "sysadminsmedia/homebox",
|
"repo": "sysadminsmedia/homebox",
|
||||||
"version": "v0.23.1",
|
"version": "v0.23.0",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-01T22:53:32Z"
|
"date": "2026-01-30T17:41:01Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "homepage",
|
"slug": "homepage",
|
||||||
@@ -515,9 +515,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "huntarr",
|
"slug": "huntarr",
|
||||||
"repo": "plexguide/Huntarr.io",
|
"repo": "plexguide/Huntarr.io",
|
||||||
"version": "9.1.12",
|
"version": "9.1.5",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-04T14:28:36Z"
|
"date": "2026-01-31T22:55:29Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "inspircd",
|
"slug": "inspircd",
|
||||||
@@ -536,16 +536,16 @@
|
|||||||
{
|
{
|
||||||
"slug": "invoiceninja",
|
"slug": "invoiceninja",
|
||||||
"repo": "invoiceninja/invoiceninja",
|
"repo": "invoiceninja/invoiceninja",
|
||||||
"version": "v5.12.53",
|
"version": "v5.12.52",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-04T00:52:01Z"
|
"date": "2026-02-01T02:08:10Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "jackett",
|
"slug": "jackett",
|
||||||
"repo": "Jackett/Jackett",
|
"repo": "Jackett/Jackett",
|
||||||
"version": "v0.24.1027",
|
"version": "v0.24.1003",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-04T05:56:22Z"
|
"date": "2026-02-01T05:55:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "joplin-server",
|
"slug": "joplin-server",
|
||||||
@@ -596,13 +596,6 @@
|
|||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-01-31T18:10:59Z"
|
"date": "2026-01-31T18:10:59Z"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"slug": "kitchenowl",
|
|
||||||
"repo": "TomBursch/kitchenowl",
|
|
||||||
"version": "v0.7.6",
|
|
||||||
"pinned": false,
|
|
||||||
"date": "2026-01-24T01:21:14Z"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"slug": "koel",
|
"slug": "koel",
|
||||||
"repo": "koel/koel",
|
"repo": "koel/koel",
|
||||||
@@ -669,9 +662,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "libretranslate",
|
"slug": "libretranslate",
|
||||||
"repo": "LibreTranslate/LibreTranslate",
|
"repo": "LibreTranslate/LibreTranslate",
|
||||||
"version": "v1.8.4",
|
"version": "v1.8.3",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-02T17:45:16Z"
|
"date": "2025-12-04T21:07:00Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "lidarr",
|
"slug": "lidarr",
|
||||||
@@ -746,9 +739,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "mealie",
|
"slug": "mealie",
|
||||||
"repo": "mealie-recipes/mealie",
|
"repo": "mealie-recipes/mealie",
|
||||||
"version": "v3.10.1",
|
"version": "v3.9.2",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-03T01:04:38Z"
|
"date": "2026-01-02T19:40:09Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "mediamanager",
|
"slug": "mediamanager",
|
||||||
@@ -767,9 +760,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "meilisearch",
|
"slug": "meilisearch",
|
||||||
"repo": "riccox/meilisearch-ui",
|
"repo": "riccox/meilisearch-ui",
|
||||||
"version": "v0.15.1",
|
"version": "v0.15.0",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-04T03:56:59Z"
|
"date": "2026-01-29T03:54:27Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "memos",
|
"slug": "memos",
|
||||||
@@ -781,9 +774,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "metube",
|
"slug": "metube",
|
||||||
"repo": "alexta69/metube",
|
"repo": "alexta69/metube",
|
||||||
"version": "2026.02.03",
|
"version": "2026.02.01",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-03T21:49:49Z"
|
"date": "2026-02-01T00:20:00Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "miniflux",
|
"slug": "miniflux",
|
||||||
@@ -823,16 +816,16 @@
|
|||||||
{
|
{
|
||||||
"slug": "navidrome",
|
"slug": "navidrome",
|
||||||
"repo": "navidrome/navidrome",
|
"repo": "navidrome/navidrome",
|
||||||
"version": "v0.60.0",
|
"version": "v0.59.0",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-03T18:57:04Z"
|
"date": "2025-12-06T18:08:42Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "netbox",
|
"slug": "netbox",
|
||||||
"repo": "netbox-community/netbox",
|
"repo": "netbox-community/netbox",
|
||||||
"version": "v4.5.2",
|
"version": "v4.5.1",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-03T13:54:26Z"
|
"date": "2026-01-20T19:45:05Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "nocodb",
|
"slug": "nocodb",
|
||||||
@@ -879,9 +872,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "opengist",
|
"slug": "opengist",
|
||||||
"repo": "thomiceli/opengist",
|
"repo": "thomiceli/opengist",
|
||||||
"version": "v1.12.1",
|
"version": "v1.12.0",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-03T09:00:43Z"
|
"date": "2026-01-27T15:31:57Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "ots",
|
"slug": "ots",
|
||||||
@@ -1054,9 +1047,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "prometheus-alertmanager",
|
"slug": "prometheus-alertmanager",
|
||||||
"repo": "prometheus/alertmanager",
|
"repo": "prometheus/alertmanager",
|
||||||
"version": "v0.31.0",
|
"version": "v0.30.1",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-02T13:34:15Z"
|
"date": "2026-01-12T23:30:06Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "prometheus-blackbox-exporter",
|
"slug": "prometheus-blackbox-exporter",
|
||||||
@@ -1096,9 +1089,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "pulse",
|
"slug": "pulse",
|
||||||
"repo": "rcourtman/Pulse",
|
"repo": "rcourtman/Pulse",
|
||||||
"version": "v5.1.0",
|
"version": "v5.0.17",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-04T17:43:59Z"
|
"date": "2026-01-20T19:07:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "pve-scripts-local",
|
"slug": "pve-scripts-local",
|
||||||
@@ -1184,13 +1177,6 @@
|
|||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-01-12T05:38:30Z"
|
"date": "2026-01-12T05:38:30Z"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"slug": "rustypaste",
|
|
||||||
"repo": "orhun/rustypaste",
|
|
||||||
"version": "v0.16.1",
|
|
||||||
"pinned": false,
|
|
||||||
"date": "2025-03-21T20:44:47Z"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"slug": "sabnzbd",
|
"slug": "sabnzbd",
|
||||||
"repo": "sabnzbd/sabnzbd",
|
"repo": "sabnzbd/sabnzbd",
|
||||||
@@ -1201,9 +1187,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "scanopy",
|
"slug": "scanopy",
|
||||||
"repo": "scanopy/scanopy",
|
"repo": "scanopy/scanopy",
|
||||||
"version": "v0.14.3",
|
"version": "v0.14.0",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-04T01:41:01Z"
|
"date": "2026-02-01T17:02:37Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "scraparr",
|
"slug": "scraparr",
|
||||||
@@ -1271,16 +1257,16 @@
|
|||||||
{
|
{
|
||||||
"slug": "speedtest-tracker",
|
"slug": "speedtest-tracker",
|
||||||
"repo": "alexjustesen/speedtest-tracker",
|
"repo": "alexjustesen/speedtest-tracker",
|
||||||
"version": "v1.13.7",
|
"version": "v1.13.5",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-04T16:47:42Z"
|
"date": "2026-01-08T22:35:28Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "spoolman",
|
"slug": "spoolman",
|
||||||
"repo": "Donkie/Spoolman",
|
"repo": "Donkie/Spoolman",
|
||||||
"version": "v0.23.1",
|
"version": "v0.23.0",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-03T19:03:55Z"
|
"date": "2026-01-23T20:42:34Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "sportarr",
|
"slug": "sportarr",
|
||||||
@@ -1355,9 +1341,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "thingsboard",
|
"slug": "thingsboard",
|
||||||
"repo": "thingsboard/thingsboard",
|
"repo": "thingsboard/thingsboard",
|
||||||
"version": "v4.3.0.1",
|
"version": "v4.3",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-03T12:39:14Z"
|
"date": "2026-01-20T14:27:07Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "threadfin",
|
"slug": "threadfin",
|
||||||
@@ -1369,9 +1355,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "tianji",
|
"slug": "tianji",
|
||||||
"repo": "msgbyte/tianji",
|
"repo": "msgbyte/tianji",
|
||||||
"version": "v1.31.10",
|
"version": "v1.31.9",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-04T17:21:04Z"
|
"date": "2026-01-31T18:22:40Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "traccar",
|
"slug": "traccar",
|
||||||
@@ -1411,9 +1397,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "trip",
|
"slug": "trip",
|
||||||
"repo": "itskovacs/TRIP",
|
"repo": "itskovacs/TRIP",
|
||||||
"version": "1.38.1",
|
"version": "1.38.0",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-04T18:10:15Z"
|
"date": "2026-01-31T15:56:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "tududi",
|
"slug": "tududi",
|
||||||
@@ -1425,9 +1411,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "tunarr",
|
"slug": "tunarr",
|
||||||
"repo": "chrisbenincasa/tunarr",
|
"repo": "chrisbenincasa/tunarr",
|
||||||
"version": "v1.1.12",
|
"version": "v1.1.11",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-03T20:19:00Z"
|
"date": "2026-01-30T22:34:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "uhf",
|
"slug": "uhf",
|
||||||
@@ -1471,19 +1457,12 @@
|
|||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2025-10-22T17:03:54Z"
|
"date": "2025-10-22T17:03:54Z"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"slug": "vaultwarden",
|
|
||||||
"repo": "dani-garcia/vaultwarden",
|
|
||||||
"version": "1.35.2",
|
|
||||||
"pinned": false,
|
|
||||||
"date": "2026-01-09T18:37:04Z"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"slug": "victoriametrics",
|
"slug": "victoriametrics",
|
||||||
"repo": "VictoriaMetrics/VictoriaMetrics",
|
"repo": "VictoriaMetrics/VictoriaMetrics",
|
||||||
"version": "v1.135.0",
|
"version": "v1.134.0",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-02T14:20:15Z"
|
"date": "2026-01-19T13:29:43Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "vikunja",
|
"slug": "vikunja",
|
||||||
@@ -1509,9 +1488,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "wanderer",
|
"slug": "wanderer",
|
||||||
"repo": "meilisearch/meilisearch",
|
"repo": "meilisearch/meilisearch",
|
||||||
"version": "v1.35.0",
|
"version": "v1.34.3",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-02T09:57:03Z"
|
"date": "2026-01-28T17:52:24Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "warracker",
|
"slug": "warracker",
|
||||||
@@ -1541,13 +1520,6 @@
|
|||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2025-12-31T16:53:34Z"
|
"date": "2025-12-31T16:53:34Z"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"slug": "wealthfolio",
|
|
||||||
"repo": "afadil/wealthfolio",
|
|
||||||
"version": "v2.1.0",
|
|
||||||
"pinned": false,
|
|
||||||
"date": "2025-12-01T21:57:36Z"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"slug": "web-check",
|
"slug": "web-check",
|
||||||
"repo": "CrazyWolf13/web-check",
|
"repo": "CrazyWolf13/web-check",
|
||||||
@@ -1562,13 +1534,6 @@
|
|||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-01-08T09:50:00Z"
|
"date": "2026-01-08T09:50:00Z"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"slug": "wishlist",
|
|
||||||
"repo": "cmintey/wishlist",
|
|
||||||
"version": "v0.59.0",
|
|
||||||
"pinned": false,
|
|
||||||
"date": "2026-01-19T16:42:14Z"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"slug": "wizarr",
|
"slug": "wizarr",
|
||||||
"repo": "wizarrrr/wizarr",
|
"repo": "wizarrrr/wizarr",
|
||||||
@@ -1576,13 +1541,6 @@
|
|||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2025-12-09T14:30:23Z"
|
"date": "2025-12-09T14:30:23Z"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"slug": "writefreely",
|
|
||||||
"repo": "writefreely/writefreely",
|
|
||||||
"version": "v0.16.0",
|
|
||||||
"pinned": false,
|
|
||||||
"date": "2025-08-29T19:30:02Z"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"slug": "yt-dlp-webui",
|
"slug": "yt-dlp-webui",
|
||||||
"repo": "marcopiovanello/yt-dlp-web-ui",
|
"repo": "marcopiovanello/yt-dlp-web-ui",
|
||||||
@@ -1600,9 +1558,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "zigbee2mqtt",
|
"slug": "zigbee2mqtt",
|
||||||
"repo": "Koenkk/zigbee2mqtt",
|
"repo": "Koenkk/zigbee2mqtt",
|
||||||
"version": "2.8.0",
|
"version": "2.7.2",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-01T19:27:25Z"
|
"date": "2026-01-01T13:43:47Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "zipline",
|
"slug": "zipline",
|
||||||
@@ -1628,9 +1586,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "zwave-js-ui",
|
"slug": "zwave-js-ui",
|
||||||
"repo": "zwave-js/zwave-js-ui",
|
"repo": "zwave-js/zwave-js-ui",
|
||||||
"version": "v11.11.0",
|
"version": "v11.10.1",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-03T13:13:05Z"
|
"date": "2026-01-15T15:58:06Z"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Immich Public Proxy",
|
|
||||||
"slug": "immich-public-proxy",
|
|
||||||
"categories": [
|
|
||||||
21
|
|
||||||
],
|
|
||||||
"date_created": "2026-02-04",
|
|
||||||
"type": "addon",
|
|
||||||
"updateable": true,
|
|
||||||
"privileged": false,
|
|
||||||
"interface_port": 3000,
|
|
||||||
"documentation": "https://github.com/alangrainger/immich-public-proxy/docs",
|
|
||||||
"website": "https://github.com/alangrainger/immich-public-proxy",
|
|
||||||
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/immich-public-proxy.webp",
|
|
||||||
"config_path": "/opt/immich-proxy/app/.env",
|
|
||||||
"description": "Share your Immich photos and albums in a safe way without exposing your Immich instance to the public.",
|
|
||||||
"install_methods": [
|
|
||||||
{
|
|
||||||
"type": "default",
|
|
||||||
"script": "tools/addon/immich-public-proxy.sh",
|
|
||||||
"resources": {
|
|
||||||
"cpu": null,
|
|
||||||
"ram": null,
|
|
||||||
"hdd": null,
|
|
||||||
"os": null,
|
|
||||||
"version": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"default_credentials": {
|
|
||||||
"username": null,
|
|
||||||
"password": null
|
|
||||||
},
|
|
||||||
"notes": [
|
|
||||||
{
|
|
||||||
"text": "Requires Node.js 24+",
|
|
||||||
"type": "info"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Update with: update_immich-public-proxy",
|
|
||||||
"type": "info"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "KitchenOwl",
|
|
||||||
"slug": "kitchenowl",
|
|
||||||
"categories": [
|
|
||||||
13
|
|
||||||
],
|
|
||||||
"date_created": "2026-02-02",
|
|
||||||
"type": "ct",
|
|
||||||
"updateable": true,
|
|
||||||
"privileged": false,
|
|
||||||
"interface_port": 80,
|
|
||||||
"documentation": "https://docs.kitchenowl.org/",
|
|
||||||
"website": "https://kitchenowl.org/",
|
|
||||||
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kitchenowl.webp",
|
|
||||||
"config_path": "/opt/kitchenowl/kitchenowl.env",
|
|
||||||
"description": "KitchenOwl is a smart self-hosted grocery list and recipe manager with real-time synchronization, recipe management, meal planning, and expense tracking.",
|
|
||||||
"install_methods": [
|
|
||||||
{
|
|
||||||
"type": "default",
|
|
||||||
"script": "ct/kitchenowl.sh",
|
|
||||||
"resources": {
|
|
||||||
"cpu": 1,
|
|
||||||
"ram": 2048,
|
|
||||||
"hdd": 6,
|
|
||||||
"os": "Debian",
|
|
||||||
"version": "13"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"default_credentials": {
|
|
||||||
"username": null,
|
|
||||||
"password": null
|
|
||||||
},
|
|
||||||
"notes": []
|
|
||||||
}
|
|
||||||
@@ -13,8 +13,6 @@
|
|||||||
"website": "https://nginxproxymanager.com/",
|
"website": "https://nginxproxymanager.com/",
|
||||||
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/nginx-proxy-manager.webp",
|
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/nginx-proxy-manager.webp",
|
||||||
"config_path": "",
|
"config_path": "",
|
||||||
"disable": true,
|
|
||||||
"disable_description": "This script is temporarily disabled due to an external issue with the OpenResty APT repository. The repository's GPG key uses SHA-1 signatures, which are no longer accepted by Debian as of February 1, 2026. This causes installation to fail with APT errors. The issue is tracked in openresty/openresty#1097. A workaround exists but requires manual configuration. The script will be re-enabled once OpenResty updates their repository signing key. For more details, see: https://github.com/community-scripts/ProxmoxVE/issues/11406",
|
|
||||||
"description": "Nginx Proxy Manager is a tool that provides a web-based interface to manage Nginx reverse proxies. It enables users to easily and securely expose their services to the internet by providing features such as HTTPS encryption, domain mapping, and access control. It eliminates the need for manual configuration of Nginx reverse proxies, making it easy for users to quickly and securely expose their services to the public.",
|
"description": "Nginx Proxy Manager is a tool that provides a web-based interface to manage Nginx reverse proxies. It enables users to easily and securely expose their services to the internet by providing features such as HTTPS encryption, domain mapping, and access control. It eliminates the need for manual configuration of Nginx reverse proxies, making it easy for users to quickly and securely expose their services to the public.",
|
||||||
"install_methods": [
|
"install_methods": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "RustyPaste",
|
|
||||||
"slug": "rustypaste",
|
|
||||||
"categories": [
|
|
||||||
11
|
|
||||||
],
|
|
||||||
"date_created": "2026-02-02",
|
|
||||||
"type": "ct",
|
|
||||||
"updateable": true,
|
|
||||||
"privileged": false,
|
|
||||||
"interface_port": 8000,
|
|
||||||
"documentation": "https://github.com/orhun/rustypaste",
|
|
||||||
"config_path": "/opt/rustypaste/config.toml",
|
|
||||||
"website": "https://github.com/orhun/rustypaste",
|
|
||||||
"logo": "https://github.com/orhun/rustypaste/raw/master/img/rustypaste_logo.png",
|
|
||||||
"description": "Rustypaste is a minimal file upload/pastebin service.",
|
|
||||||
"install_methods": [
|
|
||||||
{
|
|
||||||
"type": "default",
|
|
||||||
"script": "ct/rustypaste.sh",
|
|
||||||
"resources": {
|
|
||||||
"cpu": 1,
|
|
||||||
"ram": 1024,
|
|
||||||
"hdd": 20,
|
|
||||||
"os": "Debian",
|
|
||||||
"version": "13"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "alpine",
|
|
||||||
"script": "ct/alpine-rustypaste.sh",
|
|
||||||
"resources": {
|
|
||||||
"cpu": 1,
|
|
||||||
"ram": 256,
|
|
||||||
"hdd": 4,
|
|
||||||
"os": "Alpine",
|
|
||||||
"version": "3.23"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"default_credentials": {
|
|
||||||
"username": null,
|
|
||||||
"password": null
|
|
||||||
},
|
|
||||||
"notes": [
|
|
||||||
{
|
|
||||||
"text": "When updating the script it will backup the whole project including all the uploaded files, make sure to extract it to a safe location or remove",
|
|
||||||
"type": "info"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
"type": "info"
|
"type": "info"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "The integrated daemon config is located at `/root/.config/daemon/`",
|
"text": "The integrated daemon config is located at `/root/.config/daemon/config.json`",
|
||||||
"type": "info"
|
"type": "info"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Wealthfolio",
|
|
||||||
"slug": "wealthfolio",
|
|
||||||
"categories": [
|
|
||||||
23
|
|
||||||
],
|
|
||||||
"date_created": "2026-02-03",
|
|
||||||
"type": "ct",
|
|
||||||
"updateable": true,
|
|
||||||
"privileged": false,
|
|
||||||
"interface_port": 8080,
|
|
||||||
"documentation": "https://wealthfolio.app/docs/introduction/",
|
|
||||||
"config_path": "/opt/wealthfolio/.env",
|
|
||||||
"website": "https://wealthfolio.app/",
|
|
||||||
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/wealthfolio.webp",
|
|
||||||
"description": "Wealthfolio is a beautiful, privacy-focused investment tracker with local data storage. Track your portfolio across multiple accounts and asset types with detailed performance analytics, goal planning, and multi-currency support.",
|
|
||||||
"install_methods": [
|
|
||||||
{
|
|
||||||
"type": "default",
|
|
||||||
"script": "ct/wealthfolio.sh",
|
|
||||||
"resources": {
|
|
||||||
"cpu": 4,
|
|
||||||
"ram": 4096,
|
|
||||||
"hdd": 10,
|
|
||||||
"os": "Debian",
|
|
||||||
"version": "13"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"default_credentials": {
|
|
||||||
"username": null,
|
|
||||||
"password": "See ~/wealthfolio.creds"
|
|
||||||
},
|
|
||||||
"notes": [
|
|
||||||
{
|
|
||||||
"text": "Login password is stored in ~/wealthfolio.creds",
|
|
||||||
"type": "info"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Wishlist",
|
|
||||||
"slug": "wishlist",
|
|
||||||
"categories": [
|
|
||||||
12
|
|
||||||
],
|
|
||||||
"date_created": "2026-02-04",
|
|
||||||
"type": "ct",
|
|
||||||
"updateable": true,
|
|
||||||
"privileged": false,
|
|
||||||
"interface_port": 3280,
|
|
||||||
"documentation": "https://github.com/cmintey/wishlist/blob/main/README.md#getting-started",
|
|
||||||
"config_path": "/opt/wishlist/.env",
|
|
||||||
"website": "https://github.com/cmintey/wishlist",
|
|
||||||
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/cmintey-wishlist.webp",
|
|
||||||
"description": "Wishlist is a self-hosted wishlist application that you can share with your friends and family. You no longer have to wonder what to get your family for the holidays, simply check their wishlist and claim any available item!",
|
|
||||||
"install_methods": [
|
|
||||||
{
|
|
||||||
"type": "default",
|
|
||||||
"script": "ct/wishlist.sh",
|
|
||||||
"resources": {
|
|
||||||
"cpu": 2,
|
|
||||||
"ram": 2048,
|
|
||||||
"hdd": 5,
|
|
||||||
"os": "Debian",
|
|
||||||
"version": "13"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"default_credentials": {
|
|
||||||
"username": null,
|
|
||||||
"password": null
|
|
||||||
},
|
|
||||||
"notes": []
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "WriteFreely",
|
|
||||||
"slug": "writefreely",
|
|
||||||
"categories": [
|
|
||||||
12
|
|
||||||
],
|
|
||||||
"date_created": "2026-02-04",
|
|
||||||
"type": "ct",
|
|
||||||
"updateable": true,
|
|
||||||
"privileged": false,
|
|
||||||
"interface_port": 80,
|
|
||||||
"documentation": "https://writefreely.org/docs",
|
|
||||||
"config_path": "/opt/writefreely/config.ini",
|
|
||||||
"website": "https://writefreely.org/",
|
|
||||||
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/writefreely-light.webp",
|
|
||||||
"description": "WriteFreely is free and open source software for easily publishing writing on the web with support for the ActivityPub protocol. Use it to start a personal blog — or an entire community.",
|
|
||||||
"install_methods": [
|
|
||||||
{
|
|
||||||
"type": "default",
|
|
||||||
"script": "ct/writefreely.sh",
|
|
||||||
"resources": {
|
|
||||||
"cpu": 2,
|
|
||||||
"ram": 1024,
|
|
||||||
"hdd": 4,
|
|
||||||
"os": "Debian",
|
|
||||||
"version": "13"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"default_credentials": {
|
|
||||||
"username": null,
|
|
||||||
"password": null
|
|
||||||
},
|
|
||||||
"notes": [
|
|
||||||
{
|
|
||||||
"text": "After installation execute `writefreely user create --admin <username>:<password>` to create your user.",
|
|
||||||
"type": "info"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
320
frontend/src/app/category-view/page.tsx
Normal file
320
frontend/src/app/category-view/page.tsx
Normal file
@@ -0,0 +1,320 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
|
import type { Category } from "@/lib/types";
|
||||||
|
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|
||||||
|
const defaultLogo = "/default-logo.png"; // Fallback logo path
|
||||||
|
const MAX_DESCRIPTION_LENGTH = 100; // Set max length for description
|
||||||
|
const MAX_LOGOS = 5; // Max logos to display at once
|
||||||
|
|
||||||
|
function formattedBadge(type: string) {
|
||||||
|
switch (type) {
|
||||||
|
case "vm":
|
||||||
|
return <Badge className="text-blue-500/75 border-blue-500/75 badge">VM</Badge>;
|
||||||
|
case "ct":
|
||||||
|
return <Badge className="text-yellow-500/75 border-yellow-500/75 badge">LXC</Badge>;
|
||||||
|
case "pve":
|
||||||
|
return <Badge className="text-orange-500/75 border-orange-500/75 badge">PVE</Badge>;
|
||||||
|
case "addon":
|
||||||
|
return <Badge className="text-green-500/75 border-green-500/75 badge">ADDON</Badge>;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CategoryView() {
|
||||||
|
const [categories, setCategories] = useState<Category[]>([]);
|
||||||
|
const [selectedCategoryIndex, setSelectedCategoryIndex] = useState<number | null>(null);
|
||||||
|
const [currentScripts, setCurrentScripts] = useState<any[]>([]);
|
||||||
|
const [logoIndices, setLogoIndices] = useState<{ [key: string]: number }>({});
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchCategories = async () => {
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line node/no-process-env
|
||||||
|
const basePath = process.env.NODE_ENV === "production" ? "/ProxmoxVE" : "";
|
||||||
|
const response = await fetch(`${basePath}/api/categories`);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Failed to fetch categories");
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
setCategories(data);
|
||||||
|
|
||||||
|
// Initialize logo indices
|
||||||
|
const initialLogoIndices: { [key: string]: number } = {};
|
||||||
|
data.forEach((category: any) => {
|
||||||
|
initialLogoIndices[category.name] = 0;
|
||||||
|
});
|
||||||
|
setLogoIndices(initialLogoIndices);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error("Error fetching categories:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchCategories();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleCategoryClick = (index: number) => {
|
||||||
|
setSelectedCategoryIndex(index);
|
||||||
|
setCurrentScripts(categories[index]?.scripts || []); // Update scripts for the selected category
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBackClick = () => {
|
||||||
|
setSelectedCategoryIndex(null);
|
||||||
|
setCurrentScripts([]); // Clear scripts when going back
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleScriptClick = (scriptSlug: string) => {
|
||||||
|
// Include category context when navigating to scripts
|
||||||
|
const categoryName = selectedCategoryIndex !== null ? categories[selectedCategoryIndex]?.name : null;
|
||||||
|
const queryParams = new URLSearchParams({ id: scriptSlug });
|
||||||
|
if (categoryName) {
|
||||||
|
queryParams.append("category", categoryName);
|
||||||
|
}
|
||||||
|
router.push(`/scripts?${queryParams.toString()}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const navigateCategory = (direction: "prev" | "next") => {
|
||||||
|
if (selectedCategoryIndex !== null) {
|
||||||
|
const newIndex
|
||||||
|
= direction === "prev"
|
||||||
|
? (selectedCategoryIndex - 1 + categories.length) % categories.length
|
||||||
|
: (selectedCategoryIndex + 1) % categories.length;
|
||||||
|
setSelectedCategoryIndex(newIndex);
|
||||||
|
setCurrentScripts(categories[newIndex]?.scripts || []); // Update scripts for the new category
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const switchLogos = (categoryName: string, direction: "prev" | "next") => {
|
||||||
|
setLogoIndices((prev) => {
|
||||||
|
const currentIndex = prev[categoryName] || 0;
|
||||||
|
const category = categories.find(cat => cat.name === categoryName);
|
||||||
|
if (!category || !category.scripts)
|
||||||
|
return prev;
|
||||||
|
|
||||||
|
const totalLogos = category.scripts.length;
|
||||||
|
const newIndex
|
||||||
|
= direction === "prev"
|
||||||
|
? (currentIndex - MAX_LOGOS + totalLogos) % totalLogos
|
||||||
|
: (currentIndex + MAX_LOGOS) % totalLogos;
|
||||||
|
|
||||||
|
return { ...prev, [categoryName]: newIndex };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const truncateDescription = (text: string) => {
|
||||||
|
return text.length > MAX_DESCRIPTION_LENGTH ? `${text.slice(0, MAX_DESCRIPTION_LENGTH)}...` : text;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderResources = (script: any) => {
|
||||||
|
const cpu = script.install_methods[0]?.resources.cpu;
|
||||||
|
const ram = script.install_methods[0]?.resources.ram;
|
||||||
|
const hdd = script.install_methods[0]?.resources.hdd;
|
||||||
|
|
||||||
|
const resourceParts = [];
|
||||||
|
if (cpu) {
|
||||||
|
resourceParts.push(
|
||||||
|
<span key="cpu">
|
||||||
|
<b>CPU:</b>
|
||||||
|
{" "}
|
||||||
|
{cpu}
|
||||||
|
vCPU
|
||||||
|
</span>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (ram) {
|
||||||
|
resourceParts.push(
|
||||||
|
<span key="ram">
|
||||||
|
<b>RAM:</b>
|
||||||
|
{" "}
|
||||||
|
{ram}
|
||||||
|
MB
|
||||||
|
</span>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (hdd) {
|
||||||
|
resourceParts.push(
|
||||||
|
<span key="hdd">
|
||||||
|
<b>HDD:</b>
|
||||||
|
{" "}
|
||||||
|
{hdd}
|
||||||
|
GB
|
||||||
|
</span>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceParts.length > 0
|
||||||
|
? (
|
||||||
|
<div className="text-sm text-gray-400">
|
||||||
|
{resourceParts.map((part, index) => (
|
||||||
|
<React.Fragment key={index}>
|
||||||
|
{part}
|
||||||
|
{index < resourceParts.length - 1 && " | "}
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 mt-20">
|
||||||
|
{categories.length === 0 && (
|
||||||
|
<p className="text-center text-gray-500">No categories available. Please check the API endpoint.</p>
|
||||||
|
)}
|
||||||
|
{selectedCategoryIndex !== null
|
||||||
|
? (
|
||||||
|
<div>
|
||||||
|
{/* Header with Navigation */}
|
||||||
|
<div className="flex items-center justify-between mb-6">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => navigateCategory("prev")}
|
||||||
|
className="p-2 transition-transform duration-300 hover:scale-105"
|
||||||
|
>
|
||||||
|
<ChevronLeft className="h-6 w-6" />
|
||||||
|
</Button>
|
||||||
|
<h2 className="text-3xl font-semibold transition-opacity duration-300 hover:opacity-90">
|
||||||
|
{categories[selectedCategoryIndex].name}
|
||||||
|
</h2>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => navigateCategory("next")}
|
||||||
|
className="p-2 transition-transform duration-300 hover:scale-105"
|
||||||
|
>
|
||||||
|
<ChevronRight className="h-6 w-6" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Scripts Grid */}
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6">
|
||||||
|
{currentScripts
|
||||||
|
.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
|
.map(script => (
|
||||||
|
<Card
|
||||||
|
key={script.name}
|
||||||
|
className="p-4 cursor-pointer hover:shadow-md transition-shadow duration-300"
|
||||||
|
onClick={() => handleScriptClick(script.slug)}
|
||||||
|
>
|
||||||
|
<CardContent className="flex flex-col gap-4">
|
||||||
|
<h3 className="text-lg font-bold script-text text-center hover:text-blue-600 transition-colors duration-300">
|
||||||
|
{script.name}
|
||||||
|
</h3>
|
||||||
|
<img
|
||||||
|
src={script.logo || defaultLogo}
|
||||||
|
alt={script.name || "Script logo"}
|
||||||
|
className="h-12 w-12 object-contain mx-auto"
|
||||||
|
/>
|
||||||
|
<p className="text-sm text-gray-500 text-center">
|
||||||
|
<b>Created at:</b>
|
||||||
|
{" "}
|
||||||
|
{script.date_created || "No date available"}
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
className="text-sm text-gray-700 hover:text-gray-900 text-center transition-colors duration-300"
|
||||||
|
title={script.description || "No description available."}
|
||||||
|
>
|
||||||
|
{truncateDescription(script.description || "No description available.")}
|
||||||
|
</p>
|
||||||
|
{renderResources(script)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Back to Categories Button */}
|
||||||
|
<div className="mt-8 text-center">
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
onClick={handleBackClick}
|
||||||
|
className="px-6 py-2 text-white bg-blue-600 hover:bg-blue-700 rounded-lg shadow-md transition-transform duration-300 hover:scale-105"
|
||||||
|
>
|
||||||
|
Back to Categories
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<div>
|
||||||
|
{/* Categories Grid */}
|
||||||
|
<div className="flex justify-between items-center mb-8">
|
||||||
|
<h1 className="text-3xl font-semibold mb-4">Categories</h1>
|
||||||
|
<p className="text-sm text-gray-500">
|
||||||
|
{categories.reduce((total, category) => total + (category.scripts?.length || 0), 0)}
|
||||||
|
{" "}
|
||||||
|
Total scripts
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-8">
|
||||||
|
{categories.map((category, index) => (
|
||||||
|
<Card
|
||||||
|
key={category.name}
|
||||||
|
onClick={() => handleCategoryClick(index)}
|
||||||
|
className="cursor-pointer hover:shadow-lg flex flex-col items-center justify-center py-6 transition-shadow duration-300"
|
||||||
|
>
|
||||||
|
<CardContent className="flex flex-col items-center">
|
||||||
|
<h3 className="text-xl font-bold mb-4 category-title transition-colors duration-300 hover:text-blue-600">
|
||||||
|
{category.name}
|
||||||
|
</h3>
|
||||||
|
<div className="flex justify-center items-center gap-2 mb-4">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
switchLogos(category.name, "prev");
|
||||||
|
}}
|
||||||
|
className="p-1 transition-transform duration-300 hover:scale-110"
|
||||||
|
>
|
||||||
|
<ChevronLeft className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
{category.scripts
|
||||||
|
&& category.scripts
|
||||||
|
.slice(logoIndices[category.name] || 0, (logoIndices[category.name] || 0) + MAX_LOGOS)
|
||||||
|
.map((script, i) => (
|
||||||
|
<div key={i} className="flex flex-col items-center">
|
||||||
|
<img
|
||||||
|
src={script.logo || defaultLogo}
|
||||||
|
alt={script.name || "Script logo"}
|
||||||
|
title={script.name}
|
||||||
|
className="h-8 w-8 object-contain cursor-pointer"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleScriptClick(script.slug);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{formattedBadge(script.type)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
switchLogos(category.name, "next");
|
||||||
|
}}
|
||||||
|
className="p-1 transition-transform duration-300 hover:scale-110"
|
||||||
|
>
|
||||||
|
<ChevronRight className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-gray-400 text-center">
|
||||||
|
{(category as any).description || "No description available."}
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CategoryView;
|
||||||
@@ -94,8 +94,7 @@ const chartConfigApps = {
|
|||||||
export default function DataPage() {
|
export default function DataPage() {
|
||||||
const [data, setData] = useState<DataModel[]>([]);
|
const [data, setData] = useState<DataModel[]>([]);
|
||||||
const [summary, setSummary] = useState<SummaryData | null>(null);
|
const [summary, setSummary] = useState<SummaryData | null>(null);
|
||||||
const [summaryLoading, setSummaryLoading] = useState<boolean>(true);
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
const [dataLoading, setDataLoading] = useState<boolean>(true);
|
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [itemsPerPage, setItemsPerPage] = useState(25);
|
const [itemsPerPage, setItemsPerPage] = useState(25);
|
||||||
@@ -106,40 +105,35 @@ export default function DataPage() {
|
|||||||
|
|
||||||
const nf = new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
|
const nf = new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
|
||||||
|
|
||||||
// Fetch summary only once on mount
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchSummary = async () => {
|
const fetchData = async () => {
|
||||||
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const summaryRes = await fetch("https://api.htl-braunau.at/data/summary");
|
const [summaryRes, dataRes] = await Promise.all([
|
||||||
|
fetch("https://api.htl-braunau.at/data/summary"),
|
||||||
|
fetch(
|
||||||
|
`https://api.htl-braunau.at/data/paginated?page=${currentPage}&limit=${
|
||||||
|
itemsPerPage === 0 ? "" : itemsPerPage
|
||||||
|
}`,
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
if (!summaryRes.ok) {
|
if (!summaryRes.ok) {
|
||||||
throw new Error(`Failed to fetch summary: ${summaryRes.statusText}`);
|
throw new Error(`Failed to fetch summary: ${summaryRes.statusText}`);
|
||||||
}
|
}
|
||||||
const summaryData: SummaryData = await summaryRes.json();
|
|
||||||
setSummary(summaryData);
|
|
||||||
} catch (err) {
|
|
||||||
setError((err as Error).message);
|
|
||||||
} finally {
|
|
||||||
setSummaryLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchSummary();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const fetchData = async () => {
|
|
||||||
setDataLoading(true);
|
|
||||||
try {
|
|
||||||
const dataRes = await fetch(`https://api.htl-braunau.at/data/paginated?page=${currentPage}&limit=${itemsPerPage}`);
|
|
||||||
if (!dataRes.ok) {
|
if (!dataRes.ok) {
|
||||||
throw new Error(`Failed to fetch data: ${dataRes.statusText}`);
|
throw new Error(`Failed to fetch data: ${dataRes.statusText}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const summaryData: SummaryData = await summaryRes.json();
|
||||||
const pageData: DataModel[] = await dataRes.json();
|
const pageData: DataModel[] = await dataRes.json();
|
||||||
|
|
||||||
|
setSummary(summaryData);
|
||||||
setData(pageData);
|
setData(pageData);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError((err as Error).message);
|
setError((err as Error).message);
|
||||||
} finally {
|
} finally {
|
||||||
setDataLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -312,7 +306,7 @@ export default function DataPage() {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="pl-2">
|
<CardContent className="pl-2">
|
||||||
<div className="h-[300px] w-full">
|
<div className="h-[300px] w-full">
|
||||||
{summaryLoading ? (
|
{loading ? (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
@@ -417,7 +411,7 @@ export default function DataPage() {
|
|||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{dataLoading ? (
|
{loading ? (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={8} className="h-24 text-center">
|
<TableCell colSpan={8} className="h-24 text-center">
|
||||||
<div className="flex items-center justify-center gap-2">
|
<div className="flex items-center justify-center gap-2">
|
||||||
@@ -484,7 +478,7 @@ export default function DataPage() {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setCurrentPage((prev) => Math.max(prev - 1, 1))}
|
onClick={() => setCurrentPage((prev) => Math.max(prev - 1, 1))}
|
||||||
disabled={currentPage === 1 || dataLoading}
|
disabled={currentPage === 1 || loading}
|
||||||
>
|
>
|
||||||
<ChevronLeft className="mr-2 h-4 w-4" />
|
<ChevronLeft className="mr-2 h-4 w-4" />
|
||||||
Previous
|
Previous
|
||||||
@@ -494,7 +488,7 @@ export default function DataPage() {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setCurrentPage((prev) => prev + 1)}
|
onClick={() => setCurrentPage((prev) => prev + 1)}
|
||||||
disabled={dataLoading || sortedData.length < itemsPerPage}
|
disabled={loading || sortedData.length < itemsPerPage}
|
||||||
>
|
>
|
||||||
Next
|
Next
|
||||||
<ChevronRight className="ml-2 h-4 w-4" />
|
<ChevronRight className="ml-2 h-4 w-4" />
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ function Note({
|
|||||||
const addNote = useCallback(() => {
|
const addNote = useCallback(() => {
|
||||||
setScript({
|
setScript({
|
||||||
...script,
|
...script,
|
||||||
notes: [...script.notes, { text: "", type: "info" }],
|
notes: [...script.notes, { text: "", type: "" }],
|
||||||
});
|
});
|
||||||
}, [script, setScript]);
|
}, [script, setScript]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { AlertColors } from "@/config/site-config";
|
|
||||||
|
|
||||||
export const InstallMethodSchema = z.object({
|
export const InstallMethodSchema = z.object({
|
||||||
type: z.enum(["default", "alpine"], {
|
type: z.enum(["default", "alpine"], {
|
||||||
@@ -17,9 +16,7 @@ export const InstallMethodSchema = z.object({
|
|||||||
|
|
||||||
const NoteSchema = z.object({
|
const NoteSchema = z.object({
|
||||||
text: z.string().min(1, "Note text cannot be empty"),
|
text: z.string().min(1, "Note text cannot be empty"),
|
||||||
type: z.enum(Object.keys(AlertColors) as [keyof typeof AlertColors, ...(keyof typeof AlertColors)[]], {
|
type: z.string().min(1, "Note type cannot be empty"),
|
||||||
message: `Type must be one of: ${Object.keys(AlertColors).join(", ")}`,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ScriptSchema = z.object({
|
export const ScriptSchema = z.object({
|
||||||
@@ -45,7 +42,7 @@ export const ScriptSchema = z.object({
|
|||||||
username: z.string().nullable(),
|
username: z.string().nullable(),
|
||||||
password: z.string().nullable(),
|
password: z.string().nullable(),
|
||||||
}),
|
}),
|
||||||
notes: z.array(NoteSchema).optional().default([]),
|
notes: z.array(NoteSchema),
|
||||||
}).refine((data) => {
|
}).refine((data) => {
|
||||||
if (data.disable === true && !data.disable_description) {
|
if (data.disable === true && !data.disable_description) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
||||||
import { fetchCategories } from "@/lib/data";
|
import { fetchCategories } from "@/lib/data";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
@@ -31,7 +30,6 @@ import Note from "./_components/note";
|
|||||||
|
|
||||||
import { nord } from "react-syntax-highlighter/dist/esm/styles/hljs";
|
import { nord } from "react-syntax-highlighter/dist/esm/styles/hljs";
|
||||||
import SyntaxHighlighter from "react-syntax-highlighter";
|
import SyntaxHighlighter from "react-syntax-highlighter";
|
||||||
import { ScriptItem } from "../scripts/_components/script-item";
|
|
||||||
|
|
||||||
const initialScript: Script = {
|
const initialScript: Script = {
|
||||||
name: "",
|
name: "",
|
||||||
@@ -62,7 +60,6 @@ export default function JSONGenerator() {
|
|||||||
const [isCopied, setIsCopied] = useState(false);
|
const [isCopied, setIsCopied] = useState(false);
|
||||||
const [isValid, setIsValid] = useState(false);
|
const [isValid, setIsValid] = useState(false);
|
||||||
const [categories, setCategories] = useState<Category[]>([]);
|
const [categories, setCategories] = useState<Category[]>([]);
|
||||||
const [currentTab, setCurrentTab] = useState<"json" | "preview">("json");
|
|
||||||
const [zodErrors, setZodErrors] = useState<z.ZodError | null>(null);
|
const [zodErrors, setZodErrors] = useState<z.ZodError | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -71,13 +68,6 @@ export default function JSONGenerator() {
|
|||||||
.catch((error) => console.error("Error fetching categories:", error));
|
.catch((error) => console.error("Error fetching categories:", error));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isValid && currentTab === "preview") {
|
|
||||||
setCurrentTab("json");
|
|
||||||
toast.error("Switched to JSON tab due to invalid configuration.");
|
|
||||||
}
|
|
||||||
}, [isValid, currentTab]);
|
|
||||||
|
|
||||||
const updateScript = useCallback((key: keyof Script, value: Script[keyof Script]) => {
|
const updateScript = useCallback((key: keyof Script, value: Script[keyof Script]) => {
|
||||||
setScript((prev) => {
|
setScript((prev) => {
|
||||||
const updated = { ...prev, [key]: value };
|
const updated = { ...prev, [key]: value };
|
||||||
@@ -206,7 +196,7 @@ export default function JSONGenerator() {
|
|||||||
<Input
|
<Input
|
||||||
placeholder="Path to config file"
|
placeholder="Path to config file"
|
||||||
value={script.config_path || ""}
|
value={script.config_path || ""}
|
||||||
onChange={(e) => updateScript("config_path", e.target.value || "")}
|
onChange={(e) => updateScript("config_path", e.target.value || null)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -333,41 +323,25 @@ export default function JSONGenerator() {
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-1/2 p-4 bg-background overflow-y-auto">
|
<div className="w-1/2 p-4 bg-background overflow-y-auto">
|
||||||
<Tabs
|
{validationAlert}
|
||||||
defaultValue="json"
|
<div className="relative">
|
||||||
className="w-full"
|
<div className="absolute right-2 top-2 flex gap-1">
|
||||||
onValueChange={(value) => setCurrentTab(value as "json" | "preview")}
|
<Button size="icon" variant="outline" onClick={handleCopy}>
|
||||||
value={currentTab}
|
{isCopied ? <Check className="h-4 w-4" /> : <Clipboard className="h-4 w-4" />}
|
||||||
>
|
</Button>
|
||||||
<TabsList className="grid w-full grid-cols-2">
|
<Button size="icon" variant="outline" onClick={handleDownload}>
|
||||||
<TabsTrigger value="json">JSON</TabsTrigger>
|
<Download className="h-4 w-4" />
|
||||||
<TabsTrigger disabled={!isValid} value="preview">Preview</TabsTrigger>
|
</Button>
|
||||||
</TabsList>
|
</div>
|
||||||
<TabsContent value="json" className="h-full w-full">
|
|
||||||
{validationAlert}
|
|
||||||
<div className="relative">
|
|
||||||
<div className="absolute right-2 top-2 flex gap-1">
|
|
||||||
<Button size="icon" variant="outline" onClick={handleCopy}>
|
|
||||||
{isCopied ? <Check className="h-4 w-4" /> : <Clipboard className="h-4 w-4" />}
|
|
||||||
</Button>
|
|
||||||
<Button size="icon" variant="outline" onClick={handleDownload}>
|
|
||||||
<Download className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
language="json"
|
language="json"
|
||||||
style={nord}
|
style={nord}
|
||||||
className="mt-4 p-4 bg-secondary rounded shadow overflow-x-scroll"
|
className="mt-4 p-4 bg-secondary rounded shadow overflow-x-scroll"
|
||||||
>
|
>
|
||||||
{JSON.stringify(script, null, 2)}
|
{JSON.stringify(script, null, 2)}
|
||||||
</SyntaxHighlighter>
|
</SyntaxHighlighter>
|
||||||
</div>
|
</div>
|
||||||
</TabsContent>
|
|
||||||
<TabsContent value="preview" className="h-full w-full">
|
|
||||||
<ScriptItem item={script} />
|
|
||||||
</TabsContent>
|
|
||||||
</Tabs>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ import { X, HelpCircle } from "lucide-react";
|
|||||||
import { Suspense } from "react";
|
import { Suspense } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
import type { AppVersion } from "@/lib/types";
|
import type { AppVersion, Script } from "@/lib/types";
|
||||||
import type { Script } from "@/app/json-editor/_schemas/schemas";
|
|
||||||
|
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
@@ -27,6 +26,7 @@ import Alerts from "./script-items/alerts";
|
|||||||
|
|
||||||
type ScriptItemProps = {
|
type ScriptItemProps = {
|
||||||
item: Script;
|
item: Script;
|
||||||
|
setSelectedScript: (script: string | null) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
function ScriptHeader({ item }: { item: Script }) {
|
function ScriptHeader({ item }: { item: Script }) {
|
||||||
@@ -135,10 +135,25 @@ function VersionInfo({ item }: { item: Script }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ScriptItem({ item }: ScriptItemProps) {
|
export function ScriptItem({ item, setSelectedScript }: ScriptItemProps) {
|
||||||
|
const closeScript = () => {
|
||||||
|
window.history.pushState({}, document.title, window.location.pathname);
|
||||||
|
setSelectedScript(null);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full mx-auto">
|
<div className="w-full mx-auto">
|
||||||
<div className="flex w-full flex-col">
|
<div className="flex w-full flex-col">
|
||||||
|
<div className="mb-3 flex items-center justify-between">
|
||||||
|
<h2 className="text-2xl font-semibold tracking-tight text-foreground/90">Selected Script</h2>
|
||||||
|
<button
|
||||||
|
onClick={closeScript}
|
||||||
|
className="rounded-full p-2 text-muted-foreground hover:bg-card/50 transition-colors"
|
||||||
|
>
|
||||||
|
<X className="h-5 w-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="rounded-xl border border-border bg-accent/30 backdrop-blur-sm shadow-sm">
|
<div className="rounded-xl border border-border bg-accent/30 backdrop-blur-sm shadow-sm">
|
||||||
<div className="p-6 space-y-6">
|
<div className="p-6 space-y-6">
|
||||||
<Suspense fallback={<div className="animate-pulse h-32 bg-accent/20 rounded-xl" />}>
|
<Suspense fallback={<div className="animate-pulse h-32 bg-accent/20 rounded-xl" />}>
|
||||||
@@ -147,7 +162,7 @@ export function ScriptItem({ item }: ScriptItemProps) {
|
|||||||
|
|
||||||
{item.disable && item.disable_description && (
|
{item.disable && item.disable_description && (
|
||||||
<DisableDescription item={item} />
|
<DisableDescription item={item} />
|
||||||
)}
|
) }
|
||||||
|
|
||||||
{!item.disable && (
|
{!item.disable && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { Suspense, useEffect, useState } from "react";
|
import { Suspense, useEffect, useState } from "react";
|
||||||
import { Loader2, X } from "lucide-react";
|
import { Loader2 } from "lucide-react";
|
||||||
import { useQueryState } from "nuqs";
|
import { useQueryState } from "nuqs";
|
||||||
|
|
||||||
import type { Category, Script } from "@/lib/types";
|
import type { Category, Script } from "@/lib/types";
|
||||||
@@ -20,11 +20,6 @@ function ScriptContent() {
|
|||||||
const [item, setItem] = useState<Script>();
|
const [item, setItem] = useState<Script>();
|
||||||
const [latestPage, setLatestPage] = useState(1);
|
const [latestPage, setLatestPage] = useState(1);
|
||||||
|
|
||||||
const closeScript = () => {
|
|
||||||
window.history.pushState({}, document.title, window.location.pathname);
|
|
||||||
setSelectedScript(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedScript && links.length > 0) {
|
if (selectedScript && links.length > 0) {
|
||||||
const script = links
|
const script = links
|
||||||
@@ -58,18 +53,7 @@ function ScriptContent() {
|
|||||||
<div className="px-4 w-full sm:max-w-[calc(100%-350px-16px)]">
|
<div className="px-4 w-full sm:max-w-[calc(100%-350px-16px)]">
|
||||||
{selectedScript && item
|
{selectedScript && item
|
||||||
? (
|
? (
|
||||||
<div className="flex w-full flex-col">
|
<ScriptItem item={item} setSelectedScript={setSelectedScript} />
|
||||||
<div className="mb-3 flex items-center justify-between">
|
|
||||||
<h2 className="text-2xl font-semibold tracking-tight text-foreground/90">Selected Script</h2>
|
|
||||||
<button
|
|
||||||
onClick={closeScript}
|
|
||||||
className="rounded-full p-2 text-muted-foreground hover:bg-card/50 transition-colors"
|
|
||||||
>
|
|
||||||
<X className="h-5 w-5" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<ScriptItem item={item} />
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
: (
|
: (
|
||||||
<div className="flex w-full flex-col gap-5">
|
<div className="flex w-full flex-col gap-5">
|
||||||
|
|||||||
@@ -23,34 +23,6 @@ import { Button } from "./ui/button";
|
|||||||
import { Badge } from "./ui/badge";
|
import { Badge } from "./ui/badge";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
function search(scripts: Script[], query: string): Script[] {
|
|
||||||
const queryLower = query.toLowerCase().trim();
|
|
||||||
const searchWords = queryLower.split(/\s+/).filter(Boolean);
|
|
||||||
|
|
||||||
return scripts
|
|
||||||
.map(script => {
|
|
||||||
const nameLower = script.name.toLowerCase();
|
|
||||||
const descriptionLower = (script.description || "").toLowerCase();
|
|
||||||
|
|
||||||
let score = 0;
|
|
||||||
|
|
||||||
for (const word of searchWords) {
|
|
||||||
if (nameLower.includes(word)) {
|
|
||||||
score += 10;
|
|
||||||
}
|
|
||||||
if (descriptionLower.includes(word)) {
|
|
||||||
score += 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { script, score };
|
|
||||||
})
|
|
||||||
.filter(({ score }) => score > 0)
|
|
||||||
.sort((a, b) => b.score - a.score)
|
|
||||||
.slice(0, 20)
|
|
||||||
.map(({ script }) => script);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formattedBadge(type: string) {
|
export function formattedBadge(type: string) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "vm":
|
case "vm":
|
||||||
@@ -79,11 +51,9 @@ function getRandomScript(categories: Category[], previouslySelected: Set<string>
|
|||||||
}
|
}
|
||||||
|
|
||||||
function CommandMenu() {
|
function CommandMenu() {
|
||||||
const [query, setQuery] = React.useState("");
|
|
||||||
const [open, setOpen] = React.useState(false);
|
const [open, setOpen] = React.useState(false);
|
||||||
const [links, setLinks] = React.useState<Category[]>([]);
|
const [links, setLinks] = React.useState<Category[]>([]);
|
||||||
const [isLoading, setIsLoading] = React.useState(false);
|
const [isLoading, setIsLoading] = React.useState(false);
|
||||||
const [results, setResults] = React.useState<Script[]>([]);
|
|
||||||
const [selectedScripts, setSelectedScripts] = React.useState<Set<string>>(new Set());
|
const [selectedScripts, setSelectedScripts] = React.useState<Set<string>>(new Set());
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@@ -100,27 +70,6 @@ function CommandMenu() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (query.trim() === "") {
|
|
||||||
fetchSortedCategories();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const scriptMap = new Map<string, Script>();
|
|
||||||
|
|
||||||
for (const category of links) {
|
|
||||||
for (const script of category.scripts || []) {
|
|
||||||
if (!scriptMap.has(script.slug)) {
|
|
||||||
scriptMap.set(script.slug, script);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const uniqueScripts = Array.from(scriptMap.values());
|
|
||||||
const filteredResults = search(uniqueScripts, query);
|
|
||||||
setResults(filteredResults);
|
|
||||||
}
|
|
||||||
}, [query]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
|
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
|
||||||
@@ -248,20 +197,20 @@ function CommandMenu() {
|
|||||||
|
|
||||||
<CommandDialog
|
<CommandDialog
|
||||||
open={open}
|
open={open}
|
||||||
onOpenChange={(open) => {
|
onOpenChange={setOpen}
|
||||||
setOpen(open);
|
filter={(value: string, search: string) => {
|
||||||
if (open) {
|
const searchLower = search.toLowerCase().trim();
|
||||||
setQuery("");
|
if (!searchLower)
|
||||||
setResults([]);
|
return 1;
|
||||||
}
|
const valueLower = value.toLowerCase();
|
||||||
|
const searchWords = searchLower.split(/\s+/).filter(Boolean);
|
||||||
|
// All search words must appear somewhere in the value (name + description)
|
||||||
|
const allWordsMatch = searchWords.every((word: string) => valueLower.includes(word));
|
||||||
|
return allWordsMatch ? 1 : 0;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DialogTitle className="sr-only">Search scripts</DialogTitle>
|
<DialogTitle className="sr-only">Search scripts</DialogTitle>
|
||||||
<CommandInput
|
<CommandInput placeholder="Search for a script..." />
|
||||||
placeholder="Search for a script..."
|
|
||||||
onValueChange={setQuery}
|
|
||||||
value={query}
|
|
||||||
/>
|
|
||||||
<CommandList>
|
<CommandList>
|
||||||
<CommandEmpty>
|
<CommandEmpty>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
@@ -284,10 +233,9 @@ function CommandMenu() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</CommandEmpty>
|
</CommandEmpty>
|
||||||
|
{Object.entries(uniqueScriptsByCategory).map(([categoryName, scripts]) => (
|
||||||
{results.length > 0 ? (
|
<CommandGroup key={`category:${categoryName}`} heading={categoryName}>
|
||||||
<CommandGroup heading="Search Results">
|
{scripts.map(script => (
|
||||||
{results.map(script => (
|
|
||||||
<CommandItem
|
<CommandItem
|
||||||
key={`script:${script.slug}`}
|
key={`script:${script.slug}`}
|
||||||
value={`${script.name} ${script.type} ${script.description || ""}`}
|
value={`${script.name} ${script.type} ${script.description || ""}`}
|
||||||
@@ -320,44 +268,7 @@ function CommandMenu() {
|
|||||||
</CommandItem>
|
</CommandItem>
|
||||||
))}
|
))}
|
||||||
</CommandGroup>
|
</CommandGroup>
|
||||||
) : ( // When no search results, show all scripts grouped by category
|
))}
|
||||||
Object.entries(uniqueScriptsByCategory).map(([categoryName, scripts]) => (
|
|
||||||
<CommandGroup key={`category:${categoryName}`} heading={categoryName}>
|
|
||||||
{scripts.map(script => (
|
|
||||||
<CommandItem
|
|
||||||
key={`script:${script.slug}`}
|
|
||||||
value={`${script.name} ${script.type} ${script.description || ""}`}
|
|
||||||
onSelect={() => {
|
|
||||||
setOpen(false);
|
|
||||||
router.push(`/scripts?id=${script.slug}`);
|
|
||||||
}}
|
|
||||||
tabIndex={0}
|
|
||||||
aria-label={`Open script ${script.name}`}
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.key === "Enter" || e.key === " ") {
|
|
||||||
setOpen(false);
|
|
||||||
router.push(`/scripts?id=${script.slug}`);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="flex gap-2" onClick={() => setOpen(false)}>
|
|
||||||
<Image
|
|
||||||
src={script.logo || `/${basePath}/logo.png`}
|
|
||||||
onError={e => ((e.currentTarget as HTMLImageElement).src = `/${basePath}/logo.png`)}
|
|
||||||
unoptimized
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
alt=""
|
|
||||||
className="h-5 w-5"
|
|
||||||
/>
|
|
||||||
<span>{script.name}</span>
|
|
||||||
<span>{formattedBadge(script.type)}</span>
|
|
||||||
</div>
|
|
||||||
</CommandItem>
|
|
||||||
))}
|
|
||||||
</CommandGroup>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</CommandList>
|
</CommandList>
|
||||||
</CommandDialog>
|
</CommandDialog>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ function MobileSidebar() {
|
|||||||
<p className="text-sm font-medium">Last Viewed</p>
|
<p className="text-sm font-medium">Last Viewed</p>
|
||||||
<ScriptItem
|
<ScriptItem
|
||||||
item={lastViewedScript}
|
item={lastViewedScript}
|
||||||
|
setSelectedScript={isOnScriptsPage ? setSelectedScript : setTempSelectedScript}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -130,4 +131,3 @@ function MobileSidebar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default MobileSidebar;
|
export default MobileSidebar;
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export type Script = {
|
|||||||
slug: string;
|
slug: string;
|
||||||
categories: number[];
|
categories: number[];
|
||||||
date_created: string;
|
date_created: string;
|
||||||
type: "vm" | "ct" | "pve" | "addon" | "turnkey";
|
type: "vm" | "ct" | "pve" | "addon";
|
||||||
updateable: boolean;
|
updateable: boolean;
|
||||||
privileged: boolean;
|
privileged: boolean;
|
||||||
interface_port: number | null;
|
interface_port: number | null;
|
||||||
@@ -31,10 +31,12 @@ export type Script = {
|
|||||||
username: string | null;
|
username: string | null;
|
||||||
password: string | null;
|
password: string | null;
|
||||||
};
|
};
|
||||||
notes: {
|
notes: [
|
||||||
text: string;
|
{
|
||||||
type: keyof typeof AlertColors;
|
text: string;
|
||||||
}[];
|
type: keyof typeof AlertColors;
|
||||||
|
},
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Category = {
|
export type Category = {
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ msg_info "Installing Dependencies"
|
|||||||
$STD apt install -y nginx
|
$STD apt install -y nginx
|
||||||
msg_ok "Installed Dependencies"
|
msg_ok "Installed Dependencies"
|
||||||
|
|
||||||
export PHP_VERSION="8.4"
|
PHP_VERSION="8.4" PHP_FPM="YES" setup_php
|
||||||
PHP_FPM="YES" setup_php
|
|
||||||
setup_composer
|
setup_composer
|
||||||
setup_mariadb
|
setup_mariadb
|
||||||
MARIADB_DB_NAME="2fauth_db" MARIADB_DB_USER="2fauth" setup_mariadb_db
|
MARIADB_DB_NAME="2fauth_db" MARIADB_DB_USER="2fauth" setup_mariadb_db
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
#!/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/orhun/rustypaste
|
|
||||||
|
|
||||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
|
||||||
color
|
|
||||||
verb_ip6
|
|
||||||
catch_errors
|
|
||||||
setting_up_container
|
|
||||||
network_check
|
|
||||||
update_os
|
|
||||||
|
|
||||||
msg_info "Installing RustyPaste"
|
|
||||||
$STD apk add --no-cache rustypaste --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community
|
|
||||||
msg_ok "Installed RustyPaste"
|
|
||||||
|
|
||||||
msg_info "Configuring RustyPaste"
|
|
||||||
mkdir -p /var/lib/rustypaste
|
|
||||||
sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' /etc/rustypaste/config.toml
|
|
||||||
msg_ok "Configured RustyPaste"
|
|
||||||
|
|
||||||
msg_info "Creating Service"
|
|
||||||
cat <<'EOF' >/etc/init.d/rustypaste
|
|
||||||
#!/sbin/openrc-run
|
|
||||||
|
|
||||||
name="rustypaste"
|
|
||||||
description="RustyPaste - A minimal file upload/pastebin service"
|
|
||||||
command="/usr/bin/rustypaste"
|
|
||||||
command_args=""
|
|
||||||
command_user="root"
|
|
||||||
command_background=true
|
|
||||||
pidfile="/run/${RC_SVCNAME}.pid"
|
|
||||||
directory="/var/lib/rustypaste"
|
|
||||||
|
|
||||||
depend() {
|
|
||||||
need net
|
|
||||||
after firewall
|
|
||||||
}
|
|
||||||
|
|
||||||
start_pre() {
|
|
||||||
export CONFIG=/etc/rustypaste/config.toml
|
|
||||||
checkpath --directory --owner root:root --mode 0755 /var/lib/rustypaste
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
chmod +x /etc/init.d/rustypaste
|
|
||||||
$STD rc-update add rustypaste default
|
|
||||||
$STD rc-service rustypaste start
|
|
||||||
msg_ok "Created Service"
|
|
||||||
|
|
||||||
motd_ssh
|
|
||||||
customize
|
|
||||||
cleanup_lxc
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
# Copyright (c) 2021-2026 community-scripts ORG
|
# Copyright (c) 2021-2026 community-scripts ORG
|
||||||
# Author: vhsdream
|
# Author: vhsdream
|
||||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||||
# Source: https://codeberg.org/gelbphoenix/autocaliweb
|
# Source: https://github.com/gelbphoenix/autocaliweb
|
||||||
|
|
||||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||||
color
|
color
|
||||||
@@ -56,7 +56,7 @@ msg_ok "Installed Calibre"
|
|||||||
|
|
||||||
setup_uv
|
setup_uv
|
||||||
|
|
||||||
fetch_and_deploy_codeberg_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb"
|
fetch_and_deploy_gh_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb"
|
||||||
|
|
||||||
msg_info "Configuring Autocaliweb"
|
msg_info "Configuring Autocaliweb"
|
||||||
INSTALL_DIR="/opt/autocaliweb"
|
INSTALL_DIR="/opt/autocaliweb"
|
||||||
@@ -111,8 +111,8 @@ msg_info "Initializing databases"
|
|||||||
KEPUBIFY_PATH=$(command -v kepubify 2>/dev/null || echo "/usr/bin/kepubify")
|
KEPUBIFY_PATH=$(command -v kepubify 2>/dev/null || echo "/usr/bin/kepubify")
|
||||||
EBOOK_CONVERT_PATH=$(command -v ebook-convert 2>/dev/null || echo "/usr/bin/ebook-convert")
|
EBOOK_CONVERT_PATH=$(command -v ebook-convert 2>/dev/null || echo "/usr/bin/ebook-convert")
|
||||||
CALIBRE_BIN_DIR=$(dirname "$EBOOK_CONVERT_PATH")
|
CALIBRE_BIN_DIR=$(dirname "$EBOOK_CONVERT_PATH")
|
||||||
curl -fsSL https://codeberg.org/gelbphoenix/autocaliweb/raw/branch/main/library/metadata.db -o "$CALIBRE_LIB_DIR"/metadata.db
|
curl -fsSL https://github.com/gelbphoenix/autocaliweb/raw/refs/heads/main/library/metadata.db -o "$CALIBRE_LIB_DIR"/metadata.db
|
||||||
curl -fsSL https://codeberg.org/gelbphoenix/autocaliweb/raw/branch/main/library/app.db -o "$CONFIG_DIR"/app.db
|
curl -fsSL https://github.com/gelbphoenix/autocaliweb/raw/refs/heads/main/library/app.db -o "$CONFIG_DIR"/app.db
|
||||||
sqlite3 "$CONFIG_DIR/app.db" <<EOS
|
sqlite3 "$CONFIG_DIR/app.db" <<EOS
|
||||||
UPDATE settings SET
|
UPDATE settings SET
|
||||||
config_kepubifypath='$KEPUBIFY_PATH',
|
config_kepubifypath='$KEPUBIFY_PATH',
|
||||||
|
|||||||
@@ -14,13 +14,17 @@ network_check
|
|||||||
update_os
|
update_os
|
||||||
|
|
||||||
msg_info "Installing Dependencies"
|
msg_info "Installing Dependencies"
|
||||||
$STD apt install -y \
|
$STD apt-get install -y git
|
||||||
git \
|
$STD apt-get install -y git-lfs
|
||||||
git-lfs
|
|
||||||
msg_ok "Installed Dependencies"
|
msg_ok "Installed Dependencies"
|
||||||
|
|
||||||
fetch_and_deploy_codeberg_release "forgejo" "forgejo/forgejo" "singlefile" "latest" "/opt/forgejo" "forgejo-*-linux-amd64"
|
msg_info "Installing Forgejo"
|
||||||
ln -sf /opt/forgejo/forgejo /usr/local/bin/forgejo
|
mkdir -p /opt/forgejo
|
||||||
|
RELEASE=$(curl -fsSL https://codeberg.org/api/v1/repos/forgejo/forgejo/releases/latest | grep -oP '"tag_name":\s*"\K[^"]+' | sed 's/^v//')
|
||||||
|
curl -fsSL "https://codeberg.org/forgejo/forgejo/releases/download/v${RELEASE}/forgejo-${RELEASE}-linux-amd64" -o "/opt/forgejo/forgejo-$RELEASE-linux-amd64"
|
||||||
|
chmod +x /opt/forgejo/forgejo-$RELEASE-linux-amd64
|
||||||
|
ln -sf /opt/forgejo/forgejo-$RELEASE-linux-amd64 /usr/local/bin/forgejo
|
||||||
|
msg_ok "Installed Forgejo"
|
||||||
|
|
||||||
msg_info "Setting up Forgejo"
|
msg_info "Setting up Forgejo"
|
||||||
$STD adduser --system --shell /bin/bash --gecos 'Git Version Control' --group --disabled-password --home /home/git git
|
$STD adduser --system --shell /bin/bash --gecos 'Git Version Control' --group --disabled-password --home /home/git git
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ msg_info "Installing Dependencies"
|
|||||||
$STD apt install -y \
|
$STD apt install -y \
|
||||||
make \
|
make \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
python3-venv \
|
python3-venv
|
||||||
git
|
|
||||||
msg_ok "Installed Dependencies"
|
msg_ok "Installed Dependencies"
|
||||||
NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs
|
NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs
|
||||||
fetch_and_deploy_gh_release "grist" "gristlabs/grist-core" "tarball"
|
fetch_and_deploy_gh_release "grist" "gristlabs/grist-core" "tarball"
|
||||||
|
|||||||
@@ -13,44 +13,44 @@ setting_up_container
|
|||||||
network_check
|
network_check
|
||||||
update_os
|
update_os
|
||||||
|
|
||||||
if [ -d /dev/dri ]; then
|
echo ""
|
||||||
echo ""
|
echo ""
|
||||||
echo ""
|
echo -e "🤖 ${BL}Immich Machine Learning Options${CL}"
|
||||||
echo -e "🤖 ${BL}Immich Machine Learning Options${CL}"
|
echo "─────────────────────────────────────────"
|
||||||
echo "─────────────────────────────────────────"
|
echo "Please choose your machine-learning type:"
|
||||||
echo "Please choose your machine-learning type:"
|
echo ""
|
||||||
echo ""
|
echo " 1) CPU only (default)"
|
||||||
echo " 1) CPU only (default)"
|
echo " 2) Intel OpenVINO (requires GPU passthrough)"
|
||||||
echo " 2) Intel OpenVINO (requires GPU passthrough)"
|
echo ""
|
||||||
echo ""
|
|
||||||
|
|
||||||
read -r -p "${TAB3}Select machine-learning type [1]: " ML_TYPE
|
read -r -p "${TAB3}Select machine-learning type [1]: " ML_TYPE
|
||||||
ML_TYPE="${ML_TYPE:-1}"
|
ML_TYPE="${ML_TYPE:-1}"
|
||||||
if [[ "$ML_TYPE" == "2" ]]; then
|
if [[ "$ML_TYPE" == "2" ]]; then
|
||||||
msg_info "Installing OpenVINO dependencies"
|
msg_info "Installing OpenVINO dependencies"
|
||||||
touch ~/.openvino
|
touch ~/.openvino
|
||||||
$STD apt install -y --no-install-recommends patchelf
|
$STD apt install -y --no-install-recommends patchelf
|
||||||
tmp_dir=$(mktemp -d)
|
tmp_dir=$(mktemp -d)
|
||||||
$STD pushd "$tmp_dir"
|
$STD pushd "$tmp_dir"
|
||||||
curl -fsSLO https://raw.githubusercontent.com/immich-app/base-images/refs/heads/main/server/Dockerfile
|
curl -fsSLO https://raw.githubusercontent.com/immich-app/base-images/refs/heads/main/server/Dockerfile
|
||||||
readarray -t INTEL_URLS < <(
|
readarray -t INTEL_URLS < <(
|
||||||
sed -n "/intel-[igc|opencl]/p" ./Dockerfile | awk '{print $2}'
|
sed -n "/intel-[igc|opencl]/p" ./Dockerfile | awk '{print $2}'
|
||||||
sed -n "/libigdgmm12/p" ./Dockerfile | awk '{print $3}'
|
sed -n "/libigdgmm12/p" ./Dockerfile | awk '{print $3}'
|
||||||
)
|
)
|
||||||
for url in "${INTEL_URLS[@]}"; do
|
for url in "${INTEL_URLS[@]}"; do
|
||||||
curl -fsSLO "$url"
|
curl -fsSLO "$url"
|
||||||
done
|
done
|
||||||
$STD apt install -y ./libigdgmm12*.deb
|
$STD apt install -y ./libigdgmm12*.deb
|
||||||
rm ./libigdgmm12*.deb
|
rm ./libigdgmm12*.deb
|
||||||
$STD apt install -y ./*.deb
|
$STD apt install -y ./*.deb
|
||||||
$STD apt-mark hold libigdgmm12
|
$STD apt-mark hold libigdgmm12
|
||||||
$STD popd
|
$STD popd
|
||||||
rm -rf "$tmp_dir"
|
rm -rf "$tmp_dir"
|
||||||
dpkg-query -W -f='${Version}\n' intel-opencl-icd >~/.intel_version
|
dpkg-query -W -f='${Version}\n' intel-opencl-icd >~/.intel_version
|
||||||
msg_ok "Installed OpenVINO dependencies"
|
msg_ok "Installed OpenVINO dependencies"
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
setup_uv
|
||||||
|
|
||||||
msg_info "Installing dependencies"
|
msg_info "Installing dependencies"
|
||||||
$STD apt install --no-install-recommends -y \
|
$STD apt install --no-install-recommends -y \
|
||||||
git \
|
git \
|
||||||
@@ -144,7 +144,8 @@ msg_info "Installing packages from Debian Testing repo"
|
|||||||
$STD apt install -t testing --no-install-recommends -yqq libmimalloc3 libde265-dev
|
$STD apt install -t testing --no-install-recommends -yqq libmimalloc3 libde265-dev
|
||||||
msg_ok "Installed packages from Debian Testing repo"
|
msg_ok "Installed packages from Debian Testing repo"
|
||||||
|
|
||||||
setup_uv
|
PNPM_VERSION="$(curl -fsSL "https://raw.githubusercontent.com/immich-app/immich/refs/heads/main/package.json" | jq -r '.packageManager | split("@")[1]')"
|
||||||
|
NODE_VERSION="24" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs
|
||||||
PG_VERSION="16" PG_MODULES="pgvector" setup_postgresql
|
PG_VERSION="16" PG_MODULES="pgvector" setup_postgresql
|
||||||
|
|
||||||
VCHORD_RELEASE="0.5.3"
|
VCHORD_RELEASE="0.5.3"
|
||||||
@@ -289,9 +290,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.5.3" "$SRC_DIR"
|
fetch_and_deploy_gh_release "immich" "immich-app/immich" "tarball" "v2.5.2" "$SRC_DIR"
|
||||||
PNPM_VERSION="$(jq -r '.packageManager | split("@")[1]' ${SRC_DIR}/package.json)"
|
|
||||||
NODE_VERSION="24" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs
|
|
||||||
|
|
||||||
msg_info "Installing Immich (patience)"
|
msg_info "Installing Immich (patience)"
|
||||||
|
|
||||||
|
|||||||
@@ -1,145 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Copyright (c) 2021-2025 community-scripts ORG
|
|
||||||
# Author: snazzybean
|
|
||||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
|
||||||
# Source: https://github.com/TomBursch/kitchenowl
|
|
||||||
|
|
||||||
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 \
|
|
||||||
build-essential \
|
|
||||||
gfortran \
|
|
||||||
pkg-config \
|
|
||||||
ninja-build \
|
|
||||||
autoconf \
|
|
||||||
automake \
|
|
||||||
libpq-dev \
|
|
||||||
libffi-dev \
|
|
||||||
libssl-dev \
|
|
||||||
libpcre2-dev \
|
|
||||||
libre2-dev \
|
|
||||||
libxml2-dev \
|
|
||||||
libxslt-dev \
|
|
||||||
libopenblas-dev \
|
|
||||||
liblapack-dev \
|
|
||||||
zlib1g-dev \
|
|
||||||
libjpeg62-turbo-dev \
|
|
||||||
libsqlite3-dev \
|
|
||||||
libexpat1-dev \
|
|
||||||
libicu-dev
|
|
||||||
msg_ok "Installed Dependencies"
|
|
||||||
|
|
||||||
PYTHON_VERSION="3.14" setup_uv
|
|
||||||
fetch_and_deploy_gh_release "kitchenowl" "TomBursch/kitchenowl" "tarball" "latest" "/opt/kitchenowl"
|
|
||||||
rm -rf /opt/kitchenowl/web
|
|
||||||
fetch_and_deploy_gh_release "kitchenowl-web" "TomBursch/kitchenowl" "prebuild" "latest" "/opt/kitchenowl/web" "kitchenowl_Web.tar.gz"
|
|
||||||
|
|
||||||
msg_info "Setting up KitchenOwl"
|
|
||||||
cd /opt/kitchenowl/backend
|
|
||||||
$STD uv sync --no-dev
|
|
||||||
sed -i 's/default=True/default=False/' /opt/kitchenowl/backend/wsgi.py
|
|
||||||
mkdir -p /nltk_data
|
|
||||||
$STD uv run python -m nltk.downloader -d /nltk_data averaged_perceptron_tagger_eng
|
|
||||||
JWT_SECRET=$(openssl rand -hex 32)
|
|
||||||
mkdir -p /opt/kitchenowl/data
|
|
||||||
cat <<EOF >/opt/kitchenowl/kitchenowl.env
|
|
||||||
STORAGE_PATH=/opt/kitchenowl/data
|
|
||||||
JWT_SECRET_KEY=${JWT_SECRET}
|
|
||||||
NLTK_DATA=/nltk_data
|
|
||||||
FRONT_URL=http://${LOCAL_IP}
|
|
||||||
FLASK_APP=wsgi.py
|
|
||||||
FLASK_ENV=production
|
|
||||||
EOF
|
|
||||||
set -a
|
|
||||||
source /opt/kitchenowl/kitchenowl.env
|
|
||||||
set +a
|
|
||||||
$STD uv run flask db upgrade
|
|
||||||
msg_ok "Set up KitchenOwl"
|
|
||||||
|
|
||||||
msg_info "Creating Systemd Service"
|
|
||||||
cat <<EOF >/etc/systemd/system/kitchenowl.service
|
|
||||||
[Unit]
|
|
||||||
Description=KitchenOwl Backend
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
User=root
|
|
||||||
WorkingDirectory=/opt/kitchenowl/backend
|
|
||||||
EnvironmentFile=/opt/kitchenowl/kitchenowl.env
|
|
||||||
ExecStart=/usr/local/bin/uv run wsgi.py
|
|
||||||
Restart=on-failure
|
|
||||||
RestartSec=5
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
EOF
|
|
||||||
systemctl enable -q --now kitchenowl
|
|
||||||
msg_ok "Created and Started Service"
|
|
||||||
|
|
||||||
msg_info "Configuring Nginx"
|
|
||||||
rm -f /etc/nginx/sites-enabled/default
|
|
||||||
cat <<'EOF' >/etc/nginx/sites-available/kitchenowl.conf
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name _;
|
|
||||||
|
|
||||||
root /opt/kitchenowl/web;
|
|
||||||
index index.html;
|
|
||||||
|
|
||||||
client_max_body_size 100M;
|
|
||||||
|
|
||||||
# Security Headers
|
|
||||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
||||||
add_header X-Content-Type-Options "nosniff" always;
|
|
||||||
add_header X-XSS-Protection "1; mode=block" always;
|
|
||||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ /index.html;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /api {
|
|
||||||
proxy_pass http://127.0.0.1:5000;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
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;
|
|
||||||
proxy_connect_timeout 60s;
|
|
||||||
proxy_send_timeout 60s;
|
|
||||||
proxy_read_timeout 60s;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /socket.io {
|
|
||||||
proxy_pass http://127.0.0.1:5000;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
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;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection "upgrade";
|
|
||||||
# WebSocket Timeouts - allow long-lived connections
|
|
||||||
proxy_read_timeout 86400s;
|
|
||||||
proxy_send_timeout 86400s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
ln -sf /etc/nginx/sites-available/kitchenowl.conf /etc/nginx/sites-enabled/
|
|
||||||
rm -f /etc/nginx/sites-enabled/default
|
|
||||||
$STD systemctl reload nginx
|
|
||||||
msg_ok "Configured Nginx"
|
|
||||||
|
|
||||||
motd_ssh
|
|
||||||
customize
|
|
||||||
cleanup_lxc
|
|
||||||
@@ -13,7 +13,13 @@ setting_up_container
|
|||||||
network_check
|
network_check
|
||||||
update_os
|
update_os
|
||||||
|
|
||||||
fetch_and_deploy_codeberg_release "readeck" "readeck/readeck" "singlefile" "latest" "/opt/readeck" "readeck-*-linux-amd64"
|
msg_info "Installing Readeck"
|
||||||
|
LATEST=$(curl -fsSL https://codeberg.org/readeck/readeck/releases/ | grep -oP '/releases/tag/\K\d+\.\d+\.\d+' | head -1)
|
||||||
|
mkdir -p /opt/readeck
|
||||||
|
cd /opt/readeck
|
||||||
|
curl -fsSL "https://codeberg.org/readeck/readeck/releases/download/${LATEST}/readeck-${LATEST}-linux-amd64" -o "readeck"
|
||||||
|
chmod a+x readeck
|
||||||
|
msg_ok "Installed Readeck"
|
||||||
|
|
||||||
msg_info "Creating Service"
|
msg_info "Creating Service"
|
||||||
cat <<EOF >/etc/systemd/system/readeck.service
|
cat <<EOF >/etc/systemd/system/readeck.service
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Copyright (c) 2021-2026 community-scripts ORG
|
|
||||||
# Author: GoldenSpringness | MickLesk (CanbiZ)
|
|
||||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
|
||||||
# Source: https://github.com/orhun/rustypaste
|
|
||||||
|
|
||||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
|
||||||
color
|
|
||||||
verb_ip6
|
|
||||||
catch_errors
|
|
||||||
setting_up_container
|
|
||||||
network_check
|
|
||||||
update_os
|
|
||||||
|
|
||||||
fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "prebuild" "latest" "/opt/rustypaste" "*x86_64-unknown-linux-gnu.tar.gz"
|
|
||||||
fetch_and_deploy_gh_release "rustypaste-cli" "orhun/rustypaste-cli" "prebuild" "latest" "/usr/local/bin" "*x86_64-unknown-linux-gnu.tar.gz"
|
|
||||||
|
|
||||||
msg_info "Setting up RustyPaste"
|
|
||||||
cd /opt/rustypaste
|
|
||||||
sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml
|
|
||||||
msg_ok "Set up RustyPaste"
|
|
||||||
|
|
||||||
msg_info "Creating Service"
|
|
||||||
cat <<EOF >/etc/systemd/system/rustypaste.service
|
|
||||||
[Unit]
|
|
||||||
Description=rustypaste Service
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
WorkingDirectory=/opt/rustypaste
|
|
||||||
ExecStart=/opt/rustypaste/rustypaste
|
|
||||||
Restart=always
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
EOF
|
|
||||||
systemctl enable -q --now rustypaste
|
|
||||||
msg_ok "Created Service"
|
|
||||||
|
|
||||||
motd_ssh
|
|
||||||
customize
|
|
||||||
cleanup_lxc
|
|
||||||
@@ -41,34 +41,39 @@ $STD cargo build --release --bin server
|
|||||||
mv ./target/release/server /usr/bin/scanopy-server
|
mv ./target/release/server /usr/bin/scanopy-server
|
||||||
msg_ok "Built scanopy-server"
|
msg_ok "Built scanopy-server"
|
||||||
|
|
||||||
|
msg_info "Building scanopy-daemon"
|
||||||
|
$STD cargo build --release --bin daemon
|
||||||
|
cp ./target/release/daemon /usr/bin/scanopy-daemon
|
||||||
|
msg_ok "Built scanopy-daemon"
|
||||||
|
|
||||||
msg_info "Configuring server for first-run"
|
msg_info "Configuring server for first-run"
|
||||||
cat <<EOF >/opt/scanopy/.env
|
cat <<EOF >/opt/scanopy/.env
|
||||||
### - SERVER
|
### - SERVER
|
||||||
SCANOPY_DATABASE_URL=postgresql://$PG_DB_USER:$PG_DB_PASS@localhost:5432/$PG_DB_NAME
|
scanopy_DATABASE_URL=postgresql://$PG_DB_USER:$PG_DB_PASS@localhost:5432/$PG_DB_NAME
|
||||||
SCANOPY_WEB_EXTERNAL_PATH="/opt/scanopy/ui/build"
|
scanopy_WEB_EXTERNAL_PATH="/opt/scanopy/ui/build"
|
||||||
SCANOPY_PUBLIC_URL=http://${LOCAL_IP}:60072
|
scanopy_PUBLIC_URL=http://${LOCAL_IP}:60072
|
||||||
SCANOPY_SERVER_PORT=60072
|
scanopy_SERVER_PORT=60072
|
||||||
SCANOPY_LOG_LEVEL=info
|
scanopy_LOG_LEVEL=info
|
||||||
SCANOPY_INTEGRATED_DAEMON_URL=http://127.0.0.1:60073
|
scanopy_INTEGRATED_DAEMON_URL=http://127.0.0.1:60073
|
||||||
## - uncomment to disable signups
|
## - uncomment to disable signups
|
||||||
# SCANOPY_DISABLE_REGISTRATION=true
|
# scanopy_DISABLE_REGISTRATION=true
|
||||||
## - uncomment when using TLS
|
## - uncomment when using TLS
|
||||||
# SCANOPY_USE_SECURE_SESSION_COOKIES=true
|
# scanopy_USE_SECURE_SESSION_COOKIES=true
|
||||||
## - see https://github.com/imbolc/axum-client-ip?tab=readme-ov-file#configurable-vs-specific-extractors
|
## - see https://github.com/imbolc/axum-client-ip?tab=readme-ov-file#configurable-vs-specific-extractors
|
||||||
## - before uncommenting the below
|
## - before uncommenting the below
|
||||||
# SCANOPY_CLIENT_IP_SOURCE=
|
# scanopy_CLIENT_IP_SOURCE=
|
||||||
|
|
||||||
### - SMTP (password reset and notifications - optional)
|
### - SMTP (password reset and notifications - optional)
|
||||||
# SCANOPY_SMTP_RELAY=smtp.gmail.com:587
|
# scanopy_SMTP_RELAY=smtp.gmail.com:587
|
||||||
# SCANOPY_SMTP_USERNAME=your-email@gmail.com
|
# scanopy_SMTP_USERNAME=your-email@gmail.com
|
||||||
# SCANOPY_SMTP_PASSWORD=your-app-password
|
# scanopy_SMTP_PASSWORD=your-app-password
|
||||||
# SCANOPY_SMTP_EMAIL=scanopy@yourdomain.tld
|
# scanopy_SMTP_EMAIL=scanopy@yourdomain.tld
|
||||||
|
|
||||||
### - INTEGRATED DAEMON
|
### - INTEGRATED DAEMON
|
||||||
SCANOPY_SERVER_URL=http://127.0.0.1:60072
|
scanopy_SERVER_URL=http://127.0.0.1:60072
|
||||||
SCANOPY_BIND_ADDRESS=0.0.0.0
|
scanopy_BIND_ADDRESS=0.0.0.0
|
||||||
SCANOPY_NAME="scanopy-daemon"
|
scanopy_NAME="scanopy-daemon"
|
||||||
SCANOPY_HEARTBEAT_INTERVAL=30
|
scanopy_HEARTBEAT_INTERVAL=30
|
||||||
|
|
||||||
### - see https://github.com/scanopy/scanopy/blob/main/docs/CONFIGURATION.md for more options
|
### - see https://github.com/scanopy/scanopy/blob/main/docs/CONFIGURATION.md for more options
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
@@ -105,13 +105,14 @@ elif [[ "$DEPLOYMENT_TYPE" == "4" ]]; then
|
|||||||
sed -i '/_BYPASS=/s/true/false/' /etc/shelfmark/.env
|
sed -i '/_BYPASS=/s/true/false/' /etc/shelfmark/.env
|
||||||
else
|
else
|
||||||
DEPLOYMENT_TYPE="1"
|
DEPLOYMENT_TYPE="1"
|
||||||
|
CHROME_VERSION=$(curl -fsSL https://raw.githubusercontent.com/calibrain/shelfmark/refs/heads/main/Dockerfile | sed -n '/chromium=/s/[^=]*=//p' | awk '{print $1}')
|
||||||
msg_info "Installing internal bypasser dependencies"
|
msg_info "Installing internal bypasser dependencies"
|
||||||
$STD apt install -y --no-install-recommends \
|
$STD apt install -y --no-install-recommends \
|
||||||
xvfb \
|
xvfb \
|
||||||
ffmpeg \
|
ffmpeg \
|
||||||
chromium-common \
|
chromium-common=${CHROME_VERSION} \
|
||||||
chromium \
|
chromium=${CHROME_VERSION} \
|
||||||
chromium-driver \
|
chromium-driver=${CHROME_VERSION} \
|
||||||
python3-tk
|
python3-tk
|
||||||
msg_ok "Installed internal bypasser dependencies"
|
msg_ok "Installed internal bypasser dependencies"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,89 +0,0 @@
|
|||||||
#!/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://wealthfolio.app/
|
|
||||||
|
|
||||||
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 \
|
|
||||||
pkg-config \
|
|
||||||
libssl-dev \
|
|
||||||
build-essential \
|
|
||||||
libsqlite3-dev \
|
|
||||||
argon2
|
|
||||||
msg_ok "Installed Dependencies"
|
|
||||||
|
|
||||||
setup_rust
|
|
||||||
NODE_MODULE="pnpm" setup_nodejs
|
|
||||||
fetch_and_deploy_gh_release "wealthfolio" "afadil/wealthfolio" "tarball"
|
|
||||||
|
|
||||||
msg_info "Building Frontend (patience)"
|
|
||||||
cd /opt/wealthfolio
|
|
||||||
$STD pnpm install --frozen-lockfile
|
|
||||||
$STD pnpm tsc
|
|
||||||
$STD pnpm vite build
|
|
||||||
msg_ok "Built Frontend"
|
|
||||||
|
|
||||||
msg_info "Building Backend (patience)"
|
|
||||||
cd /opt/wealthfolio/src-server
|
|
||||||
$STD cargo build --release --manifest-path Cargo.toml
|
|
||||||
cp /opt/wealthfolio/src-server/target/release/wealthfolio-server /usr/local/bin/wealthfolio-server
|
|
||||||
chmod +x /usr/local/bin/wealthfolio-server
|
|
||||||
msg_ok "Built Backend"
|
|
||||||
|
|
||||||
msg_info "Configuring Wealthfolio"
|
|
||||||
mkdir -p /opt/wealthfolio_data
|
|
||||||
SECRET_KEY=$(openssl rand -base64 32)
|
|
||||||
WF_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-16)
|
|
||||||
WF_PASSWORD_HASH=$(echo -n "$WF_PASSWORD" | argon2 "$(openssl rand -base64 16)" -id -e)
|
|
||||||
cat <<EOF >/opt/wealthfolio/.env
|
|
||||||
WF_LISTEN_ADDR=0.0.0.0:8080
|
|
||||||
WF_DB_PATH=/opt/wealthfolio_data/wealthfolio.db
|
|
||||||
WF_SECRET_KEY=${SECRET_KEY}
|
|
||||||
WF_AUTH_PASSWORD_HASH=${WF_PASSWORD_HASH}
|
|
||||||
WF_STATIC_DIR=/opt/wealthfolio/dist
|
|
||||||
WF_CORS_ALLOW_ORIGINS=*
|
|
||||||
WF_REQUEST_TIMEOUT_MS=30000
|
|
||||||
EOF
|
|
||||||
echo "WF_PASSWORD=${WF_PASSWORD}" >~/wealthfolio.creds
|
|
||||||
msg_ok "Configured Wealthfolio"
|
|
||||||
|
|
||||||
msg_info "Cleaning Up"
|
|
||||||
rm -rf /opt/wealthfolio/src-server/target
|
|
||||||
rm -rf /root/.cargo/registry
|
|
||||||
rm -rf /opt/wealthfolio/node_modules
|
|
||||||
msg_ok "Cleaned Up"
|
|
||||||
|
|
||||||
msg_info "Creating Service"
|
|
||||||
cat <<EOF >/etc/systemd/system/wealthfolio.service
|
|
||||||
[Unit]
|
|
||||||
Description=Wealthfolio Investment Tracker
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
User=root
|
|
||||||
WorkingDirectory=/opt/wealthfolio
|
|
||||||
EnvironmentFile=/opt/wealthfolio/.env
|
|
||||||
ExecStart=/usr/local/bin/wealthfolio-server
|
|
||||||
Restart=on-failure
|
|
||||||
RestartSec=5
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
EOF
|
|
||||||
systemctl enable -q --now wealthfolio
|
|
||||||
msg_ok "Created Service"
|
|
||||||
|
|
||||||
motd_ssh
|
|
||||||
customize
|
|
||||||
cleanup_lxc
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Copyright (c) 2021-2026 community-scripts ORG
|
|
||||||
# Author: Dunky13
|
|
||||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
|
||||||
# Source: https://github.com/cmintey/wishlist
|
|
||||||
|
|
||||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
|
||||||
color
|
|
||||||
verb_ip6
|
|
||||||
catch_errors
|
|
||||||
setting_up_container
|
|
||||||
network_check
|
|
||||||
update_os
|
|
||||||
|
|
||||||
msg_info "Installing dependencies"
|
|
||||||
$STD apt install -y \
|
|
||||||
build-essential \
|
|
||||||
openssl \
|
|
||||||
caddy
|
|
||||||
msg_ok "Installed dependencies"
|
|
||||||
|
|
||||||
NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs
|
|
||||||
fetch_and_deploy_gh_release "wishlist" "cmintey/wishlist" "tarball"
|
|
||||||
LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist")
|
|
||||||
|
|
||||||
msg_info "Installing Wishlist"
|
|
||||||
cd /opt/wishlist
|
|
||||||
cp .env.example .env
|
|
||||||
sed -i "s|^ORIGIN=.*|ORIGIN=http://${LOCAL_IP}:3280|" /opt/wishlist/.env
|
|
||||||
echo "" >>/opt/wishlist/.env
|
|
||||||
echo "NODE_ENV=production" >>/opt/wishlist/.env
|
|
||||||
$STD pnpm install
|
|
||||||
$STD pnpm svelte-kit sync
|
|
||||||
$STD pnpm prisma generate
|
|
||||||
sed -i 's|/usr/src/app/|/opt/wishlist/|g' $(grep -rl '/usr/src/app/' /opt/wishlist)
|
|
||||||
export VERSION="v${LATEST_APP_VERSION}"
|
|
||||||
export SHA="v${LATEST_APP_VERSION}"
|
|
||||||
$STD pnpm run build
|
|
||||||
$STD pnpm prune --prod
|
|
||||||
chmod +x /opt/wishlist/entrypoint.sh
|
|
||||||
mkdir -p /opt/wishlist/uploads
|
|
||||||
mkdir -p /opt/wishlist/data
|
|
||||||
msg_ok "Installed Wishlist"
|
|
||||||
|
|
||||||
msg_info "Creating Service"
|
|
||||||
cat <<EOF >/etc/systemd/system/wishlist.service
|
|
||||||
[Unit]
|
|
||||||
Description=Wishlist Service
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
WorkingDirectory=/opt/wishlist
|
|
||||||
EnvironmentFile=/opt/wishlist/.env
|
|
||||||
ExecStart=/usr/bin/env sh -c './entrypoint.sh'
|
|
||||||
Restart=on-failure
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
EOF
|
|
||||||
systemctl enable -q --now wishlist
|
|
||||||
msg_ok "Created Service"
|
|
||||||
|
|
||||||
motd_ssh
|
|
||||||
customize
|
|
||||||
cleanup_lxc
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Copyright (c) 2021-2026 community-scripts ORG
|
|
||||||
# Author: StellaeAlis
|
|
||||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
|
||||||
# Source: https://github.com/writefreely/writefreely
|
|
||||||
|
|
||||||
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 crudini
|
|
||||||
msg_ok "Installed Dependencies"
|
|
||||||
|
|
||||||
setup_mariadb
|
|
||||||
MARIADB_DB_NAME="writefreely" MARIADB_DB_USER="writefreely" setup_mariadb_db
|
|
||||||
fetch_and_deploy_gh_release "writefreely" "writefreely/writefreely" "prebuild" "latest" "/opt/writefreely" "writefreely_*_linux_amd64.tar.gz"
|
|
||||||
|
|
||||||
msg_info "Setting up WriteFreely"
|
|
||||||
cd /opt/writefreely
|
|
||||||
$STD ./writefreely config generate
|
|
||||||
$STD ./writefreely keys generate
|
|
||||||
msg_ok "Setup WriteFreely"
|
|
||||||
|
|
||||||
msg_info "Configuring WriteFreely"
|
|
||||||
$STD crudini --set config.ini server port 80
|
|
||||||
$STD crudini --set config.ini server bind $LOCAL_IP
|
|
||||||
$STD crudini --set config.ini database username $MARIADB_DB_USER
|
|
||||||
$STD crudini --set config.ini database password $MARIADB_DB_PASS
|
|
||||||
$STD crudini --set config.ini database database $MARIADB_DB_NAME
|
|
||||||
$STD crudini --set config.ini app host http://$LOCAL_IP:80
|
|
||||||
$STD ./writefreely db init
|
|
||||||
ln -s /opt/writefreely/writefreely /usr/local/bin/writefreely
|
|
||||||
msg_ok "Configured WriteFreely"
|
|
||||||
|
|
||||||
msg_info "Creating Service"
|
|
||||||
cat <<EOF >/etc/systemd/system/writefreely.service
|
|
||||||
[Unit]
|
|
||||||
Description=WriteFreely Service
|
|
||||||
After=syslog.target network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
User=root
|
|
||||||
WorkingDirectory=/opt/writefreely
|
|
||||||
ExecStart=/opt/writefreely/writefreely
|
|
||||||
Restart=on-failure
|
|
||||||
RestartSec=5
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
EOF
|
|
||||||
systemctl enable -q --now writefreely
|
|
||||||
msg_ok "Created Service"
|
|
||||||
|
|
||||||
motd_ssh
|
|
||||||
customize
|
|
||||||
cleanup_lxc
|
|
||||||
629
misc/tools.func
629
misc/tools.func
@@ -821,54 +821,6 @@ github_api_call() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Codeberg API call with retry logic
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
codeberg_api_call() {
|
|
||||||
local url="$1"
|
|
||||||
local output_file="${2:-/dev/stdout}"
|
|
||||||
local max_retries=3
|
|
||||||
local retry_delay=2
|
|
||||||
|
|
||||||
for attempt in $(seq 1 $max_retries); do
|
|
||||||
local http_code
|
|
||||||
http_code=$(curl -fsSL -w "%{http_code}" -o "$output_file" \
|
|
||||||
-H "Accept: application/json" \
|
|
||||||
"$url" 2>/dev/null || echo "000")
|
|
||||||
|
|
||||||
case "$http_code" in
|
|
||||||
200)
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
403)
|
|
||||||
# Rate limit - retry
|
|
||||||
if [[ $attempt -lt $max_retries ]]; then
|
|
||||||
msg_warn "Codeberg API rate limit, waiting ${retry_delay}s... (attempt $attempt/$max_retries)"
|
|
||||||
sleep "$retry_delay"
|
|
||||||
retry_delay=$((retry_delay * 2))
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
msg_error "Codeberg API rate limit exceeded."
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
404)
|
|
||||||
msg_error "Codeberg API endpoint not found: $url"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
if [[ $attempt -lt $max_retries ]]; then
|
|
||||||
sleep "$retry_delay"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
msg_error "Codeberg API call failed with HTTP $http_code"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
should_upgrade() {
|
should_upgrade() {
|
||||||
local current="$1"
|
local current="$1"
|
||||||
local target="$2"
|
local target="$2"
|
||||||
@@ -1433,37 +1385,6 @@ get_latest_github_release() {
|
|||||||
echo "$version"
|
echo "$version"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Get latest Codeberg release version
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
get_latest_codeberg_release() {
|
|
||||||
local repo="$1"
|
|
||||||
local strip_v="${2:-true}"
|
|
||||||
local temp_file=$(mktemp)
|
|
||||||
|
|
||||||
# Codeberg API: get all releases and pick the first non-draft/non-prerelease
|
|
||||||
if ! codeberg_api_call "https://codeberg.org/api/v1/repos/${repo}/releases" "$temp_file"; then
|
|
||||||
rm -f "$temp_file"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
local version
|
|
||||||
# Codeberg uses same JSON structure but releases endpoint returns array
|
|
||||||
version=$(jq -r '[.[] | select(.draft==false and .prerelease==false)][0].tag_name // empty' "$temp_file")
|
|
||||||
|
|
||||||
if [[ "$strip_v" == "true" ]]; then
|
|
||||||
version="${version#v}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
rm -f "$temp_file"
|
|
||||||
|
|
||||||
if [[ -z "$version" ]]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "$version"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Debug logging (only if DEBUG=1)
|
# Debug logging (only if DEBUG=1)
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
@@ -1531,8 +1452,7 @@ check_for_gh_release() {
|
|||||||
local app="$1"
|
local app="$1"
|
||||||
local source="$2"
|
local source="$2"
|
||||||
local pinned_version_in="${3:-}" # optional
|
local pinned_version_in="${3:-}" # optional
|
||||||
local app_lc=""
|
local app_lc="${app,,}"
|
||||||
app_lc="$(echo "${app,,}" | tr -d ' ')"
|
|
||||||
local current_file="$HOME/.${app_lc}"
|
local current_file="$HOME/.${app_lc}"
|
||||||
|
|
||||||
msg_info "Checking for update: ${app}"
|
msg_info "Checking for update: ${app}"
|
||||||
@@ -1639,119 +1559,6 @@ check_for_gh_release() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Checks for new Codeberg release (latest tag).
|
|
||||||
#
|
|
||||||
# Description:
|
|
||||||
# - Queries the Codeberg API for the latest release tag
|
|
||||||
# - Compares it to a local cached version (~/.<app>)
|
|
||||||
# - If newer, sets global CHECK_UPDATE_RELEASE and returns 0
|
|
||||||
#
|
|
||||||
# Usage:
|
|
||||||
# if check_for_codeberg_release "autocaliweb" "gelbphoenix/autocaliweb" [optional] "v0.11.3"; then
|
|
||||||
# # trigger update...
|
|
||||||
# fi
|
|
||||||
# exit 0
|
|
||||||
# } (end of update_script not from the function)
|
|
||||||
#
|
|
||||||
# Notes:
|
|
||||||
# - Requires `jq` (auto-installed if missing)
|
|
||||||
# - Does not modify anything, only checks version state
|
|
||||||
# - Does not support pre-releases
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
check_for_codeberg_release() {
|
|
||||||
local app="$1"
|
|
||||||
local source="$2"
|
|
||||||
local pinned_version_in="${3:-}" # optional
|
|
||||||
local app_lc="${app,,}"
|
|
||||||
local current_file="$HOME/.${app_lc}"
|
|
||||||
|
|
||||||
msg_info "Checking for update: ${app}"
|
|
||||||
|
|
||||||
# DNS check
|
|
||||||
if ! getent hosts codeberg.org >/dev/null 2>&1; then
|
|
||||||
msg_error "Network error: cannot resolve codeberg.org"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
ensure_dependencies jq
|
|
||||||
|
|
||||||
# Fetch releases from Codeberg API
|
|
||||||
local releases_json=""
|
|
||||||
releases_json=$(curl -fsSL --max-time 20 \
|
|
||||||
-H 'Accept: application/json' \
|
|
||||||
"https://codeberg.org/api/v1/repos/${source}/releases" 2>/dev/null) || {
|
|
||||||
msg_error "Unable to fetch releases for ${app}"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
mapfile -t raw_tags < <(jq -r '.[] | select(.draft==false and .prerelease==false) | .tag_name' <<<"$releases_json")
|
|
||||||
if ((${#raw_tags[@]} == 0)); then
|
|
||||||
msg_error "No stable releases found for ${app}"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
local clean_tags=()
|
|
||||||
for t in "${raw_tags[@]}"; do
|
|
||||||
clean_tags+=("${t#v}")
|
|
||||||
done
|
|
||||||
|
|
||||||
local latest_raw="${raw_tags[0]}"
|
|
||||||
local latest_clean="${clean_tags[0]}"
|
|
||||||
|
|
||||||
# current installed (stored without v)
|
|
||||||
local current=""
|
|
||||||
if [[ -f "$current_file" ]]; then
|
|
||||||
current="$(<"$current_file")"
|
|
||||||
else
|
|
||||||
# Migration: search for any /opt/*_version.txt
|
|
||||||
local legacy_files
|
|
||||||
mapfile -t legacy_files < <(find /opt -maxdepth 1 -type f -name "*_version.txt" 2>/dev/null)
|
|
||||||
if ((${#legacy_files[@]} == 1)); then
|
|
||||||
current="$(<"${legacy_files[0]}")"
|
|
||||||
echo "${current#v}" >"$current_file"
|
|
||||||
rm -f "${legacy_files[0]}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
current="${current#v}"
|
|
||||||
|
|
||||||
# Pinned version handling
|
|
||||||
if [[ -n "$pinned_version_in" ]]; then
|
|
||||||
local pin_clean="${pinned_version_in#v}"
|
|
||||||
local match_raw=""
|
|
||||||
for i in "${!clean_tags[@]}"; do
|
|
||||||
if [[ "${clean_tags[$i]}" == "$pin_clean" ]]; then
|
|
||||||
match_raw="${raw_tags[$i]}"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ -z "$match_raw" ]]; then
|
|
||||||
msg_error "Pinned version ${pinned_version_in} not found upstream"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$current" != "$pin_clean" ]]; then
|
|
||||||
CHECK_UPDATE_RELEASE="$match_raw"
|
|
||||||
msg_ok "Update available: ${app} ${current:-not installed} → ${pin_clean}"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
msg_ok "No update available: ${app} is already on pinned version (${current})"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# No pinning → use latest
|
|
||||||
if [[ -z "$current" || "$current" != "$latest_clean" ]]; then
|
|
||||||
CHECK_UPDATE_RELEASE="$latest_raw"
|
|
||||||
msg_ok "Update available: ${app} ${current:-not installed} → ${latest_clean}"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
msg_ok "No update available: ${app} (${latest_clean})"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Creates and installs self-signed certificates.
|
# Creates and installs self-signed certificates.
|
||||||
#
|
#
|
||||||
@@ -1841,440 +1648,6 @@ function ensure_usr_local_bin_persist() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Downloads and deploys latest Codeberg release (source, binary, tarball, asset).
|
|
||||||
#
|
|
||||||
# Description:
|
|
||||||
# - Fetches latest release metadata from Codeberg API
|
|
||||||
# - Supports the following modes:
|
|
||||||
# - tarball: Source code tarball (default if omitted)
|
|
||||||
# - source: Alias for tarball (same behavior)
|
|
||||||
# - binary: .deb package install (arch-dependent)
|
|
||||||
# - prebuild: Prebuilt .tar.gz archive (e.g. Go binaries)
|
|
||||||
# - singlefile: Standalone binary (no archive, direct chmod +x install)
|
|
||||||
# - tag: Direct tag download (bypasses Release API)
|
|
||||||
# - Handles download, extraction/installation and version tracking in ~/.<app>
|
|
||||||
#
|
|
||||||
# Parameters:
|
|
||||||
# $1 APP - Application name (used for install path and version file)
|
|
||||||
# $2 REPO - Codeberg repository in form user/repo
|
|
||||||
# $3 MODE - Release type:
|
|
||||||
# tarball → source tarball (.tar.gz)
|
|
||||||
# binary → .deb file (auto-arch matched)
|
|
||||||
# prebuild → prebuilt archive (e.g. tar.gz)
|
|
||||||
# singlefile→ standalone binary (chmod +x)
|
|
||||||
# tag → direct tag (bypasses Release API)
|
|
||||||
# $4 VERSION - Optional release tag (default: latest)
|
|
||||||
# $5 TARGET_DIR - Optional install path (default: /opt/<app>)
|
|
||||||
# $6 ASSET_FILENAME - Required for:
|
|
||||||
# - prebuild → archive filename or pattern
|
|
||||||
# - singlefile→ binary filename or pattern
|
|
||||||
#
|
|
||||||
# Examples:
|
|
||||||
# # 1. Minimal: Fetch and deploy source tarball
|
|
||||||
# fetch_and_deploy_codeberg_release "autocaliweb" "gelbphoenix/autocaliweb"
|
|
||||||
#
|
|
||||||
# # 2. Binary install via .deb asset (architecture auto-detected)
|
|
||||||
# fetch_and_deploy_codeberg_release "myapp" "myuser/myapp" "binary"
|
|
||||||
#
|
|
||||||
# # 3. Prebuilt archive (.tar.gz) with asset filename match
|
|
||||||
# fetch_and_deploy_codeberg_release "myapp" "myuser/myapp" "prebuild" "latest" "/opt/myapp" "myapp_Linux_x86_64.tar.gz"
|
|
||||||
#
|
|
||||||
# # 4. Single binary (chmod +x)
|
|
||||||
# fetch_and_deploy_codeberg_release "myapp" "myuser/myapp" "singlefile" "v1.0.0" "/opt/myapp" "myapp-linux-amd64"
|
|
||||||
#
|
|
||||||
# # 5. Explicit tag version
|
|
||||||
# fetch_and_deploy_codeberg_release "autocaliweb" "gelbphoenix/autocaliweb" "tag" "v0.11.3" "/opt/autocaliweb"
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function fetch_and_deploy_codeberg_release() {
|
|
||||||
local app="$1"
|
|
||||||
local repo="$2"
|
|
||||||
local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile | tag
|
|
||||||
local version="${4:-latest}"
|
|
||||||
local target="${5:-/opt/$app}"
|
|
||||||
local asset_pattern="${6:-}"
|
|
||||||
|
|
||||||
local app_lc=$(echo "${app,,}" | tr -d ' ')
|
|
||||||
local version_file="$HOME/.${app_lc}"
|
|
||||||
|
|
||||||
local api_timeout="--connect-timeout 10 --max-time 60"
|
|
||||||
local download_timeout="--connect-timeout 15 --max-time 900"
|
|
||||||
|
|
||||||
local current_version=""
|
|
||||||
[[ -f "$version_file" ]] && current_version=$(<"$version_file")
|
|
||||||
|
|
||||||
ensure_dependencies jq
|
|
||||||
|
|
||||||
### Tag Mode (bypass Release API) ###
|
|
||||||
if [[ "$mode" == "tag" ]]; then
|
|
||||||
if [[ "$version" == "latest" ]]; then
|
|
||||||
msg_error "Mode 'tag' requires explicit version (not 'latest')"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
local tag_name="$version"
|
|
||||||
[[ "$tag_name" =~ ^v ]] && version="${tag_name:1}" || version="$tag_name"
|
|
||||||
|
|
||||||
if [[ "$current_version" == "$version" ]]; then
|
|
||||||
$STD msg_ok "$app is already up-to-date (v$version)"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# DNS check
|
|
||||||
if ! getent hosts "codeberg.org" &>/dev/null; then
|
|
||||||
msg_error "DNS resolution failed for codeberg.org – check /etc/resolv.conf or networking"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
local tmpdir
|
|
||||||
tmpdir=$(mktemp -d) || return 1
|
|
||||||
|
|
||||||
msg_info "Fetching Codeberg tag: $app ($tag_name)"
|
|
||||||
|
|
||||||
local safe_version="${version//@/_}"
|
|
||||||
safe_version="${safe_version//\//_}"
|
|
||||||
local filename="${app_lc}-${safe_version}.tar.gz"
|
|
||||||
local download_success=false
|
|
||||||
|
|
||||||
# Codeberg archive URL format: https://codeberg.org/{owner}/{repo}/archive/{tag}.tar.gz
|
|
||||||
local archive_url="https://codeberg.org/$repo/archive/${tag_name}.tar.gz"
|
|
||||||
if curl $download_timeout -fsSL -o "$tmpdir/$filename" "$archive_url"; then
|
|
||||||
download_success=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$download_success" != "true" ]]; then
|
|
||||||
msg_error "Download failed for $app ($tag_name)"
|
|
||||||
rm -rf "$tmpdir"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p "$target"
|
|
||||||
if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then
|
|
||||||
rm -rf "${target:?}/"*
|
|
||||||
fi
|
|
||||||
|
|
||||||
tar --no-same-owner -xzf "$tmpdir/$filename" -C "$tmpdir" || {
|
|
||||||
msg_error "Failed to extract tarball"
|
|
||||||
rm -rf "$tmpdir"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
local unpack_dir
|
|
||||||
unpack_dir=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d | head -n1)
|
|
||||||
|
|
||||||
shopt -s dotglob nullglob
|
|
||||||
cp -r "$unpack_dir"/* "$target/"
|
|
||||||
shopt -u dotglob nullglob
|
|
||||||
|
|
||||||
echo "$version" >"$version_file"
|
|
||||||
msg_ok "Deployed: $app ($version)"
|
|
||||||
rm -rf "$tmpdir"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Codeberg API: https://codeberg.org/api/v1/repos/{owner}/{repo}/releases
|
|
||||||
local api_url="https://codeberg.org/api/v1/repos/$repo/releases"
|
|
||||||
if [[ "$version" != "latest" ]]; then
|
|
||||||
# Get release by tag: /repos/{owner}/{repo}/releases/tags/{tag}
|
|
||||||
api_url="https://codeberg.org/api/v1/repos/$repo/releases/tags/$version"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# dns pre check
|
|
||||||
if ! getent hosts "codeberg.org" &>/dev/null; then
|
|
||||||
msg_error "DNS resolution failed for codeberg.org – check /etc/resolv.conf or networking"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
local max_retries=3 retry_delay=2 attempt=1 success=false resp http_code
|
|
||||||
|
|
||||||
while ((attempt <= max_retries)); do
|
|
||||||
resp=$(curl $api_timeout -fsSL -w "%{http_code}" -o /tmp/codeberg_rel.json "$api_url") && success=true && break
|
|
||||||
sleep "$retry_delay"
|
|
||||||
((attempt++))
|
|
||||||
done
|
|
||||||
|
|
||||||
if ! $success; then
|
|
||||||
msg_error "Failed to fetch release metadata from $api_url after $max_retries attempts"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
http_code="${resp:(-3)}"
|
|
||||||
[[ "$http_code" != "200" ]] && {
|
|
||||||
msg_error "Codeberg API returned HTTP $http_code"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
local json tag_name
|
|
||||||
json=$(</tmp/codeberg_rel.json)
|
|
||||||
|
|
||||||
# For "latest", the API returns an array - take the first (most recent) release
|
|
||||||
if [[ "$version" == "latest" ]]; then
|
|
||||||
json=$(echo "$json" | jq '.[0]')
|
|
||||||
fi
|
|
||||||
|
|
||||||
tag_name=$(echo "$json" | jq -r '.tag_name // .name // empty')
|
|
||||||
[[ "$tag_name" =~ ^v ]] && version="${tag_name:1}" || version="$tag_name"
|
|
||||||
|
|
||||||
if [[ "$current_version" == "$version" ]]; then
|
|
||||||
$STD msg_ok "$app is already up-to-date (v$version)"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
local tmpdir
|
|
||||||
tmpdir=$(mktemp -d) || return 1
|
|
||||||
local filename="" url=""
|
|
||||||
|
|
||||||
msg_info "Fetching Codeberg release: $app ($version)"
|
|
||||||
|
|
||||||
### Tarball Mode ###
|
|
||||||
if [[ "$mode" == "tarball" || "$mode" == "source" ]]; then
|
|
||||||
local safe_version="${version//@/_}"
|
|
||||||
safe_version="${safe_version//\//_}"
|
|
||||||
filename="${app_lc}-${safe_version}.tar.gz"
|
|
||||||
local download_success=false
|
|
||||||
|
|
||||||
# Codeberg archive URL format
|
|
||||||
local archive_url="https://codeberg.org/$repo/archive/${tag_name}.tar.gz"
|
|
||||||
if curl $download_timeout -fsSL -o "$tmpdir/$filename" "$archive_url"; then
|
|
||||||
download_success=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$download_success" != "true" ]]; then
|
|
||||||
msg_error "Download failed for $app ($tag_name)"
|
|
||||||
rm -rf "$tmpdir"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p "$target"
|
|
||||||
if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then
|
|
||||||
rm -rf "${target:?}/"*
|
|
||||||
fi
|
|
||||||
|
|
||||||
tar --no-same-owner -xzf "$tmpdir/$filename" -C "$tmpdir" || {
|
|
||||||
msg_error "Failed to extract tarball"
|
|
||||||
rm -rf "$tmpdir"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
local unpack_dir
|
|
||||||
unpack_dir=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d | head -n1)
|
|
||||||
|
|
||||||
shopt -s dotglob nullglob
|
|
||||||
cp -r "$unpack_dir"/* "$target/"
|
|
||||||
shopt -u dotglob nullglob
|
|
||||||
|
|
||||||
### Binary Mode ###
|
|
||||||
elif [[ "$mode" == "binary" ]]; then
|
|
||||||
local arch
|
|
||||||
arch=$(dpkg --print-architecture 2>/dev/null || uname -m)
|
|
||||||
[[ "$arch" == "x86_64" ]] && arch="amd64"
|
|
||||||
[[ "$arch" == "aarch64" ]] && arch="arm64"
|
|
||||||
|
|
||||||
local assets url_match=""
|
|
||||||
# Codeberg assets are in .assets[].browser_download_url
|
|
||||||
assets=$(echo "$json" | jq -r '.assets[].browser_download_url')
|
|
||||||
|
|
||||||
# If explicit filename pattern is provided, match that first
|
|
||||||
if [[ -n "$asset_pattern" ]]; then
|
|
||||||
for u in $assets; do
|
|
||||||
case "${u##*/}" in
|
|
||||||
$asset_pattern)
|
|
||||||
url_match="$u"
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Fall back to architecture heuristic
|
|
||||||
if [[ -z "$url_match" ]]; then
|
|
||||||
for u in $assets; do
|
|
||||||
if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then
|
|
||||||
url_match="$u"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Fallback: any .deb file
|
|
||||||
if [[ -z "$url_match" ]]; then
|
|
||||||
for u in $assets; do
|
|
||||||
[[ "$u" =~ \.deb$ ]] && url_match="$u" && break
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z "$url_match" ]]; then
|
|
||||||
msg_error "No suitable .deb asset found for $app"
|
|
||||||
rm -rf "$tmpdir"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
filename="${url_match##*/}"
|
|
||||||
curl $download_timeout -fsSL -o "$tmpdir/$filename" "$url_match" || {
|
|
||||||
msg_error "Download failed: $url_match"
|
|
||||||
rm -rf "$tmpdir"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
chmod 644 "$tmpdir/$filename"
|
|
||||||
$STD apt install -y "$tmpdir/$filename" || {
|
|
||||||
$STD dpkg -i "$tmpdir/$filename" || {
|
|
||||||
msg_error "Both apt and dpkg installation failed"
|
|
||||||
rm -rf "$tmpdir"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
### Prebuild Mode ###
|
|
||||||
elif [[ "$mode" == "prebuild" ]]; then
|
|
||||||
local pattern="${6%\"}"
|
|
||||||
pattern="${pattern#\"}"
|
|
||||||
[[ -z "$pattern" ]] && {
|
|
||||||
msg_error "Mode 'prebuild' requires 6th parameter (asset filename pattern)"
|
|
||||||
rm -rf "$tmpdir"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
local asset_url=""
|
|
||||||
for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do
|
|
||||||
filename_candidate="${u##*/}"
|
|
||||||
case "$filename_candidate" in
|
|
||||||
$pattern)
|
|
||||||
asset_url="$u"
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
[[ -z "$asset_url" ]] && {
|
|
||||||
msg_error "No asset matching '$pattern' found"
|
|
||||||
rm -rf "$tmpdir"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
filename="${asset_url##*/}"
|
|
||||||
curl $download_timeout -fsSL -o "$tmpdir/$filename" "$asset_url" || {
|
|
||||||
msg_error "Download failed: $asset_url"
|
|
||||||
rm -rf "$tmpdir"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
local unpack_tmp
|
|
||||||
unpack_tmp=$(mktemp -d)
|
|
||||||
mkdir -p "$target"
|
|
||||||
if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then
|
|
||||||
rm -rf "${target:?}/"*
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$filename" == *.zip ]]; then
|
|
||||||
ensure_dependencies unzip
|
|
||||||
unzip -q "$tmpdir/$filename" -d "$unpack_tmp" || {
|
|
||||||
msg_error "Failed to extract ZIP archive"
|
|
||||||
rm -rf "$tmpdir" "$unpack_tmp"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
elif [[ "$filename" == *.tar.* || "$filename" == *.tgz ]]; then
|
|
||||||
tar --no-same-owner -xf "$tmpdir/$filename" -C "$unpack_tmp" || {
|
|
||||||
msg_error "Failed to extract TAR archive"
|
|
||||||
rm -rf "$tmpdir" "$unpack_tmp"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
else
|
|
||||||
msg_error "Unsupported archive format: $filename"
|
|
||||||
rm -rf "$tmpdir" "$unpack_tmp"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
local top_dirs
|
|
||||||
top_dirs=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1 -type d | wc -l)
|
|
||||||
local top_entries inner_dir
|
|
||||||
top_entries=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1)
|
|
||||||
if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then
|
|
||||||
inner_dir="$top_entries"
|
|
||||||
shopt -s dotglob nullglob
|
|
||||||
if compgen -G "$inner_dir/*" >/dev/null; then
|
|
||||||
cp -r "$inner_dir"/* "$target/" || {
|
|
||||||
msg_error "Failed to copy contents from $inner_dir to $target"
|
|
||||||
rm -rf "$tmpdir" "$unpack_tmp"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
else
|
|
||||||
msg_error "Inner directory is empty: $inner_dir"
|
|
||||||
rm -rf "$tmpdir" "$unpack_tmp"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
shopt -u dotglob nullglob
|
|
||||||
else
|
|
||||||
shopt -s dotglob nullglob
|
|
||||||
if compgen -G "$unpack_tmp/*" >/dev/null; then
|
|
||||||
cp -r "$unpack_tmp"/* "$target/" || {
|
|
||||||
msg_error "Failed to copy contents to $target"
|
|
||||||
rm -rf "$tmpdir" "$unpack_tmp"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
else
|
|
||||||
msg_error "Unpacked archive is empty"
|
|
||||||
rm -rf "$tmpdir" "$unpack_tmp"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
shopt -u dotglob nullglob
|
|
||||||
fi
|
|
||||||
|
|
||||||
### Singlefile Mode ###
|
|
||||||
elif [[ "$mode" == "singlefile" ]]; then
|
|
||||||
local pattern="${6%\"}"
|
|
||||||
pattern="${pattern#\"}"
|
|
||||||
[[ -z "$pattern" ]] && {
|
|
||||||
msg_error "Mode 'singlefile' requires 6th parameter (asset filename pattern)"
|
|
||||||
rm -rf "$tmpdir"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
local asset_url=""
|
|
||||||
for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do
|
|
||||||
filename_candidate="${u##*/}"
|
|
||||||
case "$filename_candidate" in
|
|
||||||
$pattern)
|
|
||||||
asset_url="$u"
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
[[ -z "$asset_url" ]] && {
|
|
||||||
msg_error "No asset matching '$pattern' found"
|
|
||||||
rm -rf "$tmpdir"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
filename="${asset_url##*/}"
|
|
||||||
mkdir -p "$target"
|
|
||||||
|
|
||||||
local use_filename="${USE_ORIGINAL_FILENAME:-false}"
|
|
||||||
local target_file="$app"
|
|
||||||
[[ "$use_filename" == "true" ]] && target_file="$filename"
|
|
||||||
|
|
||||||
curl $download_timeout -fsSL -o "$target/$target_file" "$asset_url" || {
|
|
||||||
msg_error "Download failed: $asset_url"
|
|
||||||
rm -rf "$tmpdir"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if [[ "$target_file" != *.jar && -f "$target/$target_file" ]]; then
|
|
||||||
chmod +x "$target/$target_file"
|
|
||||||
fi
|
|
||||||
|
|
||||||
else
|
|
||||||
msg_error "Unknown mode: $mode"
|
|
||||||
rm -rf "$tmpdir"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "$version" >"$version_file"
|
|
||||||
msg_ok "Deployed: $app ($version)"
|
|
||||||
rm -rf "$tmpdir"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Downloads and deploys latest GitHub release (source, binary, tarball, asset).
|
# Downloads and deploys latest GitHub release (source, binary, tarball, asset).
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -1,620 +0,0 @@
|
|||||||
# Copyright (c) 2021-2026 community-scripts ORG
|
|
||||||
# License: MIT | https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/LICENSE
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
SPINNER_PID=""
|
|
||||||
SPINNER_ACTIVE=0
|
|
||||||
SPINNER_MSG=""
|
|
||||||
declare -A MSG_INFO_SHOWN
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Loads core utility groups once (colors, formatting, icons, defaults).
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
[[ -n "${_CORE_FUNC_LOADED:-}" ]] && return
|
|
||||||
_CORE_FUNC_LOADED=1
|
|
||||||
|
|
||||||
load_functions() {
|
|
||||||
[[ -n "${__FUNCTIONS_LOADED:-}" ]] && return
|
|
||||||
__FUNCTIONS_LOADED=1
|
|
||||||
color
|
|
||||||
formatting
|
|
||||||
icons
|
|
||||||
default_vars
|
|
||||||
set_std_mode
|
|
||||||
shell_check
|
|
||||||
get_valid_nextid
|
|
||||||
cleanup_vmid
|
|
||||||
cleanup
|
|
||||||
check_root
|
|
||||||
pve_check
|
|
||||||
arch_check
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to download & save header files
|
|
||||||
get_header() {
|
|
||||||
local app_name=$(echo "${APP,,}" | tr ' ' '-')
|
|
||||||
local app_type=${APP_TYPE:-vm}
|
|
||||||
local header_url="https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/${app_type}/headers/${app_name}"
|
|
||||||
local local_header_path="/usr/local/community-scripts/headers/${app_type}/${app_name}"
|
|
||||||
|
|
||||||
mkdir -p "$(dirname "$local_header_path")"
|
|
||||||
|
|
||||||
if [ ! -s "$local_header_path" ]; then
|
|
||||||
if ! curl -fsSL "$header_url" -o "$local_header_path"; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
cat "$local_header_path" 2>/dev/null || true
|
|
||||||
}
|
|
||||||
|
|
||||||
header_info() {
|
|
||||||
local app_name=$(echo "${APP,,}" | tr ' ' '-')
|
|
||||||
local header_content
|
|
||||||
|
|
||||||
header_content=$(get_header "$app_name") || header_content=""
|
|
||||||
|
|
||||||
clear
|
|
||||||
local term_width
|
|
||||||
term_width=$(tput cols 2>/dev/null || echo 120)
|
|
||||||
|
|
||||||
if [ -n "$header_content" ]; then
|
|
||||||
echo "$header_content"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Sets ANSI color codes used for styled terminal output.
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
color() {
|
|
||||||
YW=$(echo "\033[33m")
|
|
||||||
YWB=$(echo "\033[93m")
|
|
||||||
BL=$(echo "\033[36m")
|
|
||||||
RD=$(echo "\033[01;31m")
|
|
||||||
BGN=$(echo "\033[4;92m")
|
|
||||||
GN=$(echo "\033[1;92m")
|
|
||||||
DGN=$(echo "\033[32m")
|
|
||||||
CL=$(echo "\033[m")
|
|
||||||
}
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Defines formatting helpers like tab, bold, and line reset sequences.
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
formatting() {
|
|
||||||
BFR="\\r\\033[K"
|
|
||||||
BOLD=$(echo "\033[1m")
|
|
||||||
HOLD=" "
|
|
||||||
TAB=" "
|
|
||||||
TAB3=" "
|
|
||||||
}
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Sets symbolic icons used throughout user feedback and prompts.
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
icons() {
|
|
||||||
CM="${TAB}✔️${TAB}"
|
|
||||||
CROSS="${TAB}✖️${TAB}"
|
|
||||||
DNSOK="✔️ "
|
|
||||||
DNSFAIL="${TAB}✖️${TAB}"
|
|
||||||
INFO="${TAB}💡${TAB}${CL}"
|
|
||||||
OS="${TAB}🖥️${TAB}${CL}"
|
|
||||||
OSVERSION="${TAB}🌟${TAB}${CL}"
|
|
||||||
CONTAINERTYPE="${TAB}📦${TAB}${CL}"
|
|
||||||
DISKSIZE="${TAB}💾${TAB}${CL}"
|
|
||||||
CPUCORE="${TAB}🧠${TAB}${CL}"
|
|
||||||
RAMSIZE="${TAB}🛠️${TAB}${CL}"
|
|
||||||
SEARCH="${TAB}🔍${TAB}${CL}"
|
|
||||||
VERBOSE_CROPPED="🔍${TAB}"
|
|
||||||
VERIFYPW="${TAB}🔐${TAB}${CL}"
|
|
||||||
CONTAINERID="${TAB}🆔${TAB}${CL}"
|
|
||||||
HOSTNAME="${TAB}🏠${TAB}${CL}"
|
|
||||||
BRIDGE="${TAB}🌉${TAB}${CL}"
|
|
||||||
NETWORK="${TAB}📡${TAB}${CL}"
|
|
||||||
GATEWAY="${TAB}🌐${TAB}${CL}"
|
|
||||||
DISABLEIPV6="${TAB}🚫${TAB}${CL}"
|
|
||||||
ICON_DISABLEIPV6="${TAB}🚫${TAB}${CL}"
|
|
||||||
DEFAULT="${TAB}⚙️${TAB}${CL}"
|
|
||||||
MACADDRESS="${TAB}🔗${TAB}${CL}"
|
|
||||||
VLANTAG="${TAB}🏷️${TAB}${CL}"
|
|
||||||
ROOTSSH="${TAB}🔑${TAB}${CL}"
|
|
||||||
CREATING="${TAB}🚀${TAB}${CL}"
|
|
||||||
ADVANCED="${TAB}🧩${TAB}${CL}"
|
|
||||||
FUSE="${TAB}🗂️${TAB}${CL}"
|
|
||||||
GPU="${TAB}🎮${TAB}${CL}"
|
|
||||||
HOURGLASS="${TAB}⏳${TAB}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Sets default verbose mode for script and os execution.
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
set_std_mode() {
|
|
||||||
if [ "${VERBOSE:-no}" = "yes" ]; then
|
|
||||||
STD=""
|
|
||||||
else
|
|
||||||
STD="silent"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# default_vars()
|
|
||||||
#
|
|
||||||
# - Sets default retry and wait variables used for system actions
|
|
||||||
# - RETRY_NUM: Maximum number of retry attempts (default: 10)
|
|
||||||
# - RETRY_EVERY: Seconds to wait between retries (default: 3)
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
default_vars() {
|
|
||||||
RETRY_NUM=10
|
|
||||||
RETRY_EVERY=3
|
|
||||||
i=$RETRY_NUM
|
|
||||||
}
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# get_active_logfile()
|
|
||||||
#
|
|
||||||
# - Returns the appropriate log file based on execution context
|
|
||||||
# - BUILD_LOG: Host operations (VM creation)
|
|
||||||
# - Fallback to /tmp/build-<timestamp>.log if not set
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
get_active_logfile() {
|
|
||||||
if [[ -n "${BUILD_LOG:-}" ]]; then
|
|
||||||
echo "$BUILD_LOG"
|
|
||||||
else
|
|
||||||
# Fallback for legacy scripts
|
|
||||||
echo "/tmp/build-$(date +%Y%m%d_%H%M%S).log"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# silent()
|
|
||||||
#
|
|
||||||
# - Executes command with output redirected to active log file
|
|
||||||
# - On error: displays last 10 lines of log and exits with original exit code
|
|
||||||
# - Temporarily disables error trap to capture exit code correctly
|
|
||||||
# - Sources explain_exit_code() for detailed error messages
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
silent() {
|
|
||||||
local cmd="$*"
|
|
||||||
local caller_line="${BASH_LINENO[0]:-unknown}"
|
|
||||||
local logfile="$(get_active_logfile)"
|
|
||||||
|
|
||||||
set +Eeuo pipefail
|
|
||||||
trap - ERR
|
|
||||||
|
|
||||||
"$@" >>"$logfile" 2>&1
|
|
||||||
local rc=$?
|
|
||||||
|
|
||||||
set -Eeuo pipefail
|
|
||||||
trap 'error_handler' ERR
|
|
||||||
|
|
||||||
if [[ $rc -ne 0 ]]; then
|
|
||||||
# Source explain_exit_code if needed
|
|
||||||
if ! declare -f explain_exit_code >/dev/null 2>&1; then
|
|
||||||
source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/misc/error_handler.func) 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
local explanation=""
|
|
||||||
if declare -f explain_exit_code >/dev/null 2>&1; then
|
|
||||||
explanation="$(explain_exit_code "$rc")"
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf "\e[?25h"
|
|
||||||
if [[ -n "$explanation" ]]; then
|
|
||||||
msg_error "in line ${caller_line}: exit code ${rc} (${explanation})"
|
|
||||||
else
|
|
||||||
msg_error "in line ${caller_line}: exit code ${rc}"
|
|
||||||
fi
|
|
||||||
msg_custom "→" "${YWB}" "${cmd}"
|
|
||||||
|
|
||||||
if [[ -s "$logfile" ]]; then
|
|
||||||
local log_lines=$(wc -l <"$logfile")
|
|
||||||
echo "--- Last 10 lines of log ---"
|
|
||||||
tail -n 10 "$logfile"
|
|
||||||
echo "----------------------------"
|
|
||||||
|
|
||||||
# Show how to view full log if there are more lines
|
|
||||||
if [[ $log_lines -gt 10 ]]; then
|
|
||||||
msg_custom "📋" "${YW}" "View full log (${log_lines} lines): ${logfile}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
exit "$rc"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Performs a curl request with retry logic and inline feedback.
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
run_curl() {
|
|
||||||
if [ "$VERB" = "no" ]; then
|
|
||||||
curl "$@" >/dev/null 2>>/tmp/curl_error.log
|
|
||||||
else
|
|
||||||
curl "$@" 2>>/tmp/curl_error.log
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
curl_handler() {
|
|
||||||
local args=()
|
|
||||||
local url=""
|
|
||||||
local max_retries=0 delay=2 attempt=1
|
|
||||||
local exit_code has_output_file=false
|
|
||||||
|
|
||||||
for arg in "$@"; do
|
|
||||||
if [[ "$arg" != -* && -z "$url" ]]; then
|
|
||||||
url="$arg"
|
|
||||||
fi
|
|
||||||
[[ "$arg" == "-o" || "$arg" == --output ]] && has_output_file=true
|
|
||||||
args+=("$arg")
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ -z "$url" ]]; then
|
|
||||||
msg_error "no valid url or option entered for curl_handler"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
$STD msg_info "Fetching: $url"
|
|
||||||
|
|
||||||
while :; do
|
|
||||||
if $has_output_file; then
|
|
||||||
$STD run_curl "${args[@]}"
|
|
||||||
exit_code=$?
|
|
||||||
else
|
|
||||||
$STD result=$(run_curl "${args[@]}")
|
|
||||||
exit_code=$?
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $exit_code -eq 0 ]]; then
|
|
||||||
stop_spinner
|
|
||||||
msg_ok "Fetched: $url"
|
|
||||||
$has_output_file || printf '%s' "$result"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ((attempt >= max_retries)); then
|
|
||||||
stop_spinner
|
|
||||||
if [ -s /tmp/curl_error.log ]; then
|
|
||||||
local curl_stderr
|
|
||||||
curl_stderr=$(</tmp/curl_error.log)
|
|
||||||
rm -f /tmp/curl_error.log
|
|
||||||
fi
|
|
||||||
__curl_err_handler "$exit_code" "$url" "$curl_stderr"
|
|
||||||
exit 1 # hard exit if exit_code is not 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
$STD printf "\r\033[K${INFO}${YW}Retry $attempt/$max_retries in ${delay}s...${CL}" >&2
|
|
||||||
sleep "$delay"
|
|
||||||
((attempt++))
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Handles specific curl error codes and displays descriptive messages.
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
__curl_err_handler() {
|
|
||||||
local exit_code="$1"
|
|
||||||
local target="$2"
|
|
||||||
local curl_msg="$3"
|
|
||||||
|
|
||||||
case $exit_code in
|
|
||||||
1) msg_error "Unsupported protocol: $target" ;;
|
|
||||||
2) msg_error "Curl init failed: $target" ;;
|
|
||||||
3) msg_error "Malformed URL: $target" ;;
|
|
||||||
5) msg_error "Proxy resolution failed: $target" ;;
|
|
||||||
6) msg_error "Host resolution failed: $target" ;;
|
|
||||||
7) msg_error "Connection failed: $target" ;;
|
|
||||||
9) msg_error "Access denied: $target" ;;
|
|
||||||
18) msg_error "Partial file transfer: $target" ;;
|
|
||||||
22) msg_error "HTTP error (e.g. 400/404): $target" ;;
|
|
||||||
23) msg_error "Write error on local system: $target" ;;
|
|
||||||
26) msg_error "Read error from local file: $target" ;;
|
|
||||||
28) msg_error "Timeout: $target" ;;
|
|
||||||
35) msg_error "SSL connect error: $target" ;;
|
|
||||||
47) msg_error "Too many redirects: $target" ;;
|
|
||||||
51) msg_error "SSL cert verify failed: $target" ;;
|
|
||||||
52) msg_error "Empty server response: $target" ;;
|
|
||||||
55) msg_error "Send error: $target" ;;
|
|
||||||
56) msg_error "Receive error: $target" ;;
|
|
||||||
60) msg_error "SSL CA not trusted: $target" ;;
|
|
||||||
67) msg_error "Login denied by server: $target" ;;
|
|
||||||
78) msg_error "Remote file not found (404): $target" ;;
|
|
||||||
*) msg_error "Curl failed with code $exit_code: $target" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
[[ -n "$curl_msg" ]] && printf "%s\n" "$curl_msg" >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# shell_check()
|
|
||||||
#
|
|
||||||
# - Verifies that the script is running under Bash shell
|
|
||||||
# - Exits with error message if different shell is detected
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
shell_check() {
|
|
||||||
if [[ "$(ps -p $$ -o comm=)" != "bash" ]]; then
|
|
||||||
clear
|
|
||||||
msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell."
|
|
||||||
echo -e "\nExiting..."
|
|
||||||
sleep 2
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# clear_line()
|
|
||||||
#
|
|
||||||
# - Clears current terminal line using tput or ANSI escape codes
|
|
||||||
# - Moves cursor to beginning of line (carriage return)
|
|
||||||
# - Fallback to ANSI codes if tput not available
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
clear_line() {
|
|
||||||
tput cr 2>/dev/null || echo -en "\r"
|
|
||||||
tput el 2>/dev/null || echo -en "\033[K"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# is_verbose_mode()
|
|
||||||
#
|
|
||||||
# - Determines if script should run in verbose mode
|
|
||||||
# - Checks VERBOSE and var_verbose variables
|
|
||||||
# - Also returns true if not running in TTY (pipe/redirect scenario)
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
is_verbose_mode() {
|
|
||||||
local verbose="${VERBOSE:-${var_verbose:-no}}"
|
|
||||||
[[ "$verbose" != "no" || ! -t 2 ]]
|
|
||||||
}
|
|
||||||
|
|
||||||
### dev spinner ###
|
|
||||||
SPINNER_ACTIVE=0
|
|
||||||
SPINNER_PID=""
|
|
||||||
SPINNER_MSG=""
|
|
||||||
declare -A MSG_INFO_SHOWN=()
|
|
||||||
|
|
||||||
# Trap cleanup on various signals
|
|
||||||
trap 'cleanup_spinner' EXIT INT TERM HUP
|
|
||||||
|
|
||||||
# Cleans up spinner process on exit
|
|
||||||
cleanup_spinner() {
|
|
||||||
stop_spinner
|
|
||||||
# Additional cleanup if needed
|
|
||||||
}
|
|
||||||
|
|
||||||
start_spinner() {
|
|
||||||
local msg="${1:-Processing...}"
|
|
||||||
local frames=(⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏)
|
|
||||||
local spin_i=0
|
|
||||||
local interval=0.1
|
|
||||||
|
|
||||||
# Set message and clear current line
|
|
||||||
SPINNER_MSG="$msg"
|
|
||||||
printf "\r\e[2K" >&2
|
|
||||||
|
|
||||||
# Stop any existing spinner
|
|
||||||
stop_spinner
|
|
||||||
|
|
||||||
# Set active flag
|
|
||||||
SPINNER_ACTIVE=1
|
|
||||||
|
|
||||||
# Start spinner in background
|
|
||||||
{
|
|
||||||
while [[ "$SPINNER_ACTIVE" -eq 1 ]]; do
|
|
||||||
printf "\r\e[2K%s %b" "${TAB}${frames[spin_i]}${TAB}" "${YW}${SPINNER_MSG}${CL}" >&2
|
|
||||||
spin_i=$(((spin_i + 1) % ${#frames[@]}))
|
|
||||||
sleep "$interval"
|
|
||||||
done
|
|
||||||
} &
|
|
||||||
|
|
||||||
SPINNER_PID=$!
|
|
||||||
|
|
||||||
# Disown to prevent getting "Terminated" messages
|
|
||||||
disown "$SPINNER_PID" 2>/dev/null || true
|
|
||||||
}
|
|
||||||
|
|
||||||
stop_spinner() {
|
|
||||||
# Check if spinner is active and PID exists
|
|
||||||
if [[ "$SPINNER_ACTIVE" -eq 1 ]] && [[ -n "${SPINNER_PID}" ]]; then
|
|
||||||
SPINNER_ACTIVE=0
|
|
||||||
|
|
||||||
if kill -0 "$SPINNER_PID" 2>/dev/null; then
|
|
||||||
kill "$SPINNER_PID" 2>/dev/null
|
|
||||||
# Give it a moment to terminate
|
|
||||||
sleep 0.1
|
|
||||||
# Force kill if still running
|
|
||||||
if kill -0 "$SPINNER_PID" 2>/dev/null; then
|
|
||||||
kill -9 "$SPINNER_PID" 2>/dev/null
|
|
||||||
fi
|
|
||||||
# Wait for process but ignore errors
|
|
||||||
wait "$SPINNER_PID" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Clear spinner line
|
|
||||||
printf "\r\e[2K" >&2
|
|
||||||
SPINNER_PID=""
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
spinner_guard() {
|
|
||||||
# Safely stop spinner if it's running
|
|
||||||
if [[ "$SPINNER_ACTIVE" -eq 1 ]] && [[ -n "${SPINNER_PID}" ]]; then
|
|
||||||
stop_spinner
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
msg_info() {
|
|
||||||
local msg="${1:-Information message}"
|
|
||||||
|
|
||||||
# Only show each message once unless reset
|
|
||||||
if [[ -n "${MSG_INFO_SHOWN["$msg"]+x}" ]]; then
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
MSG_INFO_SHOWN["$msg"]=1
|
|
||||||
|
|
||||||
spinner_guard
|
|
||||||
start_spinner "$msg"
|
|
||||||
}
|
|
||||||
|
|
||||||
msg_ok() {
|
|
||||||
local msg="${1:-Operation completed successfully}"
|
|
||||||
stop_spinner
|
|
||||||
printf "\r\e[2K%s %b\n" "${CM}" "${GN}${msg}${CL}" >&2
|
|
||||||
|
|
||||||
# Remove from shown messages to allow it to be shown again
|
|
||||||
local sanitized_msg
|
|
||||||
sanitized_msg=$(printf '%s' "$msg" | sed 's/\x1b\[[0-9;]*m//g; s/[^a-zA-Z0-9_]/_/g')
|
|
||||||
unset 'MSG_INFO_SHOWN['"$sanitized_msg"']' 2>/dev/null || true
|
|
||||||
}
|
|
||||||
|
|
||||||
msg_error() {
|
|
||||||
local msg="${1:-An error occurred}"
|
|
||||||
stop_spinner
|
|
||||||
printf "\r\e[2K%s %b\n" "${CROSS}" "${RD}${msg}${CL}" >&2
|
|
||||||
}
|
|
||||||
|
|
||||||
msg_warn() {
|
|
||||||
stop_spinner
|
|
||||||
local msg="$1"
|
|
||||||
echo -e "${BFR:-}${INFO:-ℹ️} ${YWB}${msg}${CL}" >&2
|
|
||||||
}
|
|
||||||
|
|
||||||
# Helper function to display a message with custom symbol and color
|
|
||||||
msg_custom() {
|
|
||||||
local symbol="${1:-*}"
|
|
||||||
local color="${2:-$CL}"
|
|
||||||
local msg="${3:-Custom message}"
|
|
||||||
[[ -z "$msg" ]] && return
|
|
||||||
stop_spinner
|
|
||||||
printf "\r\e[2K%s %b\n" "$symbol" "${color}${msg}${CL}" >&2
|
|
||||||
}
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# msg_debug()
|
|
||||||
#
|
|
||||||
# - Displays debug message with timestamp when var_full_verbose=1
|
|
||||||
# - Automatically enables var_verbose if not already set
|
|
||||||
# - Uses bright yellow color for debug output
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
msg_debug() {
|
|
||||||
if [[ "${var_full_verbose:-0}" == "1" ]]; then
|
|
||||||
[[ "${var_verbose:-0}" != "1" ]] && var_verbose=1
|
|
||||||
echo -e "${YWB}[$(date '+%F %T')] [DEBUG]${CL} $*"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Displays error message and immediately terminates script
|
|
||||||
fatal() {
|
|
||||||
msg_error "$1"
|
|
||||||
kill -INT $$
|
|
||||||
}
|
|
||||||
|
|
||||||
get_valid_nextid() {
|
|
||||||
local try_id
|
|
||||||
try_id=$(pvesh get /cluster/nextid)
|
|
||||||
while true; do
|
|
||||||
if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then
|
|
||||||
try_id=$((try_id + 1))
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then
|
|
||||||
try_id=$((try_id + 1))
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
break
|
|
||||||
done
|
|
||||||
echo "$try_id"
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup_vmid() {
|
|
||||||
if [[ -z "${VMID:-}" ]]; then
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
if qm status "$VMID" &>/dev/null; then
|
|
||||||
qm stop "$VMID" &>/dev/null
|
|
||||||
qm destroy "$VMID" &>/dev/null
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup() {
|
|
||||||
if [[ "$(dirs -p | wc -l)" -gt 1 ]]; then
|
|
||||||
popd >/dev/null || true
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
check_root() {
|
|
||||||
if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then
|
|
||||||
clear
|
|
||||||
msg_error "Please run this script as root."
|
|
||||||
echo -e "\nExiting..."
|
|
||||||
sleep 2
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
pve_check() {
|
|
||||||
if ! pveversion | grep -Eq "pve-manager/(8\.[1-4]|9\.[0-1])(\.[0-9]+)*"; then
|
|
||||||
msg_error "This version of Proxmox Virtual Environment is not supported"
|
|
||||||
echo -e "Requires Proxmox Virtual Environment Version 8.1 - 8.4 or 9.0 - 9.1."
|
|
||||||
echo -e "Exiting..."
|
|
||||||
sleep 2
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
arch_check() {
|
|
||||||
if [ "$(dpkg --print-architecture)" != "amd64" ]; then
|
|
||||||
echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n"
|
|
||||||
echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n"
|
|
||||||
echo -e "Exiting..."
|
|
||||||
sleep 2
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
exit_script() {
|
|
||||||
clear
|
|
||||||
echo -e "\n${CROSS}${RD}User exited script${CL}\n"
|
|
||||||
exit
|
|
||||||
}
|
|
||||||
|
|
||||||
check_hostname_conflict() {
|
|
||||||
local hostname="$1"
|
|
||||||
if qm list | awk '{print $2}' | grep -qx "$hostname"; then
|
|
||||||
msg_error "Hostname $hostname already in use by another VM."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
set_description() {
|
|
||||||
DESCRIPTION=$(
|
|
||||||
cat <<EOF
|
|
||||||
<div align='center'>
|
|
||||||
<a href='https://Helper-Scripts.com' 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;'/>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<h2 style='font-size: 24px; margin: 20px 0;'>${NSAPP} VM</h2>
|
|
||||||
|
|
||||||
<p style='margin: 16px 0;'>
|
|
||||||
<a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'>
|
|
||||||
<img src='https://img.shields.io/badge/☕-Buy us a coffee-blue' alt='spend Coffee' />
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<span style='margin: 0 10px;'>
|
|
||||||
<i class="fa fa-github fa-fw" style="color: #f5f5f5;"></i>
|
|
||||||
<a href='https://github.com/community-scripts/ProxmoxVE' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>GitHub</a>
|
|
||||||
</span>
|
|
||||||
<span style='margin: 0 10px;'>
|
|
||||||
<i class="fa fa-comments fa-fw" style="color: #f5f5f5;"></i>
|
|
||||||
<a href='https://github.com/community-scripts/ProxmoxVE/discussions' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Discussions</a>
|
|
||||||
</span>
|
|
||||||
<span style='margin: 0 10px;'>
|
|
||||||
<i class="fa fa-exclamation-circle fa-fw" style="color: #f5f5f5;"></i>
|
|
||||||
<a href='https://github.com/community-scripts/ProxmoxVE/issues' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Issues</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
EOF
|
|
||||||
)
|
|
||||||
qm set "$VMID" -description "$DESCRIPTION" >/dev/null
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,264 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Copyright (c) 2021-2026 community-scripts ORG
|
|
||||||
# Author: vhsdream
|
|
||||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
|
||||||
# Source: https://github.com/alangrainger/immich-public-proxy
|
|
||||||
|
|
||||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func)
|
|
||||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func)
|
|
||||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func)
|
|
||||||
|
|
||||||
# Enable error handling
|
|
||||||
set -Eeuo pipefail
|
|
||||||
trap 'error_handler' ERR
|
|
||||||
|
|
||||||
# ==============================================================================
|
|
||||||
# CONFIGURATION
|
|
||||||
# ==============================================================================
|
|
||||||
APP="Immich Public Proxy"
|
|
||||||
APP_TYPE="addon"
|
|
||||||
INSTALL_PATH="/opt/immich-proxy"
|
|
||||||
CONFIG_PATH="/opt/immich-proxy/app"
|
|
||||||
DEFAULT_PORT=3000
|
|
||||||
|
|
||||||
# Initialize all core functions (colors, formatting, icons, STD mode)
|
|
||||||
load_functions
|
|
||||||
|
|
||||||
# ==============================================================================
|
|
||||||
# HEADER
|
|
||||||
# ==============================================================================
|
|
||||||
function header_info {
|
|
||||||
clear
|
|
||||||
cat <<"EOF"
|
|
||||||
____ _ __ ____
|
|
||||||
/ _/___ ___ ____ ___ (_)____/ /_ / __ \_________ _ ____ __
|
|
||||||
/ // __ `__ \/ __ `__ \/ / ___/ __ \______/ /_/ / ___/ __ \| |/_/ / / /
|
|
||||||
_/ // / / / / / / / / / / / /__/ / / /_____/ ____/ / / /_/ /> </ /_/ /
|
|
||||||
/___/_/ /_/ /_/_/ /_/ /_/_/\___/_/ /_/ /_/ /_/ \____/_/|_|\__, /
|
|
||||||
/____/
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
# ==============================================================================
|
|
||||||
# OS DETECTION
|
|
||||||
# ==============================================================================
|
|
||||||
if [[ -f "/etc/alpine-release" ]]; then
|
|
||||||
msg_error "Alpine is not supported for ${APP}. Use Debian."
|
|
||||||
exit 1
|
|
||||||
elif [[ -f "/etc/debian_version" ]]; then
|
|
||||||
OS="Debian"
|
|
||||||
SERVICE_PATH="/etc/systemd/system/immich-proxy.service"
|
|
||||||
else
|
|
||||||
echo -e "${CROSS} Unsupported OS detected. Exiting."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ==============================================================================
|
|
||||||
# UNINSTALL
|
|
||||||
# ==============================================================================
|
|
||||||
function uninstall() {
|
|
||||||
msg_info "Uninstalling ${APP}"
|
|
||||||
systemctl disable --now immich-proxy.service &>/dev/null || true
|
|
||||||
rm -f "$SERVICE_PATH"
|
|
||||||
rm -rf "$INSTALL_PATH"
|
|
||||||
rm -f "/usr/local/bin/update_immich-public-proxy"
|
|
||||||
rm -f "$HOME/.immichpublicproxy"
|
|
||||||
msg_ok "${APP} has been uninstalled"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ==============================================================================
|
|
||||||
# UPDATE
|
|
||||||
# ==============================================================================
|
|
||||||
function update() {
|
|
||||||
if check_for_gh_release "Immich Public Proxy" "alangrainger/immich-public-proxy"; then
|
|
||||||
msg_info "Stopping service"
|
|
||||||
systemctl stop immich-proxy.service &>/dev/null || true
|
|
||||||
msg_ok "Stopped service"
|
|
||||||
|
|
||||||
msg_info "Backing up configuration"
|
|
||||||
cp "$CONFIG_PATH"/.env /tmp/ipp.env.bak 2>/dev/null || true
|
|
||||||
cp "$CONFIG_PATH"/config.json /tmp/ipp.config.json.bak 2>/dev/null || true
|
|
||||||
msg_ok "Backed up configuration"
|
|
||||||
|
|
||||||
NODE_VERSION="24" setup_nodejs
|
|
||||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Immich Public Proxy" "alangrainger/immich-public-proxy" "tarball" "latest" "$INSTALL_PATH"
|
|
||||||
|
|
||||||
msg_info "Restoring configuration"
|
|
||||||
cp /tmp/ipp.env.bak "$CONFIG_PATH"/.env 2>/dev/null || true
|
|
||||||
cp /tmp/ipp.config.json.bak "$CONFIG_PATH"/config.json 2>/dev/null || true
|
|
||||||
rm -f /tmp/ipp.*.bak
|
|
||||||
msg_ok "Restored configuration"
|
|
||||||
|
|
||||||
msg_info "Installing dependencies"
|
|
||||||
cd "$CONFIG_PATH"
|
|
||||||
$STD npm install
|
|
||||||
msg_ok "Installed dependencies"
|
|
||||||
|
|
||||||
msg_info "Building ${APP}"
|
|
||||||
$STD npm run build
|
|
||||||
msg_ok "Built ${APP}"
|
|
||||||
|
|
||||||
msg_info "Starting service"
|
|
||||||
systemctl start immich-proxy
|
|
||||||
msg_ok "Started service"
|
|
||||||
msg_ok "Updated successfully"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ==============================================================================
|
|
||||||
# INSTALL
|
|
||||||
# ==============================================================================
|
|
||||||
function install() {
|
|
||||||
NODE_VERSION="24" setup_nodejs
|
|
||||||
|
|
||||||
# Force fresh download by removing version cache
|
|
||||||
rm -f "$HOME/.immichpublicproxy"
|
|
||||||
fetch_and_deploy_gh_release "Immich Public Proxy" "alangrainger/immich-public-proxy" "tarball" "latest" "$INSTALL_PATH"
|
|
||||||
|
|
||||||
msg_info "Installing dependencies"
|
|
||||||
cd "$CONFIG_PATH"
|
|
||||||
$STD npm install
|
|
||||||
msg_ok "Installed dependencies"
|
|
||||||
|
|
||||||
msg_info "Building ${APP}"
|
|
||||||
$STD npm run build
|
|
||||||
msg_ok "Built ${APP}"
|
|
||||||
|
|
||||||
MAX_ATTEMPTS=3
|
|
||||||
attempt=0
|
|
||||||
while true; do
|
|
||||||
attempt=$((attempt + 1))
|
|
||||||
read -rp "${TAB3}Enter your LOCAL Immich IP or domain (ex. 192.168.1.100 or immich.local.lan): " DOMAIN
|
|
||||||
if [[ -z "$DOMAIN" ]]; then
|
|
||||||
if ((attempt >= MAX_ATTEMPTS)); then
|
|
||||||
DOMAIN="${LOCAL_IP:-localhost}"
|
|
||||||
msg_warn "Using fallback: $DOMAIN"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
msg_warn "Domain cannot be empty! (Attempt $attempt/$MAX_ATTEMPTS)"
|
|
||||||
elif [[ "$DOMAIN" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
|
||||||
valid_ip=true
|
|
||||||
IFS='.' read -ra octets <<<"$DOMAIN"
|
|
||||||
for octet in "${octets[@]}"; do
|
|
||||||
if ((octet > 255)); then
|
|
||||||
valid_ip=false
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
if $valid_ip; then
|
|
||||||
break
|
|
||||||
else
|
|
||||||
msg_warn "Invalid IP address!"
|
|
||||||
fi
|
|
||||||
elif [[ "$DOMAIN" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$ || "$DOMAIN" == "localhost" ]]; then
|
|
||||||
break
|
|
||||||
else
|
|
||||||
msg_warn "Invalid domain format!"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
msg_info "Creating configuration"
|
|
||||||
cat <<EOF >"$CONFIG_PATH"/.env
|
|
||||||
NODE_ENV=production
|
|
||||||
IMMICH_URL=http://${DOMAIN}:2283
|
|
||||||
EOF
|
|
||||||
chmod 600 "$CONFIG_PATH"/.env
|
|
||||||
msg_ok "Created configuration"
|
|
||||||
|
|
||||||
msg_info "Creating service"
|
|
||||||
cat <<EOF >"$SERVICE_PATH"
|
|
||||||
[Unit]
|
|
||||||
Description=Immich Public Proxy
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
User=root
|
|
||||||
WorkingDirectory=${INSTALL_PATH}
|
|
||||||
EnvironmentFile=${CONFIG_PATH}/.env
|
|
||||||
ExecStart=/usr/bin/node ${INSTALL_PATH}/app/server.js
|
|
||||||
Restart=always
|
|
||||||
RestartSec=10
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
EOF
|
|
||||||
systemctl enable -q --now immich-proxy
|
|
||||||
msg_ok "Created and started service"
|
|
||||||
|
|
||||||
# Create update script (simple wrapper that calls this addon with type=update)
|
|
||||||
msg_info "Creating update script"
|
|
||||||
cat <<'UPDATEEOF' >/usr/local/bin/update_immich-public-proxy
|
|
||||||
#!/usr/bin/env bash
|
|
||||||
# Immich Public Proxy Update Script
|
|
||||||
type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/immich-public-proxy.sh)"
|
|
||||||
UPDATEEOF
|
|
||||||
chmod +x /usr/local/bin/update_immich-public-proxy
|
|
||||||
msg_ok "Created update script (/usr/local/bin/update_immich-public-proxy)"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
msg_ok "${APP} is reachable at: ${BL}http://${LOCAL_IP}:${DEFAULT_PORT}${CL}"
|
|
||||||
echo ""
|
|
||||||
msg_warn "Additional configuration is available at '/opt/immich-proxy/app/config.json'"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ==============================================================================
|
|
||||||
# MAIN
|
|
||||||
# ==============================================================================
|
|
||||||
|
|
||||||
# Handle type=update (called from update script)
|
|
||||||
if [[ "${type:-}" == "update" ]]; then
|
|
||||||
header_info
|
|
||||||
if [[ -d "$INSTALL_PATH" && -f "$SERVICE_PATH" ]]; then
|
|
||||||
update
|
|
||||||
else
|
|
||||||
msg_error "${APP} is not installed. Nothing to update."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
header_info
|
|
||||||
get_lxc_ip
|
|
||||||
|
|
||||||
# Check if already installed
|
|
||||||
if [[ -d "$INSTALL_PATH" && -f "$SERVICE_PATH" ]]; 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} - Node.js 24"
|
|
||||||
echo -e "${TAB} - Immich Public Proxy"
|
|
||||||
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
|
|
||||||
Reference in New Issue
Block a user