diff --git a/.github/DISCUSSION_TEMPLATE/request-script.yml b/.github/DISCUSSION_TEMPLATE/request-script.yml index 64d694c97..a2cf93136 100644 --- a/.github/DISCUSSION_TEMPLATE/request-script.yml +++ b/.github/DISCUSSION_TEMPLATE/request-script.yml @@ -12,13 +12,17 @@ body: Requests may be closed if the application is out of scope, abandoned, too new, not publicly verifiable, or not suitable for a reliable Proxmox VE Helper-Scripts integration. General requirements: - - The application should be self-hosted. - - The project should have an official public source repository. - - The project should provide official releases, tags, or release tarballs. - - The project should be actively maintained. - - The project should generally have at least 1,000 stars or a comparable public adoption signal. - - The latest official release or tag should not be older than 6 months. - - The project itself should be at least 6 months old. + - The application must be self-hosted. + - The project must have an official public source repository. + - The project must provide official releases, tags, or release tarballs. + - The project must be actively maintained. + - The official source repository must have at least 1,000 stars. + - The latest official release or tag must not be older than 6 months. + - The project itself must be at least 6 months old. + + Projects that do not meet these requirements may be closed without further evaluation. + + Exceptions to the 1,000-star requirement are rare and require a clearly verifiable, significant public adoption signal. - type: input id: application-name @@ -47,6 +51,35 @@ body: validations: required: true + - type: markdown + attributes: + value: | + ## ⚠️ Project Eligibility + + Before continuing, verify that the requested project meets the minimum requirements below. + + **Projects with fewer than 1,000 stars are generally not eligible for a script request.** + + Exceptions are only considered where there is a clearly verifiable, significant public adoption signal. + + - type: input + id: repository-stars + attributes: + label: Repository Stars + description: Enter the current number of stars of the official source repository. + placeholder: "e.g., 15,000" + validations: + required: true + + - type: checkboxes + id: minimum-stars + attributes: + label: Minimum Adoption Requirement + description: Confirm that you have verified the project's public adoption. + options: + - label: The official source repository has at least 1,000 stars. + required: true + - type: textarea id: app-description attributes: diff --git a/.github/changelogs/2026/07.md b/.github/changelogs/2026/07.md index 4c20ca3e6..2ecd58f89 100644 --- a/.github/changelogs/2026/07.md +++ b/.github/changelogs/2026/07.md @@ -1,3 +1,94 @@ +## 2026-07-11 + +### 🆕 New Scripts + + - LocalAGI ([#15687](https://github.com/community-scripts/ProxmoxVE/pull/15687)) + +### 🚀 Updated Scripts + + - fix(adventurelog): allow pnpm build scripts so install/update doesn't abort [@TowyTowy](https://github.com/TowyTowy) ([#15681](https://github.com/community-scripts/ProxmoxVE/pull/15681)) + + - #### 🐞 Bug Fixes + + - fix(fileflows): install .NET 10 ASP.NET Core Runtime to match current release [@TowyTowy](https://github.com/TowyTowy) ([#15702](https://github.com/community-scripts/ProxmoxVE/pull/15702)) + - Fix spacing on VLAN Input Box in haos-vm.sh [@pumrum](https://github.com/pumrum) ([#15696](https://github.com/community-scripts/ProxmoxVE/pull/15696)) + +### 💾 Core + + - #### ✨ New Features + + - [tools.func]: Add function to handle deployment via GitLab release tags [@tremor021](https://github.com/tremor021) ([#15641](https://github.com/community-scripts/ProxmoxVE/pull/15641)) + +## 2026-07-10 + +### 🆕 New Scripts + + - Squid ([#15605](https://github.com/community-scripts/ProxmoxVE/pull/15605)) + +### 🚀 Updated Scripts + + - #### 🐞 Bug Fixes + + - Adapt to new artifact filename format for pocket id [@wollew](https://github.com/wollew) ([#15689](https://github.com/community-scripts/ProxmoxVE/pull/15689)) + - Fireshare: Fix for install and upgrade to v1.7.3 [@tremor021](https://github.com/tremor021) ([#15673](https://github.com/community-scripts/ProxmoxVE/pull/15673)) + - Endurain: Fix update procedure [@tremor021](https://github.com/tremor021) ([#15674](https://github.com/community-scripts/ProxmoxVE/pull/15674)) + +## 2026-07-09 + +### 🚀 Updated Scripts + + - fix(pihole): repair Unbound DNS-over-TLS (DoT) forwarding config [@TowyTowy](https://github.com/TowyTowy) ([#15654](https://github.com/community-scripts/ProxmoxVE/pull/15654)) + +## 2026-07-08 + +### 🚀 Updated Scripts + + - #### 🐞 Bug Fixes + + - Opencloud: Bump version to 7.2.1 [@vhsdream](https://github.com/vhsdream) ([#15655](https://github.com/community-scripts/ProxmoxVE/pull/15655)) + - BabyBuddy: Harden update script [@MickLesk](https://github.com/MickLesk) ([#15642](https://github.com/community-scripts/ProxmoxVE/pull/15642)) + +## 2026-07-07 + +### 🆕 New Scripts + + - Forgejo-Runner ([#15046](https://github.com/community-scripts/ProxmoxVE/pull/15046)) + +## 2026-07-06 + +### 🚀 Updated Scripts + + - Fix alignment in various ct end messages [@tremor021](https://github.com/tremor021) ([#15632](https://github.com/community-scripts/ProxmoxVE/pull/15632)) +- Immich: Update libvips to 8.18.4 [@vhsdream](https://github.com/vhsdream) ([#15619](https://github.com/community-scripts/ProxmoxVE/pull/15619)) + + - #### 🐞 Bug Fixes + + - Wizarr: Build JS and CSS static assets [@vhsdream](https://github.com/vhsdream) ([#15634](https://github.com/community-scripts/ProxmoxVE/pull/15634)) + - RustDesk Server: Update URL format in rustdeskserver.sh [@tremor021](https://github.com/tremor021) ([#15626](https://github.com/community-scripts/ProxmoxVE/pull/15626)) + - attempt to port docker-vm to support arm64 [@asylumexp](https://github.com/asylumexp) ([#15611](https://github.com/community-scripts/ProxmoxVE/pull/15611)) + - fix(plane): don't clobber global app var, breaking /usr/bin/update [@asylumexp](https://github.com/asylumexp) ([#15612](https://github.com/community-scripts/ProxmoxVE/pull/15612)) + + - #### 🔧 Refactor + + - cliproxyapi: point setup message at /management.html [@austinpilz](https://github.com/austinpilz) ([#15628](https://github.com/community-scripts/ProxmoxVE/pull/15628)) + +### 🗑️ Deleted Scripts + + - Remove: FlowiseAI [@MickLesk](https://github.com/MickLesk) ([#15624](https://github.com/community-scripts/ProxmoxVE/pull/15624)) + +## 2026-07-05 + +### 🆕 New Scripts + + - excalidash ([#15604](https://github.com/community-scripts/ProxmoxVE/pull/15604)) + +### 🚀 Updated Scripts + + - #### 🐞 Bug Fixes + + - fix: homarr: cli [@CrazyWolf13](https://github.com/CrazyWolf13) ([#15603](https://github.com/community-scripts/ProxmoxVE/pull/15603)) + - immich: vacuum smart_search/face_search before VectorChord bump [@irishpadres](https://github.com/irishpadres) ([#15607](https://github.com/community-scripts/ProxmoxVE/pull/15607)) + ## 2026-07-04 ### 🚀 Updated Scripts diff --git a/.github/workflows/close-invalid-pr-template.yml b/.github/workflows/close-invalid-pr-template.yml new file mode 100644 index 000000000..ce4c9a1bb --- /dev/null +++ b/.github/workflows/close-invalid-pr-template.yml @@ -0,0 +1,163 @@ +name: Close PRs Missing Template + +on: + pull_request_target: + branches: ["main"] + types: [opened, edited, reopened, synchronize, labeled] + +jobs: + validate-pr-template: + if: github.repository == 'community-scripts/ProxmoxVE' + runs-on: ubuntu-latest + permissions: + pull-requests: write + issues: write + contents: read + steps: + - name: Close PR if it does not follow the PR template + uses: actions/github-script@v7 + with: + script: | + const pr = context.payload.pull_request; + const prNumber = pr.number; + const author = pr.user.login; + const owner = context.repo.owner; + const repo = context.repo.repo; + + const allowedBots = [ + "push-app-to-main[bot]", + "push-app-to-main", + "community-scripts-pr-app", + "github-actions[bot]", + "dependabot[bot]", + ]; + + if (allowedBots.includes(author) || author.endsWith("[bot]")) { + core.info(`PR #${prNumber} by bot "${author}" — skipping template validation.`); + return; + } + + const association = pr.author_association; + const exemptAssociations = ["OWNER", "MEMBER", "COLLABORATOR"]; + if (exemptAssociations.includes(association)) { + core.info(`PR #${prNumber} by ${association} "${author}" — skipping template validation.`); + return; + } + + const labels = pr.labels.map((label) => label.name); + const skipLabels = ["automated pr", "keep open"]; + + if (skipLabels.some((label) => labels.includes(label))) { + core.info(`PR #${prNumber} has a skip label (${labels.join(", ")}) — skipping template validation.`); + return; + } + + if (pr.draft) { + core.info(`PR #${prNumber} is a draft — skipping template validation.`); + return; + } + + const body = pr.body || ""; + const failures = []; + + const requiredSections = [ + "## ✍️ Description", + "## ✅ Prerequisites", + "## 🛠️ Type of Change", + ]; + + for (const section of requiredSections) { + if (!body.includes(section)) { + failures.push(`Missing required section: \`${section}\``); + } + } + + const descriptionMatch = body.match( + /## ✍️ Description\s*\n+([\s\S]*?)(?=\n## )/i + ); + const description = (descriptionMatch?.[1] || "").trim(); + if (!description) { + failures.push("The **Description** section is empty."); + } + + const prerequisiteCheckboxes = [ + "**Self-review completed**", + "**Tested thoroughly**", + "**No security risks**", + ]; + + for (const checkbox of prerequisiteCheckboxes) { + const escaped = checkbox.replace(/([.*+?^=!:${}()|[\]\/\\])/g, "\\$1"); + const regex = new RegExp(`- \\[(x|X)\\]\\s*${escaped}`, "i"); + if (!regex.test(body)) { + failures.push(`Prerequisite not checked: ${checkbox}`); + } + } + + const typeOfChangeCheckboxes = [ + "🐞 **Bug fix**", + "✨ **New feature**", + "💥 **Breaking change**", + "🆕 **New script**", + "🌍 **Website update**", + "🔧 **Refactoring / Code Cleanup**", + "📝 **Documentation update**", + ]; + + const hasTypeChecked = typeOfChangeCheckboxes.some((checkbox) => { + const escaped = checkbox.replace(/([.*+?^=!:${}()|[\]\/\\])/g, "\\$1"); + const regex = new RegExp(`- \\[(x|X)\\]\\s*${escaped}`, "i"); + return regex.test(body); + }); + + if (!hasTypeChecked) { + failures.push("At least one **Type of Change** checkbox must be checked."); + } + + if (failures.length === 0) { + core.info(`PR #${prNumber} follows the PR template.`); + return; + } + + core.info(`Closing PR #${prNumber} — template validation failed.`); + + const templateUrl = + "https://github.com/community-scripts/ProxmoxVE/blob/main/.github/pull_request_template.md"; + const failureList = failures.map((item) => `- ${item}`).join("\n"); + + const comment = [ + `👋 Hi @${author},`, + ``, + `This pull request was closed because it does not follow the [PR template](${templateUrl}).`, + ``, + `Please fix the following and open a new PR (or reopen this one after updating the description):`, + ``, + failureList, + ``, + `> Use the template sections, fill in the description, check all prerequisite boxes, and select at least one type of change.`, + ``, + `Maintainers can add the \`keep open\` label to exempt a PR from this check.`, + ``, + `Thank you for contributing! 🙏`, + ].join("\n"); + + await github.rest.issues.createComment({ + owner, + repo, + issue_number: prNumber, + body: comment, + }); + + await github.rest.pulls.update({ + owner, + repo, + pull_number: prNumber, + state: "closed", + }); + + await github.rest.issues.addLabels({ + owner, + repo, + issue_number: prNumber, + labels: ["missing pr template"], + }); diff --git a/.github/workflows/delete-merged-branches.yml b/.github/workflows/delete-merged-branches.yml index aafaf2b44..09a9f8ab1 100644 --- a/.github/workflows/delete-merged-branches.yml +++ b/.github/workflows/delete-merged-branches.yml @@ -91,6 +91,30 @@ jobs: let skipped = 0; for (const branch of candidates) { + // A branch name can be reused after an earlier PR was merged. Never delete a + // branch while it is the head of a current open PR, even if it is also a + // candidate from an older merged PR. + try { + const { data: openPrs } = await github.rest.pulls.list({ + owner, + repo, + state: "open", + head: `${owner}:${branch}`, + per_page: 1, + }); + + if (openPrs.length > 0) { + console.log(`Skipped "${branch}" (head of open PR #${openPrs[0].number})`); + skipped++; + continue; + } + } catch (error) { + // Do not risk deleting a branch if GitHub cannot confirm it has no open PR. + console.log(`Failed to check open PRs for "${branch}": ${error.message}`); + skipped++; + continue; + } + // Confirm the branch still exists and isn't protected. let branchData; try { diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dabf3bbe..ab484bb31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,9 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit + + + @@ -87,7 +90,7 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
-

July (4 entries)

+

July (11 entries)

[View July 2026 Changelog](.github/changelogs/2026/07.md) @@ -499,6 +502,208 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
+## 2026-07-17 + +### 🆕 New Scripts + + - Invidious ([#15824](https://github.com/community-scripts/ProxmoxVE/pull/15824)) +- OxiCloud ([#15823](https://github.com/community-scripts/ProxmoxVE/pull/15823)) + +### 🚀 Updated Scripts + + - #### 🐞 Bug Fixes + + - webtrees: initialize database schema before admin user creation [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#15837](https://github.com/community-scripts/ProxmoxVE/pull/15837)) + - Fix DocuSeal missing Leptonica deps on install and update [@Copilot](https://github.com/Copilot) ([#15858](https://github.com/community-scripts/ProxmoxVE/pull/15858)) + - apache-guacamole: detect installed extensions during update [@TowyTowy](https://github.com/TowyTowy) ([#15841](https://github.com/community-scripts/ProxmoxVE/pull/15841)) + - CLIProxyAPI: fix update deleting config.yaml [@austinpilz](https://github.com/austinpilz) ([#15834](https://github.com/community-scripts/ProxmoxVE/pull/15834)) + - esphome: install libusb-1.0-0 for ESP-IDF native builds [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#15838](https://github.com/community-scripts/ProxmoxVE/pull/15838)) + + - #### ✨ New Features + + - AFFiNE: Bump to 0.27.0 [@MickLesk](https://github.com/MickLesk) ([#15848](https://github.com/community-scripts/ProxmoxVE/pull/15848)) + - n8n: unpin / use latest release [@MickLesk](https://github.com/MickLesk) ([#15817](https://github.com/community-scripts/ProxmoxVE/pull/15817)) + - Pin Opencloud to v7.3.0 [@vhsdream](https://github.com/vhsdream) ([#15826](https://github.com/community-scripts/ProxmoxVE/pull/15826)) + + - #### 🔧 Refactor + + - SFTPGo: Update APT Repo & Re-Enable Script [@MickLesk](https://github.com/MickLesk) ([#15829](https://github.com/community-scripts/ProxmoxVE/pull/15829)) + +### 💾 Core + + - #### ✨ New Features + + - tools.func: enhance rbenv with profile updates / bundle in bashrc [@MickLesk](https://github.com/MickLesk) ([#15822](https://github.com/community-scripts/ProxmoxVE/pull/15822)) + - feat(build.func): notify users when already on a pinned script version [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#15819](https://github.com/community-scripts/ProxmoxVE/pull/15819)) + + - #### 💥 Breaking Changes + + - MongoDB: Implement kernel version check and patch [@MickLesk](https://github.com/MickLesk) ([#15821](https://github.com/community-scripts/ProxmoxVE/pull/15821)) + +### 🧰 Tools + + - #### ✨ New Features + + - update-lxc: autoremove and autoclean after apt full-upgrade [@soupy-boy](https://github.com/soupy-boy) ([#15831](https://github.com/community-scripts/ProxmoxVE/pull/15831)) + +## 2026-07-16 + +### 🆕 New Scripts + + - Sync-In ([#15812](https://github.com/community-scripts/ProxmoxVE/pull/15812)) +- Beaverhabits ([#15813](https://github.com/community-scripts/ProxmoxVE/pull/15813)) +- Notediscovery ([#15811](https://github.com/community-scripts/ProxmoxVE/pull/15811)) + +### 🚀 Updated Scripts + + - #### 🐞 Bug Fixes + + - Pin Immich to v3.0.3 [@vhsdream](https://github.com/vhsdream) ([#15790](https://github.com/community-scripts/ProxmoxVE/pull/15790)) + +## 2026-07-15 + +### 🆕 New Scripts + + - Nexterm ([#15688](https://github.com/community-scripts/ProxmoxVE/pull/15688)) + +### 🚀 Updated Scripts + + - #### 🐞 Bug Fixes + + - 2fauth: minor fixes for 8.0.0 [@MickLesk](https://github.com/MickLesk) ([#15795](https://github.com/community-scripts/ProxmoxVE/pull/15795)) + - SnapOtter: refactor update process to prebuild [@MickLesk](https://github.com/MickLesk) ([#15797](https://github.com/community-scripts/ProxmoxVE/pull/15797)) + +### 💾 Core + + - #### 🔧 Refactor + + - tools.func: default Docker setup to official repo [@MickLesk](https://github.com/MickLesk) ([#15794](https://github.com/community-scripts/ProxmoxVE/pull/15794)) + +## 2026-07-14 + +### 🆕 New Scripts + + - Grav ([#15773](https://github.com/community-scripts/ProxmoxVE/pull/15773)) +- Yuvomi ([#15772](https://github.com/community-scripts/ProxmoxVE/pull/15772)) + +### 🚀 Updated Scripts + + - #### 🐞 Bug Fixes + + - Lychee: Preserve uploads and ownership during update [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#15768](https://github.com/community-scripts/ProxmoxVE/pull/15768)) + - Wanderer: Clean deploy and install plugins for v0.20.0 update [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#15759](https://github.com/community-scripts/ProxmoxVE/pull/15759)) + - FileFlows: Handle update API 401, force update, and Node install [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#15766](https://github.com/community-scripts/ProxmoxVE/pull/15766)) + - BirdNET-Go: Match new upstream release asset naming [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#15758](https://github.com/community-scripts/ProxmoxVE/pull/15758)) + - [Upstream Fix] Immich: Fix loader priority [@vhsdream](https://github.com/vhsdream) ([#15755](https://github.com/community-scripts/ProxmoxVE/pull/15755)) + + - #### ✨ New Features + + - Bump OpenCloud version to v7.2.2 [@MickLesk](https://github.com/MickLesk) ([#15769](https://github.com/community-scripts/ProxmoxVE/pull/15769)) + - Silverbullet: Add optional Runtime API install via Chromium [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#15761](https://github.com/community-scripts/ProxmoxVE/pull/15761)) + + - #### 💥 Breaking Changes + + - Pangolin: Bump to 1.20.0 | BREAKING: Switch to PostgreSQL [@MickLesk](https://github.com/MickLesk) ([#15682](https://github.com/community-scripts/ProxmoxVE/pull/15682)) + + - #### 🔧 Refactor + + - AFFiNE: Pin to v0.26.3 [@MickLesk](https://github.com/MickLesk) ([#15782](https://github.com/community-scripts/ProxmoxVE/pull/15782)) + +## 2026-07-13 + +### 🆕 New Scripts + + - LeafWiki ([#15748](https://github.com/community-scripts/ProxmoxVE/pull/15748)) + +### 🚀 Updated Scripts + + - #### 🐞 Bug Fixes + + - fix(hyperion): keep service running after container reboot [@TowyTowy](https://github.com/TowyTowy) ([#15653](https://github.com/community-scripts/ProxmoxVE/pull/15653)) + - Change sign-in URL to admin URL in affine.sh [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#15741](https://github.com/community-scripts/ProxmoxVE/pull/15741)) + - immich: use actual PostgreSQL version for VectorChord package lookup [@mnavon](https://github.com/mnavon) ([#15705](https://github.com/community-scripts/ProxmoxVE/pull/15705)) + - fix storyteller release selection for stable web tags [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#15736](https://github.com/community-scripts/ProxmoxVE/pull/15736)) + - Docmost: Fix update procedure [@MickLesk](https://github.com/MickLesk) ([#15732](https://github.com/community-scripts/ProxmoxVE/pull/15732)) + - fix(shinobi): remove obsolete --unsafe-perm npm flag [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#15730](https://github.com/community-scripts/ProxmoxVE/pull/15730)) + + - #### 💥 Breaking Changes + + - reitti: update to v5 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#15635](https://github.com/community-scripts/ProxmoxVE/pull/15635)) + +### 💾 Core + + - #### 🐞 Bug Fixes + + - fix(build.func): parse script status without jq dependency [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#15729](https://github.com/community-scripts/ProxmoxVE/pull/15729)) + + - #### 🔧 Refactor + + - tools.func: some improvements (sql injection / command injection / guard) [@MickLesk](https://github.com/MickLesk) ([#15661](https://github.com/community-scripts/ProxmoxVE/pull/15661)) + +## 2026-07-12 + +### 🆕 New Scripts + + - AFFiNE ([#15690](https://github.com/community-scripts/ProxmoxVE/pull/15690)) + +### 🚀 Updated Scripts + + - Immich: Bump version to 3.0.2 [@vhsdream](https://github.com/vhsdream) ([#15668](https://github.com/community-scripts/ProxmoxVE/pull/15668)) + +### ❔ Uncategorized + + - fix(immich): correct Python indentation error in ct/immich.sh heredoc patch [@Copilot](https://github.com/Copilot) ([#15723](https://github.com/community-scripts/ProxmoxVE/pull/15723)) + +## 2026-07-11 + +### 🆕 New Scripts + + - LocalAGI ([#15687](https://github.com/community-scripts/ProxmoxVE/pull/15687)) + +### 🚀 Updated Scripts + + - fix(adventurelog): allow pnpm build scripts so install/update doesn't abort [@TowyTowy](https://github.com/TowyTowy) ([#15681](https://github.com/community-scripts/ProxmoxVE/pull/15681)) + + - #### 🐞 Bug Fixes + + - fix(fileflows): install .NET 10 ASP.NET Core Runtime to match current release [@TowyTowy](https://github.com/TowyTowy) ([#15702](https://github.com/community-scripts/ProxmoxVE/pull/15702)) + - Fix spacing on VLAN Input Box in haos-vm.sh [@pumrum](https://github.com/pumrum) ([#15696](https://github.com/community-scripts/ProxmoxVE/pull/15696)) + +### 💾 Core + + - #### ✨ New Features + + - [tools.func]: Add function to handle deployment via GitLab release tags [@tremor021](https://github.com/tremor021) ([#15641](https://github.com/community-scripts/ProxmoxVE/pull/15641)) + +## 2026-07-10 + +### 🆕 New Scripts + + - Squid ([#15605](https://github.com/community-scripts/ProxmoxVE/pull/15605)) + +### 🚀 Updated Scripts + + - #### 🐞 Bug Fixes + + - Adapt to new artifact filename format for pocket id [@wollew](https://github.com/wollew) ([#15689](https://github.com/community-scripts/ProxmoxVE/pull/15689)) + - Fireshare: Fix for install and upgrade to v1.7.3 [@tremor021](https://github.com/tremor021) ([#15673](https://github.com/community-scripts/ProxmoxVE/pull/15673)) + - Endurain: Fix update procedure [@tremor021](https://github.com/tremor021) ([#15674](https://github.com/community-scripts/ProxmoxVE/pull/15674)) + +## 2026-07-09 + +### 🚀 Updated Scripts + + - fix(pihole): repair Unbound DNS-over-TLS (DoT) forwarding config [@TowyTowy](https://github.com/TowyTowy) ([#15654](https://github.com/community-scripts/ProxmoxVE/pull/15654)) + +## 2026-07-08 + +### 🚀 Updated Scripts + + - #### 🐞 Bug Fixes + + - Opencloud: Bump version to 7.2.1 [@vhsdream](https://github.com/vhsdream) ([#15655](https://github.com/community-scripts/ProxmoxVE/pull/15655)) + - BabyBuddy: Harden update script [@MickLesk](https://github.com/MickLesk) ([#15642](https://github.com/community-scripts/ProxmoxVE/pull/15642)) + ## 2026-07-07 ### 🆕 New Scripts @@ -1054,135 +1259,4 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit - #### ✨ New Features - - [core] Implement backup and restore functions [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#15067](https://github.com/community-scripts/ProxmoxVE/pull/15067)) - -## 2026-06-11 - -### 🆕 New Scripts - - - Clickhouse ([#15045](https://github.com/community-scripts/ProxmoxVE/pull/15045)) - -### 🚀 Updated Scripts - - - #### 🐞 Bug Fixes - - - Manyfold: add new dependency [@MickLesk](https://github.com/MickLesk) ([#15040](https://github.com/community-scripts/ProxmoxVE/pull/15040)) - - OpenArchiver: switch Rebuild Function [@MickLesk](https://github.com/MickLesk) ([#15042](https://github.com/community-scripts/ProxmoxVE/pull/15042)) - - CLIProxyAPI: Save management password to creds file [@tremor021](https://github.com/tremor021) ([#15051](https://github.com/community-scripts/ProxmoxVE/pull/15051)) - - Jotty: Fix wrong path test in config restore [@vhsdream](https://github.com/vhsdream) ([#15038](https://github.com/community-scripts/ProxmoxVE/pull/15038)) - - Fix for cross-seed after node upgrade [@TorinFrancis](https://github.com/TorinFrancis) ([#15025](https://github.com/community-scripts/ProxmoxVE/pull/15025)) - - - #### 🔧 Refactor - - - Alpine-Nextcloud: Upgrade PHP and dependencies in installation script [@MickLesk](https://github.com/MickLesk) ([#15039](https://github.com/community-scripts/ProxmoxVE/pull/15039)) - - [arm64] porting stage 1: set script arm64 statuses to yes [@asylumexp](https://github.com/asylumexp) ([#15052](https://github.com/community-scripts/ProxmoxVE/pull/15052)) - -### 💾 Core - - - #### ✨ New Features - - - misc scripts: add support for arm64 [@asylumexp](https://github.com/asylumexp) ([#12639](https://github.com/community-scripts/ProxmoxVE/pull/12639)) - - - #### 🔧 Refactor - - - [arm64] remove logic for custom debian arm64 template [@asylumexp](https://github.com/asylumexp) ([#15050](https://github.com/community-scripts/ProxmoxVE/pull/15050)) - -### 📚 Documentation - - - (github): Revise script request template [@MickLesk](https://github.com/MickLesk) ([#15058](https://github.com/community-scripts/ProxmoxVE/pull/15058)) - -## 2026-06-10 - -### 🆕 New Scripts - - - Baserow ([#14968](https://github.com/community-scripts/ProxmoxVE/pull/14968)) - -### 🚀 Updated Scripts - - - #### 🐞 Bug Fixes - - - Koillection: Fix update procedure [@tremor021](https://github.com/tremor021) ([#15033](https://github.com/community-scripts/ProxmoxVE/pull/15033)) - -## 2026-06-09 - -### 🆕 New Scripts - - - paperclip ([#14990](https://github.com/community-scripts/ProxmoxVE/pull/14990)) - -### 🚀 Updated Scripts - - - #### 🐞 Bug Fixes - - - endurain: Install pytz package during backend setup [@MickLesk](https://github.com/MickLesk) ([#15014](https://github.com/community-scripts/ProxmoxVE/pull/15014)) - - - #### 🔧 Refactor - - - Refactor: Proxmox Backup Server - use deb822 [@MickLesk](https://github.com/MickLesk) ([#15013](https://github.com/community-scripts/ProxmoxVE/pull/15013)) - -## 2026-06-08 - -### 🚀 Updated Scripts - - - #### 🐞 Bug Fixes - - - security: Fix HTTP to HTTPS for all package and repository downloads [@MickLesk](https://github.com/MickLesk) ([#15009](https://github.com/community-scripts/ProxmoxVE/pull/15009)) - - homelable: preserve MCP server config across updates [@ferr079](https://github.com/ferr079) ([#14996](https://github.com/community-scripts/ProxmoxVE/pull/14996)) - - changedetection: migrate Python install to uv venv [@ferr079](https://github.com/ferr079) ([#14995](https://github.com/community-scripts/ProxmoxVE/pull/14995)) - - - #### 🔧 Refactor - - - Update Flowwiseai to node 24 [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#14999](https://github.com/community-scripts/ProxmoxVE/pull/14999)) - -### 🧰 Tools - - - #### 🐞 Bug Fixes - - - security: Fix MITM RCE vulnerability in microcode scripts (CVE) [@MickLesk](https://github.com/MickLesk) ([#15007](https://github.com/community-scripts/ProxmoxVE/pull/15007)) - -## 2026-06-07 - -### 🚀 Updated Scripts - - - #### 🐞 Bug Fixes - - - Immich: use actual installed PostgreSQL version for vchord package [@MickLesk](https://github.com/MickLesk) ([#14989](https://github.com/community-scripts/ProxmoxVE/pull/14989)) - - - #### 🔧 Refactor - - - Navidrome: remove genereic filebrowser addon setup [@MickLesk](https://github.com/MickLesk) ([#14991](https://github.com/community-scripts/ProxmoxVE/pull/14991)) - -## 2026-06-06 - -### 🆕 New Scripts - - - Spliit ([#14966](https://github.com/community-scripts/ProxmoxVE/pull/14966)) -- Tolgee ([#14965](https://github.com/community-scripts/ProxmoxVE/pull/14965)) -- XYOps ([#14967](https://github.com/community-scripts/ProxmoxVE/pull/14967)) - -### 🚀 Updated Scripts - - - #### 🐞 Bug Fixes - - - Photoprism: Allow env variables with spaces [@Badintral](https://github.com/Badintral) ([#14969](https://github.com/community-scripts/ProxmoxVE/pull/14969)) - -## 2026-06-05 - -### 🆕 New Scripts - - - MatterJS-Server ([#14951](https://github.com/community-scripts/ProxmoxVE/pull/14951)) -- CyberChef ([#14952](https://github.com/community-scripts/ProxmoxVE/pull/14952)) - -### 🚀 Updated Scripts - - - #### 🐞 Bug Fixes - - - Jackett: Create missing .env file [@tremor021](https://github.com/tremor021) ([#14959](https://github.com/community-scripts/ProxmoxVE/pull/14959)) - - OpenThread-BR: use systemd instead of init.d [@tomfrenzel](https://github.com/tomfrenzel) ([#14942](https://github.com/community-scripts/ProxmoxVE/pull/14942)) - - - #### ✨ New Features - - - AMD IGPU support [@Learath](https://github.com/Learath) ([#14944](https://github.com/community-scripts/ProxmoxVE/pull/14944)) - - - #### 💥 Breaking Changes - - - update authentik to 2026.5.2 [@thieneret](https://github.com/thieneret) ([#14846](https://github.com/community-scripts/ProxmoxVE/pull/14846)) \ No newline at end of file + - [core] Implement backup and restore functions [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#15067](https://github.com/community-scripts/ProxmoxVE/pull/15067)) \ No newline at end of file diff --git a/ct/2fauth.sh b/ct/2fauth.sh index 506f140d8..5b5dc3eb7 100644 --- a/ct/2fauth.sh +++ b/ct/2fauth.sh @@ -60,6 +60,7 @@ function update_script() { php artisan 2fauth:install chown -R www-data: /opt/2fauth chmod -R 755 /opt/2fauth + $STD php artisan 2fauth:fix-passport-key-permissions $STD systemctl restart php8.4-fpm $STD systemctl restart nginx msg_ok "Configured 2FAuth" diff --git a/ct/adventurelog.sh b/ct/adventurelog.sh index 0da4c09c0..98327747e 100644 --- a/ct/adventurelog.sh +++ b/ct/adventurelog.sh @@ -60,6 +60,7 @@ function update_script() { $STD .venv/bin/python -m manage migrate cd /opt/adventurelog/frontend + grep -q "^dangerouslyAllowAllBuilds:" ./pnpm-workspace.yaml 2>/dev/null || echo "dangerouslyAllowAllBuilds: true" >>./pnpm-workspace.yaml $STD pnpm i $STD pnpm build msg_ok "Updated AdventureLog" diff --git a/ct/affine.sh b/ct/affine.sh new file mode 100644 index 000000000..1480c2a99 --- /dev/null +++ b/ct/affine.sh @@ -0,0 +1,131 @@ +#!/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/toeverything/AFFiNE + +APP="AFFiNE" +var_tags="${var_tags:-knowledge;notes;workspace}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-8192}" +var_disk="${var_disk:-20}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_arm64="${var_arm64:-no}" +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/affine ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE="v0.27.0" + if check_for_gh_release "affine_app" "toeverything/AFFiNE" "${RELEASE}" "each release is tested individually before the version is updated. Please do not open issues for this"; then + msg_info "Stopping Services" + systemctl stop affine-web affine-worker + msg_ok "Stopped Services" + + ensure_dependencies cmake + + create_backup /root/.affine/config /root/.affine/storage + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "affine_app" "toeverything/AFFiNE" "tarball" "${RELEASE}" "/opt/affine" + + msg_info "Rebuilding Application (Patience ~25 mins, don't close the console!)" + cd /opt/affine + source /root/.profile + export PATH="/root/.cargo/bin:/root/.rbenv/shims:$PATH" + + set -a && source /opt/affine/.env && set +a + + export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 + export VITE_CORE_COMMIT_SHA=$(cat ~/.affine_app) + + # Initialize git repo (required for build process) + export HUSKY=0 + $STD git init -q + $STD git config user.email "build@local" + $STD git config user.name "Build" + $STD git add -A + $STD git commit -q -m "update" --no-verify --allow-empty + + # Force Turbo to run sequentially + mkdir -p /opt/affine/.turbo + cat </opt/affine/.turbo/config.json +{ + "concurrency": 1 +} +TURBO + + $STD corepack enable + $STD corepack prepare yarn@4.13.0 --activate + $STD yarn config set enableTelemetry 0 + + export NODE_OPTIONS="--max-old-space-size=2048" + $STD yarn install + $STD npm install -g typescript + + $STD yarn affine @affine/native build + $STD yarn affine @affine/server-native build + + # Create architecture-specific symlinks + ln -sf /opt/affine/packages/backend/native/server-native.node \ + /opt/affine/packages/backend/native/server-native.x64.node + ln -sf /opt/affine/packages/backend/native/server-native.node \ + /opt/affine/packages/backend/native/server-native.arm64.node + ln -sf /opt/affine/packages/backend/native/server-native.node \ + /opt/affine/packages/backend/native/server-native.armv7.node + + $STD yarn affine init + $STD yarn affine build -p @affine/reader + $STD yarn affine build -p @affine/server + + export NODE_OPTIONS="--max-old-space-size=4096" + $STD yarn affine build -p @affine/web + $STD yarn affine build -p @affine/admin + + # Copy web assets + mkdir -p /opt/affine/packages/backend/server/static + cp -r /opt/affine/packages/frontend/apps/web/dist/* /opt/affine/packages/backend/server/static/ + mkdir -p /opt/affine/packages/backend/server/static/admin + cp -r /opt/affine/packages/frontend/admin/dist/* /opt/affine/packages/backend/server/static/admin/ + + # Mobile manifest placeholder + mkdir -p /opt/affine/packages/backend/server/static/mobile + echo '{"publicPath":"/","js":[],"css":[],"gitHash":"","description":""}' \ + >/opt/affine/packages/backend/server/static/mobile/assets-manifest.json + + # Run migrations + cd /opt/affine/packages/backend/server + set -a && source /opt/affine/.env && set +a + $STD node ./scripts/self-host-predeploy.js + + restore_backup + + msg_info "Starting Services" + systemctl start affine-web affine-worker + 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 "${GATEWAY}${BGN}http://${IP}:3010/admin${CL}" diff --git a/ct/apache-guacamole.sh b/ct/apache-guacamole.sh index bbd9673cc..89f1b973b 100644 --- a/ct/apache-guacamole.sh +++ b/ct/apache-guacamole.sh @@ -146,7 +146,7 @@ function update_script() { # Check and upgrade optional extensions # TOTP Extension - if [[ -f /etc/guacamole/extensions/guacamole-auth-totp-*.jar ]]; then + if compgen -G "/etc/guacamole/extensions/guacamole-auth-totp-*.jar" >/dev/null; then msg_info "Updating TOTP Extension" rm -f /etc/guacamole/extensions/guacamole-auth-totp-*.jar curl_download "/tmp/guacamole-auth-totp.tar.gz" "https://downloads.apache.org/guacamole/${LATEST_SERVER}/binary/guacamole-auth-totp-${LATEST_SERVER}.tar.gz" @@ -158,7 +158,7 @@ function update_script() { fi # DUO Extension - if [[ -f /etc/guacamole/extensions/guacamole-auth-duo-*.jar ]]; then + if compgen -G "/etc/guacamole/extensions/guacamole-auth-duo-*.jar" >/dev/null; then msg_info "Updating DUO Extension" rm -f /etc/guacamole/extensions/guacamole-auth-duo-*.jar curl_download "/tmp/guacamole-auth-duo.tar.gz" "https://downloads.apache.org/guacamole/${LATEST_SERVER}/binary/guacamole-auth-duo-${LATEST_SERVER}.tar.gz" @@ -170,7 +170,7 @@ function update_script() { fi # LDAP Extension - if [[ -f /etc/guacamole/extensions/guacamole-auth-ldap-*.jar ]]; then + if compgen -G "/etc/guacamole/extensions/guacamole-auth-ldap-*.jar" >/dev/null; then msg_info "Updating LDAP Extension" rm -f /etc/guacamole/extensions/guacamole-auth-ldap-*.jar curl_download "/tmp/guacamole-auth-ldap.tar.gz" "https://downloads.apache.org/guacamole/${LATEST_SERVER}/binary/guacamole-auth-ldap-${LATEST_SERVER}.tar.gz" @@ -182,7 +182,7 @@ function update_script() { fi # Quick Connect Extension - if [[ -f /etc/guacamole/extensions/guacamole-auth-quickconnect-*.jar ]]; then + if compgen -G "/etc/guacamole/extensions/guacamole-auth-quickconnect-*.jar" >/dev/null; then msg_info "Updating Quick Connect Extension" rm -f /etc/guacamole/extensions/guacamole-auth-quickconnect-*.jar curl_download "/tmp/guacamole-auth-quickconnect.tar.gz" "https://downloads.apache.org/guacamole/${LATEST_SERVER}/binary/guacamole-auth-quickconnect-${LATEST_SERVER}.tar.gz" @@ -194,7 +194,7 @@ function update_script() { fi # History Recording Storage Extension - if [[ -f /etc/guacamole/extensions/guacamole-history-recording-storage-*.jar ]]; then + if compgen -G "/etc/guacamole/extensions/guacamole-history-recording-storage-*.jar" >/dev/null; then msg_info "Updating History Recording Storage Extension" rm -f /etc/guacamole/extensions/guacamole-history-recording-storage-*.jar curl_download "/tmp/guacamole-history-recording-storage.tar.gz" "https://downloads.apache.org/guacamole/${LATEST_SERVER}/binary/guacamole-history-recording-storage-${LATEST_SERVER}.tar.gz" diff --git a/ct/babybuddy.sh b/ct/babybuddy.sh index 29fc7703a..b718a42ef 100644 --- a/ct/babybuddy.sh +++ b/ct/babybuddy.sh @@ -40,7 +40,8 @@ function update_script() { create_backup /opt/babybuddy/babybuddy/settings/production.py msg_info "Cleaning old files" - find . -mindepth 1 -maxdepth 1 ! -name '.venv' -exec rm -rf {} + + cd /opt/babybuddy || exit + find . -mindepth 1 -maxdepth 1 ! -name '.venv' -exec rm -rf -- {} + msg_ok "Cleaned old files" fetch_and_deploy_gh_release "babybuddy" "babybuddy/babybuddy" "tarball" @@ -51,6 +52,7 @@ function update_script() { source .venv/bin/activate $STD uv pip install -r requirements.txt export DJANGO_SETTINGS_MODULE=babybuddy.settings.production + $STD python manage.py makemigrations $STD python manage.py migrate msg_ok "Updated ${APP}" diff --git a/ct/beaverhabits.sh b/ct/beaverhabits.sh new file mode 100644 index 000000000..168be42d4 --- /dev/null +++ b/ct/beaverhabits.sh @@ -0,0 +1,64 @@ +#!/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/daya0576/beaverhabits + +APP="BeaverHabits" +var_tags="${var_tags:-habits;tracking;productivity}" +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_arm64="${var_arm64:-yes}" +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/beaverhabits ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "beaverhabits" "daya0576/beaverhabits"; then + msg_info "Stopping Service" + systemctl stop beaverhabits + msg_ok "Stopped Service" + + create_backup /opt/beaverhabits/.user + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "beaverhabits" "daya0576/beaverhabits" "tarball" + + msg_info "Syncing Dependencies" + cd /opt/beaverhabits + $STD uv sync --no-dev + msg_ok "Synced Dependencies" + + restore_backup + + msg_info "Starting Service" + systemctl start beaverhabits + 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 "${GATEWAY}${BGN}http://${IP}:8080/register{CL}" diff --git a/ct/birdnet-go.sh b/ct/birdnet-go.sh index 39db9ca1f..6319219f9 100644 --- a/ct/birdnet-go.sh +++ b/ct/birdnet-go.sh @@ -37,7 +37,7 @@ function update_script() { systemctl stop birdnet msg_ok "Stopped Service" - fetch_and_deploy_gh_release "birdnet" "tphakala/birdnet-go" "prebuild" "latest" "/opt/birdnet" "birdnet-go-linux-$(arch_resolve).tar.gz" + fetch_and_deploy_gh_release "birdnet" "tphakala/birdnet-go" "prebuild" "latest" "/opt/birdnet" "birdnet-go-linux-$(arch_resolve)*.tar.gz" msg_info "Deploying Binary" cp /opt/birdnet/birdnet-go /usr/local/bin/birdnet-go diff --git a/ct/cliproxyapi.sh b/ct/cliproxyapi.sh index 9eb49a1a6..40f9f2b1f 100644 --- a/ct/cliproxyapi.sh +++ b/ct/cliproxyapi.sh @@ -36,8 +36,12 @@ function update_script() { systemctl stop cliproxyapi msg_ok "Stopped CLIProxyAPI" + create_backup /opt/cliproxyapi/config.yaml + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "cliproxyapi" "router-for-me/CLIProxyAPI" "prebuild" "latest" "/opt/cliproxyapi" "CLIProxyAPI_*_linux_$(arch_resolve "amd64" "aarch64").tar.gz" + restore_backup + msg_info "Starting CLIProxyAPI" systemctl start cliproxyapi msg_ok "Started CLIProxyAPI" diff --git a/ct/docmost.sh b/ct/docmost.sh index c3392c585..1c6271c98 100644 --- a/ct/docmost.sh +++ b/ct/docmost.sh @@ -39,7 +39,6 @@ function update_script() { create_backup /opt/docmost/.env \ /opt/docmost/data - fetch_and_deploy_gh_release "docmost" "docmost/docmost" "tarball" restore_backup @@ -54,9 +53,11 @@ function update_script() { sed -i '/^@Module({$/i @Global()' /opt/docmost/apps/server/src/core/core.module.ts fi + msg_info "Configuring Docmost" + cd /opt/docmost $STD pnpm install --force $STD pnpm build - msg_ok "Updated ${APP}" + msg_ok "Configured Docmost" msg_info "Starting Service" systemctl start docmost diff --git a/ct/docuseal.sh b/ct/docuseal.sh index d856d1694..9fbdca313 100644 --- a/ct/docuseal.sh +++ b/ct/docuseal.sh @@ -35,6 +35,8 @@ function update_script() { systemctl stop docuseal docuseal-sidekiq msg_ok "Stopped Services" + ensure_dependencies libleptonica-dev libleptonica6 + create_backup /opt/docuseal/.env \ /opt/docuseal/data diff --git a/ct/endurain.sh b/ct/endurain.sh index 934532aea..cdfd6eb0a 100644 --- a/ct/endurain.sh +++ b/ct/endurain.sh @@ -34,31 +34,22 @@ function update_script() { systemctl stop endurain msg_ok "Stopped Service" - msg_info "Creating Backup" - cp /opt/endurain/.env /opt/endurain.env - cp /opt/endurain/frontend/dist/env.js /opt/endurain.env.js - msg_ok "Created Backup" - + create_backup /opt/endurain/.env /opt/endurain/frontend/dist/env.js CLEAN_INSTALL=1 fetch_and_deploy_codeberg_release "endurain" "endurain-project/endurain" "tarball" "latest" "/opt/endurain" msg_info "Preparing Update" cd /opt/endurain - rm -rf \ - /opt/endurain/{docs,example.env,screenshot_01.png} \ - /opt/endurain/docker* \ - /opt/endurain/*.yml - cp /opt/endurain.env /opt/endurain/.env - rm /opt/endurain.env + rm -rf /opt/endurain/{docs,example.env,screenshot_01.png} /opt/endurain/docker* /opt/endurain/*.yml msg_ok "Prepared Update" msg_info "Updating Frontend" cd /opt/endurain/frontend $STD npm ci $STD npm run build - cp /opt/endurain.env.js /opt/endurain/frontend/dist/env.js - rm /opt/endurain.env.js msg_ok "Updated Frontend" + restore_backup + msg_info "Updating Backend" cd /opt/endurain/backend UV_VERSION=$(grep -Po 'required-version\s*=\s*"\K[^"]+' pyproject.toml 2>/dev/null || echo "0.11.18") diff --git a/ct/esphome.sh b/ct/esphome.sh index 4dcf1ea06..086be0e83 100644 --- a/ct/esphome.sh +++ b/ct/esphome.sh @@ -28,6 +28,7 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi + ensure_dependencies libusb-1.0-0 msg_info "Stopping Service" systemctl stop esphome-device-builder 2>/dev/null || true diff --git a/ct/fileflows.sh b/ct/fileflows.sh index 1f33d2dd0..3f72cef69 100644 --- a/ct/fileflows.sh +++ b/ct/fileflows.sh @@ -31,28 +31,83 @@ function update_script() { exit fi - update_available=$(curl -fsSL -X 'GET' "http://localhost:19200/api/status/update-available" -H 'accept: application/json' | jq .UpdateAvailable) - if [[ "${update_available}" == "true" ]]; then - msg_info "Stopping Service" - systemctl --all stop 'fileflows*' - msg_info "Stopped Service" + local proceed=false - msg_info "Creating Backup" - ls /opt/*.tar.gz &>/dev/null && rm -f /opt/*.tar.gz - backup_filename="/opt/${APP}_backup_$(date +%F).tar.gz" - tar -czf "$backup_filename" -C /opt/fileflows Data - msg_ok "Backup Created" - - fetch_and_deploy_from_url "https://fileflows.com/downloads/zip" "/opt/fileflows" - - msg_info "Starting Service" - systemctl --all start 'fileflows*' - msg_ok "Started Service" - msg_ok "Updated successfully!" + if systemctl list-unit-files 'fileflows.service' --no-legend 2>/dev/null | grep -q '^fileflows\.service'; then + tmp=$(mktemp) + http_code=$(curl -sSL -X 'GET' "http://localhost:19200/api/status/update-available" -H 'accept: application/json' -o "$tmp" -w '%{http_code}' 2>/dev/null) || http_code="000" + if [[ "$http_code" == "200" ]]; then + update_available=$(jq -r '.UpdateAvailable // false' "$tmp" 2>/dev/null) + rm -f "$tmp" + if [[ "${update_available}" == "true" ]]; then + proceed=true + else + msg_ok "No update required. ${APP} is already at latest version" + exit + fi + else + rm -f "$tmp" + if [[ "$http_code" == "401" ]]; then + msg_warn "Could not check for updates: API returned 401 (security may be enabled)." + else + msg_warn "Could not check for updates: API unreachable (HTTP ${http_code})." + fi + if [[ "${FORCE_UPDATE:-}" == "1" ]]; then + proceed=true + else + read -r -p "${TAB3}Force update without version check? [y/N]: " CONFIRM + if [[ "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then + proceed=true + else + msg_error "Update aborted." + exit + fi + fi + fi else - msg_ok "No update required. ${APP} is already at latest version" + proceed=true fi + if [[ "$proceed" != "true" ]]; then + exit + fi + + msg_info "Stopping Service" + systemctl --all stop 'fileflows*' + msg_ok "Stopped Service" + + msg_info "Creating Backup" + ls /opt/*.tar.gz &>/dev/null && rm -f /opt/*.tar.gz + backup_filename="/opt/${APP}_backup_$(date +%F).tar.gz" + tar -czf "$backup_filename" -C /opt/fileflows Data + msg_ok "Backup Created" + + msg_info "Ensuring ASP.NET Core Runtime" + if [[ "$(arch_resolve)" == "arm64" ]]; then + if [[ ! -x /usr/lib/dotnet10/dotnet ]]; then + curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh + $STD bash /tmp/dotnet-install.sh --channel 10.0 --runtime aspnetcore --install-dir /usr/lib/dotnet10 + ln -sf /usr/lib/dotnet10/dotnet /usr/bin/dotnet + rm -f /tmp/dotnet-install.sh + fi + elif ! is_package_installed "aspnetcore-runtime-10.0"; then + $STD apt remove -y aspnetcore-runtime-8.0 aspnetcore-runtime-9.0 2>/dev/null || true + setup_deb822_repo \ + "microsoft" \ + "https://packages.microsoft.com/keys/microsoft-2025.asc" \ + "https://packages.microsoft.com/debian/13/prod/" \ + "trixie" + $STD apt install -y aspnetcore-runtime-10.0 + fi + msg_ok "Ensured ASP.NET Core Runtime" + + fetch_and_deploy_from_url "https://fileflows.com/downloads/zip" "/opt/fileflows" + + msg_info "Starting Service" + systemctl --all start 'fileflows*' + msg_ok "Started Service" + msg_ok "Updated successfully!" + exit } diff --git a/ct/fireshare.sh b/ct/fireshare.sh index 296cb6c01..f10be013b 100644 --- a/ct/fireshare.sh +++ b/ct/fireshare.sh @@ -35,12 +35,22 @@ function update_script() { systemctl stop fireshare msg_ok "Stopped Service" - mv /opt/fireshare/fireshare.env /opt + create_backup /opt/fireshare/fireshare.env CLEAN_INSTALL=1 fetch_and_deploy_gh_release "fireshare" "ShaneIsrael/fireshare" "tarball" - mv /opt/fireshare.env /opt/fireshare + restore_backup rm -f /usr/local/bin/fireshare - msg_info "Updating Fireshare" + if ! grep -q "__FIRESHARE_PORT__" /etc/nginx/nginx.conf; then + cp /opt/fireshare/app/nginx/prod.conf /etc/nginx/nginx.conf + sed -i 's|root /processed/|root /opt/fireshare-processed/|g' /etc/nginx/nginx.conf + sed -i 's/^user[[:space:]]\+nginx;/user root;/' /etc/nginx/nginx.conf + sed -i 's|root[[:space:]]\+/app/build;|root /opt/fireshare/app/client/build;|' /etc/nginx/nginx.conf + sed -i 's/__FIRESHARE_PORT__/80/g' /etc/nginx/nginx.conf + cp /opt/fireshare/app/nginx/error.html /etc/nginx/ + cp /opt/fireshare/app/nginx/api_unavailable.html /etc/nginx/ + fi + msg_info "Configuring Fireshare" + cd /opt/fireshare $STD uv venv --clear $STD .venv/bin/python -m ensurepip --upgrade @@ -53,13 +63,10 @@ function update_script() { export VIDEO_DIRECTORY=/opt/fireshare-videos export PROCESSED_DIRECTORY=/opt/fireshare-processed $STD uv run flask db upgrade - - msg_info "Building Fireshare Client" cd /opt/fireshare/app/client $STD npm install $STD npm run build - msg_ok "Built Fireshare Client" - msg_ok "Updated Fireshare" + msg_ok "Configured Fireshare" msg_info "Starting Service" systemctl start fireshare diff --git a/ct/grav.sh b/ct/grav.sh new file mode 100644 index 000000000..9271a029e --- /dev/null +++ b/ct/grav.sh @@ -0,0 +1,54 @@ +#!/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: Raffaele (rafspiny) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://getgrav.org/ + +APP="Grav" +var_tags="${var_tags:-cms}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_arm64="${var_arm64:-no}" +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/grav" ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "grav" "getgrav/grav"; then + msg_info "Creating Backup" + cd /opt/grav + bin/grav backup -nq + msg_ok "Backup Created" + bin/gpm self-upgrade -y + cd - + chown -R www-data:www-data /opt/grav + msg_ok "Update Successful" + 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 "${GATEWAY}${BGN}http://${IP}:80${CL}" diff --git a/ct/headers/affine b/ct/headers/affine new file mode 100644 index 000000000..0e1d759f0 --- /dev/null +++ b/ct/headers/affine @@ -0,0 +1,6 @@ + ___ _____________ _ ________ + / | / ____/ ____(_) | / / ____/ + / /| | / /_ / /_ / / |/ / __/ + / ___ |/ __/ / __/ / / /| / /___ +/_/ |_/_/ /_/ /_/_/ |_/_____/ + diff --git a/ct/headers/beaverhabits b/ct/headers/beaverhabits new file mode 100644 index 000000000..0a11d4242 --- /dev/null +++ b/ct/headers/beaverhabits @@ -0,0 +1,6 @@ + ____ __ __ __ _ __ + / __ )___ ____ __ _____ _____/ / / /___ _/ /_ (_) /______ + / __ / _ \/ __ `/ | / / _ \/ ___/ /_/ / __ `/ __ \/ / __/ ___/ + / /_/ / __/ /_/ /| |/ / __/ / / __ / /_/ / /_/ / / /_(__ ) +/_____/\___/\__,_/ |___/\___/_/ /_/ /_/\__,_/_.___/_/\__/____/ + diff --git a/ct/headers/grav b/ct/headers/grav new file mode 100644 index 000000000..3e041ab1e --- /dev/null +++ b/ct/headers/grav @@ -0,0 +1,6 @@ + ______ + / ____/________ __ __ + / / __/ ___/ __ `/ | / / +/ /_/ / / / /_/ /| |/ / +\____/_/ \__,_/ |___/ + diff --git a/ct/headers/invidious b/ct/headers/invidious new file mode 100644 index 000000000..c858744dd --- /dev/null +++ b/ct/headers/invidious @@ -0,0 +1,6 @@ + ____ _ ___ + / _/___ _ __(_)___/ (_)___ __ _______ + / // __ \ | / / / __ / / __ \/ / / / ___/ + _/ // / / / |/ / / /_/ / / /_/ / /_/ (__ ) +/___/_/ /_/|___/_/\__,_/_/\____/\__,_/____/ + diff --git a/ct/headers/leafwiki b/ct/headers/leafwiki new file mode 100644 index 000000000..dcfacbddf --- /dev/null +++ b/ct/headers/leafwiki @@ -0,0 +1,6 @@ + __ _____ ___ __ _ + / / ___ ____ _/ __/ | / (_) /__(_) + / / / _ \/ __ `/ /_ | | /| / / / //_/ / + / /___/ __/ /_/ / __/ | |/ |/ / / ,< / / +/_____/\___/\__,_/_/ |__/|__/_/_/|_/_/ + diff --git a/ct/headers/localagi b/ct/headers/localagi new file mode 100644 index 000000000..c47da9aac --- /dev/null +++ b/ct/headers/localagi @@ -0,0 +1,6 @@ + __ _____ __________ + / / ____ _________ _/ / | / ____/ _/ + / / / __ \/ ___/ __ `/ / /| |/ / __ / / + / /___/ /_/ / /__/ /_/ / / ___ / /_/ // / +/_____/\____/\___/\__,_/_/_/ |_\____/___/ + diff --git a/ct/headers/nexterm b/ct/headers/nexterm new file mode 100644 index 000000000..7d52bc561 --- /dev/null +++ b/ct/headers/nexterm @@ -0,0 +1,6 @@ + _ __ __ + / | / /__ _ __/ /____ _________ ___ + / |/ / _ \| |/_/ __/ _ \/ ___/ __ `__ \ + / /| / __/> "2.5.1" ]]; then msg_info "Enabling Maintenance Mode" @@ -124,7 +124,9 @@ EOF systemctl stop immich-web systemctl stop immich-ml msg_ok "Stopped Services" - VCHORD_RELEASE="1.0.0" + VCHORD_RELEASE="1.1.1" + PG_VERSION=$(ls /etc/postgresql/ 2>/dev/null | sort -V | tail -1) + PG_VERSION=${PG_VERSION:-16} [[ -f ~/.vchord_version ]] && mv ~/.vchord_version ~/.vectorchord if check_for_gh_release "VectorChord" "tensorchord/VectorChord" "${VCHORD_RELEASE}" "updated together with Immich after testing"; then # dead tuples in smart_search/face_search make the REINDEX below fail with @@ -132,7 +134,7 @@ EOF # while still on the old extension version, a post-upgrade vacuum errors instead $STD sudo -u postgres psql -d immich -c "VACUUM (ANALYZE) smart_search;" $STD sudo -u postgres psql -d immich -c "VACUUM (ANALYZE) face_search;" - fetch_and_deploy_gh_release "VectorChord" "tensorchord/VectorChord" "binary" "${VCHORD_RELEASE}" "/tmp" "postgresql-16-vchord_*_$(arch_resolve).deb" + fetch_and_deploy_gh_release "VectorChord" "tensorchord/VectorChord" "binary" "${VCHORD_RELEASE}" "/tmp" "postgresql-${PG_VERSION}-vchord_*_$(arch_resolve).deb" systemctl restart postgresql $STD sudo -u postgres psql -d immich -c "ALTER EXTENSION vector UPDATE;" $STD sudo -u postgres psql -d immich -c "ALTER EXTENSION vchord UPDATE;" @@ -328,6 +330,27 @@ EOF systemctl daemon-reload fi + # MickLesk temporary patch for HEIC thumbnail gen + msg_info "Patching media.repository.js" + MEDIA_REPO_JS="/opt/immich/app/dist/repositories/media.repository.js" + if [[ -f "$MEDIA_REPO_JS" ]]; then + python3 - <<'PY' +from pathlib import Path +p = Path('/opt/immich/app/dist/repositories/media.repository.js') +s = p.read_text() +old = "(0, sharp_1.default)(input).metadata()" +new = "(0, sharp_1.default)(input, { unlimited: true, limitInputPixels: false }).metadata()" +if new in s: + print('hotfix already there') +elif old in s: + p.write_text(s.replace(old, new, 1)) + print('hotfix applied') +else: + print('pattern not found, skipped') +PY + fi + msg_ok "Patched media.repository.js" + # chown excluding upload dir contents (may be a mount with restricted permissions) chown immich:immich "$INSTALL_DIR" find "$INSTALL_DIR" -maxdepth 1 -mindepth 1 ! -name upload -exec chown -R immich:immich {} + @@ -478,6 +501,7 @@ function compile_libvips() { $STD git clone https://github.com/libvips/libvips.git "$SOURCE" cd "$SOURCE" $STD git reset --hard "$LIBVIPS_REVISION" + $STD git apply "$BASE_DIR"/server/sources/libvips-patches/0001-put-other-loaders-ahead-of-dcrawload.patch $STD meson setup build --buildtype=release --libdir=lib -Dintrospection=disabled -Dtiff=disabled cd build $STD ninja install diff --git a/ct/invidious.sh b/ct/invidious.sh new file mode 100644 index 000000000..b3b68a632 --- /dev/null +++ b/ct/invidious.sh @@ -0,0 +1,77 @@ +#!/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: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/iv-org/invidious + +APP="Invidious" +var_tags="${var_tags:-streaming}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-20}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_arm64="${var_arm64:-yes}" +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/invidious ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "Invidious" "iv-org/invidious"; then + msg_info "Stopping services" + $STD systemctl stop invidious-companion invidious + msg_ok "Stopped services" + + create_backup /opt/invidious/config/config.yml + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Invidious" "iv-org/invidious" "tarball" "latest" "/opt/invidious" + if check_for_gh_release "Invidious-Companion" "iv-org/invidious-companion"; then + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Invidious-Companion" "iv-org/invidious-companion" "prebuild" "latest" "/opt/invidious-companion" "invidious_companion-$(arch_resolve x86_64 aarch64)-unknown-linux-gnu.tar.gz" + fi + + msg_info "Rebuilding Invidious" + cd /opt/invidious + INVIDIOUS_VERSION="$(cat ~/.invidious 2>/dev/null || echo "unknown")" + INVIDIOUS_VERSION="${INVIDIOUS_VERSION#v}" + sed -i \ + -e "s~^\(\s*CURRENT_BRANCH\s*=\).*~\1 \"master\"~" \ + -e "s~^\(\s*CURRENT_COMMIT\s*=\).*~\1 \"\"~" \ + -e "s~^\(\s*CURRENT_VERSION\s*=\).*~\1 \"${INVIDIOUS_VERSION}\"~" \ + -e "s~^\(\s*CURRENT_TAG\s*=\).*~\1 \"${INVIDIOUS_VERSION}\"~" \ + -e "s~^\(\s*ASSET_COMMIT\s*=\).*~\1 \"\"~" \ + src/invidious.cr + $STD make + msg_ok "Rebuilt Invidious" + + restore_backup + + msg_info "Starting services" + $STD systemctl start invidious invidious-companion + 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 "${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/ct/leafwiki.sh b/ct/leafwiki.sh new file mode 100644 index 000000000..20633cb57 --- /dev/null +++ b/ct/leafwiki.sh @@ -0,0 +1,57 @@ +#!/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/perber/leafwiki + +APP="LeafWiki" +var_tags="${var_tags:-wiki;markdown;notes}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_arm64="${var_arm64:-yes}" +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 /usr/local/bin/leafwiki ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "leafwiki" "perber/leafwiki"; then + msg_info "Stopping Service" + systemctl stop leafwiki + msg_ok "Stopped Service" + + create_backup /opt/leafwiki/data + fetch_and_deploy_gh_release "leafwiki" "perber/leafwiki" "singlefile" "latest" "/usr/local/bin" "leafwiki-v*-linux-$(arch_resolve)" + restore_backup + + msg_info "Starting Service" + systemctl start leafwiki + 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 "${GATEWAY}${BGN}http://${IP}:8080${CL}" diff --git a/ct/localagi.sh b/ct/localagi.sh new file mode 100644 index 000000000..b2f9fbe91 --- /dev/null +++ b/ct/localagi.sh @@ -0,0 +1,67 @@ +#!/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: BillyOutlast +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/mudler/LocalAGI + +APP="LocalAGI" +var_tags="${var_tags:-ai}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-20}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_arm64="${var_arm64:-no}" +var_unprivileged="${var_unprivileged:-1}" +var_gpu="${var_gpu:-no}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/localagi ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "localagi" "mudler/LocalAGI"; then + msg_info "Stopping Service" + systemctl stop localagi + msg_ok "Stopped Service" + + create_backup /opt/localagi/.env + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "localagi" "mudler/LocalAGI" "tarball" "latest" "/opt/localagi" + restore_backup + + msg_info "Building LocalAGI" + cd /opt/localagi/webui/react-ui + $STD bun install + $STD bun run build + cd /opt/localagi + $STD go build -o /usr/local/bin/localagi + msg_ok "Updated LocalAGI successfully" + + msg_info "Starting Service" + systemctl start localagi + msg_ok "Started Service" + msg_ok "Updated successfully!" + exit + 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 "${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/ct/lychee.sh b/ct/lychee.sh index 887d1f3ac..7adfff87b 100644 --- a/ct/lychee.sh +++ b/ct/lychee.sh @@ -31,33 +31,38 @@ function update_script() { fi if check_for_gh_release "lychee" "LycheeOrg/Lychee"; then + PHP_VER=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;') + msg_info "Stopping Services" - systemctl stop caddy + systemctl stop caddy php${PHP_VER}-fpm msg_ok "Stopped Services" - msg_info "Backing up Data" - cp /opt/lychee/.env /opt/lychee.env.bak - cp -r /opt/lychee/storage /opt/lychee_storage_backup - msg_ok "Backed up Data" + create_backup /opt/lychee/.env \ + /opt/lychee/storage \ + /opt/lychee/public/uploads \ + /opt/lychee/public/dist CLEAN_INSTALL=1 fetch_and_deploy_gh_release "lychee" "LycheeOrg/Lychee" "prebuild" "latest" "/opt/lychee" "Lychee.zip" - msg_info "Restoring Data" - cp /opt/lychee.env.bak /opt/lychee/.env - rm -f /opt/lychee.env.bak - cp -r /opt/lychee_storage_backup/. /opt/lychee/storage - rm -rf /opt/lychee_storage_backup - msg_ok "Restored Data" + restore_backup msg_info "Updating Application" cd /opt/lychee $STD php artisan migrate --force + $STD php artisan config:clear + $STD php artisan cache:clear $STD php artisan optimize:clear - chmod -R 775 /opt/lychee/storage /opt/lychee/bootstrap/cache + $STD php artisan optimize + chown -R www-data:www-data /opt/lychee + chmod -R 775 /opt/lychee/storage /opt/lychee/bootstrap/cache \ + /opt/lychee/public/dist /opt/lychee/public/uploads + if [[ "${VERBOSE:-no}" = "yes" ]]; then + php artisan lychee:diagnostics || true + fi msg_ok "Updated Application" msg_info "Starting Services" - systemctl start caddy + systemctl start caddy php${PHP_VER}-fpm msg_ok "Started Services" msg_ok "Updated successfully!" fi diff --git a/ct/n8n.sh b/ct/n8n.sh index 8f9cb483b..658a19b6c 100644 --- a/ct/n8n.sh +++ b/ct/n8n.sh @@ -45,7 +45,7 @@ EOF systemctl daemon-reload fi - $STD npm install -g n8n@2.27.5 + $STD npm install -g n8n@latest systemctl restart n8n msg_ok "Updated n8n" msg_ok "Updated successfully!" diff --git a/ct/nexterm.sh b/ct/nexterm.sh new file mode 100644 index 000000000..dd67c4697 --- /dev/null +++ b/ct/nexterm.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: Mathias Wagner (gnmyt) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://nexterm.dev/ + +APP="Nexterm" +var_tags="${var_tags:-server-management}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-6}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_arm64="${var_arm64:-yes}" +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/nexterm/server/nexterm-server ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "nexterm-engine" "gnmyt/Nexterm"; then + msg_info "Stopping nexterm-engine" + systemctl stop nexterm-engine + msg_ok "Stopped nexterm-engine" + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "nexterm-engine" "gnmyt/Nexterm" "prebuild" "latest" "/opt/nexterm/engine" "nexterm-engine-linux-$(arch_resolve "x64" "arm64").tar.gz" + + msg_info "Starting nexterm-engine" + systemctl start nexterm-engine + msg_ok "Started nexterm-engine" + fi + + if check_for_gh_release "nexterm-server" "gnmyt/Nexterm"; then + msg_info "Stopping nexterm-server" + systemctl stop nexterm-server + msg_ok "Stopped nexterm-server" + + fetch_and_deploy_gh_release "nexterm-server" "gnmyt/Nexterm" "singlefile" "latest" "/opt/nexterm/server" "nexterm-server-linux-$(arch_resolve "x64" "arm64")" + + msg_info "Starting nexterm-server" + systemctl start nexterm-server + msg_ok "Started nexterm-server" + 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 "${GATEWAY}${BGN}http://${IP}:6989${CL}" diff --git a/ct/notediscovery.sh b/ct/notediscovery.sh new file mode 100644 index 000000000..3b15fd0c7 --- /dev/null +++ b/ct/notediscovery.sh @@ -0,0 +1,64 @@ +#!/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/gamosoft/NoteDiscovery + +APP="NoteDiscovery" +var_tags="${var_tags:-notes;wiki;knowledge-base}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_arm64="${var_arm64:-yes}" +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/notediscovery ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "notediscovery" "gamosoft/NoteDiscovery"; then + msg_info "Stopping Service" + systemctl stop notediscovery + msg_ok "Stopped Service" + + create_backup /opt/notediscovery/data /opt/notediscovery/config.yaml + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "notediscovery" "gamosoft/NoteDiscovery" "tarball" + + msg_info "Syncing Dependencies" + cd /opt/notediscovery + $STD uv sync --no-dev + msg_ok "Synced Dependencies" + + restore_backup + + msg_info "Starting Service" + systemctl start notediscovery + 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 "${GATEWAY}${BGN}http://${IP}:8000${CL}" diff --git a/ct/opencloud.sh b/ct/opencloud.sh index b64dc34d0..51485fec3 100644 --- a/ct/opencloud.sh +++ b/ct/opencloud.sh @@ -30,7 +30,7 @@ function update_script() { exit fi - RELEASE="v7.2.0" + RELEASE="v7.3.0" if check_for_gh_release "OpenCloud" "opencloud-eu/opencloud" "${RELEASE}" "each release is tested individually before the version is updated. Please do not open issues for this"; then msg_info "Stopping services" systemctl stop opencloud opencloud-wopi diff --git a/ct/oxicloud.sh b/ct/oxicloud.sh new file mode 100644 index 000000000..bee864391 --- /dev/null +++ b/ct/oxicloud.sh @@ -0,0 +1,85 @@ +#!/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: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/DioCrafts/OxiCloud + +APP="OxiCloud" +var_tags="${var_tags:-files;documents}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-6144}" +var_disk="${var_disk:-20}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_arm64="${var_arm64:-yes}" +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/oxicloud ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "OxiCloud" "DioCrafts/OxiCloud"; then + msg_info "Stopping OxiCloud" + systemctl stop oxicloud + msg_ok "Stopped OxiCloud" + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "OxiCloud" "DioCrafts/OxiCloud" "tarball" "latest" "/opt/oxicloud" + TOOLCHAIN="$(grep -oP 'FROM\s+rust:\K[0-9]+\.[0-9]+(\.[0-9]+)?' /opt/oxicloud/Dockerfile | head -1)" + RUST_TOOLCHAIN="${TOOLCHAIN:-stable}" setup_rust + + msg_info "Building Frontend SPA" + cd /opt/oxicloud/frontend + $STD npm ci + $STD npm run build + msg_ok "Built Frontend SPA" + + msg_info "Updating OxiCloud (Patience)" + set -a + source /etc/oxicloud/.env + set +a + cd /opt/oxicloud + export DATABASE_URL + export RUSTFLAGS="-C target-cpu=native" + RAM_MB=$(awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo) + CARGO_JOBS=$((RAM_MB / 2560)) + [[ $CARGO_JOBS -lt 1 ]] && CARGO_JOBS=1 + [[ $CARGO_JOBS -gt $(nproc) ]] && CARGO_JOBS=$(nproc) + $STD cargo build --release -j "$CARGO_JOBS" --bin oxicloud --bin migrate-nfc-filenames + mv target/release/oxicloud /usr/local/bin/oxicloud + mv target/release/migrate-nfc-filenames /usr/local/bin/migrate-nfc-filenames + chmod +x /usr/local/bin/oxicloud /usr/local/bin/migrate-nfc-filenames + rm -f /usr/bin/oxicloud + rm -rf /opt/oxicloud/static + mv /opt/oxicloud/static-dist /opt/oxicloud/static + rm -rf /opt/oxicloud/target /opt/oxicloud/frontend/node_modules + msg_ok "Updated OxiCloud" + + msg_info "Starting OxiCloud" + systemctl start oxicloud + msg_ok "Started OxiCloud" + 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 "${GATEWAY}${BGN}http://${IP}:8086${CL}" diff --git a/ct/pangolin.sh b/ct/pangolin.sh index 08d604277..fe5b787a9 100644 --- a/ct/pangolin.sh +++ b/ct/pangolin.sh @@ -6,7 +6,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV # Source: https://pangolin.net/ | Github: https://github.com/fosrl/pangolin APP="Pangolin" -PANGOLIN_VERSION="${PANGOLIN_VERSION:-1.18.4}" +PANGOLIN_VERSION="${PANGOLIN_VERSION:-1.20.0}" var_tags="${var_tags:-proxy}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" @@ -33,6 +33,15 @@ function update_script() { ensure_dependencies build-essential python3 + if ! command -v psql &>/dev/null; then + msg_error "This installation uses SQLite and cannot be upgraded to Pangolin ${PANGOLIN_VERSION}." + echo -e "${INFO}${YW}Starting with Pangolin 1.20.0, PostgreSQL is required as the database backend.${CL}" + echo -e "${INFO}${YW}An automatic migration of your existing SQLite data is not supported.${CL}" + echo -e "${INFO}${YW}Please create a new LXC with the Pangolin install script, which sets up PostgreSQL automatically.${CL}" + echo -e "${INFO}${YW}Your current data is preserved in this container and can be manually migrated if needed.${CL}" + exit 1 + fi + NODE_VERSION="24" setup_nodejs if check_for_gh_release "pangolin" "fosrl/pangolin" "$PANGOLIN_VERSION" "Pinned to a tested release because Pangolin's schema changes have repeatedly broken unattended updates. To try a newer version at your own risk, run: 'export PANGOLIN_VERSION=' and re-run update. If it breaks, please open an issue at https://github.com/community-scripts/ProxmoxVE/issues with the error log."; then @@ -41,13 +50,8 @@ function update_script() { systemctl stop gerbil msg_info "Service stopped" - msg_info "Creating backup" - tar -czf /opt/pangolin_config_backup.tar.gz -C /opt/pangolin config - if [[ -f /opt/pangolin/config/db/db.sqlite ]]; then - cp -a /opt/pangolin/config/db/db.sqlite \ - "/opt/pangolin/config/db/db.sqlite.pre-${PANGOLIN_VERSION}-$(date +%Y%m%d-%H%M%S).bak" - fi - msg_ok "Created backup" + DB_URL=$(sed -n 's/.*connection_string: "\(.*\)".*/\1/p' /opt/pangolin/config/config.yml) + create_backup /opt/pangolin/config CLEAN_INSTALL=1 fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball" "$PANGOLIN_VERSION" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_$(arch_resolve)" @@ -55,23 +59,21 @@ function update_script() { msg_info "Updating Pangolin" cd /opt/pangolin $STD npm ci - $STD npm run set:sqlite + $STD npm run set:pg $STD npm run set:oss rm -rf server/private - $STD npm run db:generate + DATABASE_URL="$DB_URL" $STD npm run db:generate $STD npm run build $STD npm run build:cli cp -R .next/standalone ./ + cp -r server/migrations ./dist/init chmod +x ./dist/cli.mjs cp server/db/names.json ./dist/names.json cp server/db/ios_models.json ./dist/ios_models.json cp server/db/mac_models.json ./dist/mac_models.json msg_ok "Updated Pangolin" - msg_info "Restoring config" - tar -xzf /opt/pangolin_config_backup.tar.gz -C /opt/pangolin --overwrite - rm -f /opt/pangolin_config_backup.tar.gz - msg_ok "Restored config" + restore_backup if ! grep -q '^ExecStartPre=/usr/bin/node dist/migrations.mjs' /etc/systemd/system/pangolin.service 2>/dev/null; then msg_info "Adding migration step to pangolin.service" @@ -82,13 +84,8 @@ function update_script() { msg_info "Running database migrations" cd /opt/pangolin - SQLITE_DB="/opt/pangolin/config/db/db.sqlite" - if [[ -f "$SQLITE_DB" ]]; then - if ! sqlite3 "$SQLITE_DB" ".tables" 2>/dev/null | tr ' ' '\n' | grep -qx "statusHistory"; then - sqlite3 "$SQLITE_DB" "DELETE FROM versionMigrations;" 2>/dev/null || true - fi - fi ENVIRONMENT=prod $STD node dist/migrations.mjs + msg_ok "Ran database migrations" msg_info "Updating Badger plugin version" @@ -112,4 +109,4 @@ description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW}Access it using the following URL:${CL}" -echo -e "${GATEWAY}${BGN}https://${CL}" +echo -e "${GATEWAY}${BGN}https:// or http://${IP}:3002${CL}" diff --git a/ct/pocketid.sh b/ct/pocketid.sh index 6153b40c0..9ffc54426 100755 --- a/ct/pocketid.sh +++ b/ct/pocketid.sh @@ -71,7 +71,7 @@ function update_script() { cp /opt/pocket-id/.env /opt/env fi - fetch_and_deploy_gh_release "pocket-id" "pocket-id/pocket-id" "singlefile" "latest" "/opt/pocket-id/" "pocket-id-linux-$(arch_resolve)" + fetch_and_deploy_gh_release "pocket-id" "pocket-id/pocket-id" "singlefile" "latest" "/opt/pocket-id/" "pocket-id_linux_$(arch_resolve)" mv /opt/env /opt/pocket-id/.env msg_info "Starting Service" diff --git a/ct/reitti.sh b/ct/reitti.sh index f160176d4..5980e6ae7 100644 --- a/ct/reitti.sh +++ b/ct/reitti.sh @@ -106,13 +106,6 @@ spring.servlet.multipart.max-file-size=5GB spring.servlet.multipart.max-request-size=5GB server.tomcat.max-part-count=100 -# Rqueue configuration -rqueue.web.enable=false -rqueue.job.enabled=false -rqueue.message.durability.in-terminal-state=0 -rqueue.key.prefix=\${spring.cache.redis.key-prefix} -rqueue.message.converter.provider.class=com.dedicatedcode.reitti.config.RQueueCustomMessageConverter - # Application-specific settings reitti.server.advertise-uri= @@ -168,6 +161,81 @@ PROPEOF msg_ok "Rewrote application.properties (backup: application.properties.bak)" fi + # Migrate v4 -> v5: Remove Rqueue configuration (replaced by Quartz Scheduler) + if grep -q "^rqueue\." /opt/reitti/application.properties 2>/dev/null; then + msg_info "Migrating to v5: Removing Rqueue configuration" + sed -i '/^# Rqueue configuration$/d; /^rqueue\./d' /opt/reitti/application.properties + msg_ok "Removed Rqueue configuration" + fi + + # Migrate v4 -> v5: Update application.properties and nginx tile cache for v5 compatibility + if grep -q "^reitti\.process-data\.schedule=" /opt/reitti/application.properties 2>/dev/null; then + msg_info "Migrating to v5: Updating application.properties" + sed -i '/^reitti\.process-data\.schedule=/d' /opt/reitti/application.properties + sed -i 's/^reitti\.import\.processing-idle-start-time=.*/reitti.import.grace-time-seconds=30/' /opt/reitti/application.properties + sed -i 's/^spring\.datasource\.hikari\.maximum-pool-size=20$/spring.datasource.hikari.maximum-pool-size=30/' /opt/reitti/application.properties + grep -q "devices" /opt/reitti/application.properties || \ + sed -i 's/^spring\.cache\.cache-names=\(.*\)$/spring.cache.cache-names=\1,devices,mapStyles,mapStyleJson/' /opt/reitti/application.properties + grep -q "org.quartz.core.ErrorLogger" /opt/reitti/application.properties || \ + sed -i '/^logging\.level\.com\.dedicatedcode\.reitti=/a logging.level.org.quartz.core.ErrorLogger=FATAL' /opt/reitti/application.properties + grep -q "^spring.servlet.multipart.resolve-lazily=" /opt/reitti/application.properties || \ + sed -i '/^spring\.servlet\.multipart\.max-request-size=/a spring.servlet.multipart.resolve-lazily=true' /opt/reitti/application.properties + grep -q "^spring.mvc.async.request-timeout=" /opt/reitti/application.properties || \ + echo "spring.mvc.async.request-timeout=600000" >>/opt/reitti/application.properties + if ! grep -q "^spring.quartz" /opt/reitti/application.properties; then + cat >>/opt/reitti/application.properties <<'QUARTZEOF' + +# Quartz Scheduler configuration +spring.quartz.job-store-type=jdbc +spring.quartz.jdbc.initialize-schema=never +spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate +spring.quartz.properties.org.quartz.jobStore.isClustered=false +spring.quartz.properties.org.quartz.jobStore.tablePrefix=qrtz_ +spring.quartz.properties.org.quartz.threadPool.threadCount=5 +QUARTZEOF + fi + grep -q "^reitti.import.staging.cleanup.cron=" /opt/reitti/application.properties || \ + echo "reitti.import.staging.cleanup.cron=0 0 4 * * *" >>/opt/reitti/application.properties + grep -q "^reitti.batching.max-batch-size=" /opt/reitti/application.properties || \ + printf "reitti.batching.max-batch-size=100\nreitti.batching.max-wait-time=5\n" >>/opt/reitti/application.properties + grep -q "^reitti.jobs.cleanup.cron=" /opt/reitti/application.properties || \ + printf "reitti.jobs.cleanup.cron=0 0 4 * * ?\nreitti.jobs.cleanup.max-age-hours=24\n" >>/opt/reitti/application.properties + grep -q "^reitti.db-janitor.schedule=" /opt/reitti/application.properties || \ + echo "reitti.db-janitor.schedule=0 0 4 * * ?" >>/opt/reitti/application.properties + msg_ok "Updated application.properties for v5" + + if [[ -f /etc/nginx/nginx.conf ]]; then + msg_info "Migrating to v5: Updating nginx tile cache configuration" + cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak.v5 + cat >/etc/nginx/nginx.conf <<'NGINXEOF' +user www-data; + +events { + worker_connections 1024; +} +http { + resolver 1.1.1.1 8.8.8.8 valid=30s ipv6=off; + proxy_cache_path /var/cache/nginx/tiles levels=1:2 keys_zone=tiles:10m max_size=1g inactive=30d use_temp_path=off; + server { + listen 80; + location /custom/ { + set $upstream_url $http_x_reitti_upstream_url; + proxy_pass $upstream_url; + proxy_set_header Host $proxy_host; + proxy_set_header User-Agent "Reitti/1.0"; + proxy_cache tiles; + proxy_cache_key $upstream_url; + proxy_cache_valid 200 30d; + proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; + } + } +} +NGINXEOF + systemctl reload nginx + msg_ok "Updated nginx tile cache configuration" + fi + fi + if check_for_gh_release "reitti" "dedicatedcode/reitti"; then msg_info "Stopping Service" systemctl stop reitti @@ -179,10 +247,12 @@ PROPEOF USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "reitti" "dedicatedcode/reitti" "singlefile" "latest" "/opt/reitti" "reitti-app.jar" mv /opt/reitti/reitti-*.jar /opt/reitti/reitti.jar + msg_warn "v5 runs a one-time database migration on first start (GPS points → device table). This may take several minutes on large datasets — do not interrupt the container." msg_info "Starting Service" systemctl start reitti msg_ok "Started Service" msg_ok "Updated successfully!" + msg_warn "Post-upgrade: Verify each API token has a Device assigned in Settings → API Tokens. Tokens without a device cannot ingest location data in v5." fi exit } diff --git a/ct/sftpgo.sh b/ct/sftpgo.sh index d96f013d2..5c6a79adf 100644 --- a/ct/sftpgo.sh +++ b/ct/sftpgo.sh @@ -28,6 +28,12 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi + + setup_deb822_repo \ + "sftpgo" \ + "https://oss.sftpgo.com/apt/gpg.key" \ + "https://oss.sftpgo.com/apt" \ + "trixie" msg_info "Updating SFTPGo" $STD apt update diff --git a/ct/snapotter.sh b/ct/snapotter.sh index 2ebea2959..ce2323f8f 100644 --- a/ct/snapotter.sh +++ b/ct/snapotter.sh @@ -30,19 +30,63 @@ function update_script() { exit fi - if check_for_gh_release "snapotter" "snapotter-hq/SnapOtter"; then + NEEDS_V2_MIGRATION=false + grep -q '^DB_PATH=' /opt/snapotter_data/.env 2>/dev/null && NEEDS_V2_MIGRATION=true + UPDATE_AVAILABLE=false + check_for_gh_release "snapotter" "snapotter-hq/SnapOtter" && UPDATE_AVAILABLE=true + + if [[ "$NEEDS_V2_MIGRATION" == true || "$UPDATE_AVAILABLE" == true ]]; then msg_info "Stopping Service" systemctl stop snapotter msg_ok "Stopped Service" - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "snapotter" "snapotter-hq/SnapOtter" "tarball" + PG_VERSION="17" setup_postgresql + if ! sudo -u postgres psql -tAc "SELECT 1 FROM pg_database WHERE datname = 'snapotter'" | grep -qx '1'; then + PG_DB_NAME="snapotter" PG_DB_USER="snapotter" setup_postgresql_db + else + PG_DB_NAME="snapotter" + PG_DB_USER="snapotter" + PG_DB_PASS=$(sed -n 's|^DATABASE_URL=postgres://snapotter:\([^@]*\)@.*|\1|p' /opt/snapotter_data/.env | head -n1) + if [[ -z "$PG_DB_PASS" ]]; then + msg_error "SnapOtter's PostgreSQL database exists, but its password is not available in /opt/snapotter_data/.env" + exit 1 + fi + fi + + msg_info "Installing Redis" + $STD apt install -y redis-server + if grep -q '^appendonly ' /etc/redis/redis.conf; then + sed -i 's/^appendonly .*/appendonly yes/' /etc/redis/redis.conf + else + echo 'appendonly yes' >>/etc/redis/redis.conf + fi + $STD systemctl enable --now redis-server + msg_ok "Installed Redis" + + msg_info "Migrating SnapOtter Configuration" + sed -i '/^DB_PATH=/d; /^DATABASE_URL=/d; /^REDIS_URL=/d; /^SQLITE_MIGRATE_PATH=/d' /opt/snapotter_data/.env + cat <>/opt/snapotter_data/.env +DATABASE_URL=postgres://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME} +REDIS_URL=redis://127.0.0.1:6379 +EOF + if [[ -f /opt/snapotter_data/snapotter.db ]]; then + echo 'SQLITE_MIGRATE_PATH=/opt/snapotter_data/snapotter.db' >>/opt/snapotter_data/.env + fi + if ! grep -q '^Requires=postgresql.service redis-server.service$' /etc/systemd/system/snapotter.service; then + sed -i '/^After=/c\After=network-online.target postgresql.service redis-server.service' /etc/systemd/system/snapotter.service + sed -i '/^\[Unit\]/a Wants=network-online.target\nRequires=postgresql.service redis-server.service' /etc/systemd/system/snapotter.service + fi + systemctl daemon-reload + msg_ok "Migrated SnapOtter Configuration" + + if [[ "$UPDATE_AVAILABLE" == true ]]; then + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "snapotter" "snapotter-hq/SnapOtter" "prebuild" "latest" "/opt/snapotter" "snapotter-*-linux-amd64.tar.gz" + fi msg_info "Updating SnapOtter" - cd /opt/snapotter - $STD npm pkg delete scripts.prepare - $STD pnpm install --frozen-lockfile - $STD pnpm --filter @snapotter/web build - sed -i 's/mediapipe==0.10.21/mediapipe>=0.10.21/' /opt/snapotter/docker/feature-manifest.json + $STD uv python install 3.11 + $STD uv venv --seed --python 3.11 /opt/snapotter_data/ai/venv + ln -sfn /opt/snapotter /app msg_ok "Updated SnapOtter" msg_info "Starting Service" diff --git a/ct/squid.sh b/ct/squid.sh new file mode 100644 index 000000000..1698fc395 --- /dev/null +++ b/ct/squid.sh @@ -0,0 +1,53 @@ +#!/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: 007hacky007 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.squid-cache.org/ + +APP="Squid" +var_tags="${var_tags:-proxy}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_arm64="${var_arm64:-yes}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -f /etc/squid/squid.conf ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating Squid" + $STD apt update + $STD apt upgrade -y + msg_ok "Updated Squid" + + msg_info "Validating Squid Configuration" + $STD squid -k parse + msg_ok "Validated Squid Configuration" + + msg_info "Restarting Squid" + systemctl restart squid + msg_ok "Restarted Squid" + 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} Proxy endpoint:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}${IP}:3128${CL}" diff --git a/ct/storyteller.sh b/ct/storyteller.sh index df2f915c9..0e49e0196 100644 --- a/ct/storyteller.sh +++ b/ct/storyteller.sh @@ -32,7 +32,7 @@ function update_script() { NODE_VERSION="24" NODE_MODULE="corepack,yarn" setup_nodejs - if check_for_gl_release "storyteller" "storyteller-platform/storyteller"; then + if check_for_gl_release "storyteller" "storyteller-platform/storyteller" "" "" "web-v2"; then msg_info "Stopping Service" systemctl stop storyteller msg_ok "Stopped Service" @@ -41,7 +41,7 @@ function update_script() { cp /opt/storyteller/.env /opt/storyteller_env.bak msg_ok "Backed up Data" - CLEAN_INSTALL=1 fetch_and_deploy_gl_release "storyteller" "storyteller-platform/storyteller" "tarball" "latest" "/opt/storyteller" + CLEAN_INSTALL=1 fetch_and_deploy_gl_release "storyteller" "storyteller-platform/storyteller" "tarball" "latest" "/opt/storyteller" "" "web-v2" msg_info "Restoring Configuration" mv /opt/storyteller_env.bak /opt/storyteller/.env diff --git a/ct/sync-in.sh b/ct/sync-in.sh new file mode 100644 index 000000000..4b43c6dd4 --- /dev/null +++ b/ct/sync-in.sh @@ -0,0 +1,65 @@ +#!/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/Sync-in/server + +APP="Sync-in" +var_tags="${var_tags:-files;sync;collaboration}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-20}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_arm64="${var_arm64:-yes}" +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/sync-in/node_modules/@sync-in ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "sync-in" "Sync-in/server"; then + msg_info "Stopping Service" + systemctl stop sync-in + msg_ok "Stopped Service" + + msg_info "Updating Sync-in" + $STD npm install --prefix /opt/sync-in "@sync-in/server@${CHECK_UPDATE_RELEASE#v}" + msg_ok "Updated Sync-in" + + msg_info "Running Database Migrations" + cd /opt/sync-in + $STD npx sync-in-server migrate-db + msg_ok "Ran Database Migrations" + + VERSION=$(node -pe "require('/opt/sync-in/node_modules/@sync-in/server/package.json').version" 2>/dev/null || echo "") + [[ -n "$VERSION" ]] && echo "$VERSION" >~/.sync-in + + msg_info "Starting Service" + systemctl start sync-in + 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 "${GATEWAY}${BGN}http://${IP}:8080${CL}" diff --git a/ct/wanderer.sh b/ct/wanderer.sh index 432963379..bb0ec71cd 100644 --- a/ct/wanderer.sh +++ b/ct/wanderer.sh @@ -30,12 +30,14 @@ function update_script() { exit fi - if check_for_gh_release "wanderer" "Flomp/wanderer"; then + if check_for_gh_release "wanderer" "open-wanderer/wanderer"; then msg_info "Stopping service" systemctl stop wanderer-web msg_ok "Stopped service" - fetch_and_deploy_gh_release "wanderer" "open-wanderer/wanderer" "tarball" "latest" "/opt/wanderer/source" + create_backup /opt/wanderer/source/search + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wanderer" "open-wanderer/wanderer" "tarball" "latest" "/opt/wanderer/source" + restore_backup msg_info "Updating wanderer" cd /opt/wanderer/source/db @@ -44,6 +46,13 @@ function update_script() { cd /opt/wanderer/source/web $STD npm ci $STD npm run build + mkdir -p /opt/wanderer/data/plugins + [[ -e /data/plugins ]] || ln -sfn /opt/wanderer/data/plugins /data/plugins + msg_info "Installing wanderer plugins" + for plugin in hammerhead komoot strava; do + fetch_and_deploy_gh_release "wanderer-plugin-${plugin}" "open-wanderer/wanderer" "prebuild" "${CHECK_UPDATE_RELEASE:-latest}" "/opt/wanderer/data/plugins" "wanderer-plugin-${plugin}.tar.gz" || msg_warn "Failed to install wanderer plugin: ${plugin}" + done + msg_ok "Installed wanderer plugins" msg_ok "Updated wanderer" msg_info "Starting service" diff --git a/ct/yuvomi.sh b/ct/yuvomi.sh new file mode 100644 index 000000000..625a0aa61 --- /dev/null +++ b/ct/yuvomi.sh @@ -0,0 +1,64 @@ +#!/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/ulsklyc/yuvomi + +APP="Yuvomi" +var_tags="${var_tags:-family;planner;calendar}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_arm64="${var_arm64:-yes}" +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/yuvomi ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "yuvomi" "ulsklyc/yuvomi"; then + msg_info "Stopping Service" + systemctl stop yuvomi + msg_ok "Stopped Service" + + create_backup /opt/yuvomi/data /opt/yuvomi/.env + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "yuvomi" "ulsklyc/yuvomi" "tarball" + + msg_info "Installing Node.js Dependencies" + cd /opt/yuvomi + $STD npm ci --omit=dev + msg_ok "Installed Node.js Dependencies" + + restore_backup + + msg_info "Starting Service" + systemctl start yuvomi + 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 "${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/install/2fauth-install.sh b/install/2fauth-install.sh index 386b5c3de..61c8377ae 100644 --- a/install/2fauth-install.sh +++ b/install/2fauth-install.sh @@ -43,6 +43,7 @@ $STD php artisan migrate:refresh $STD php artisan passport:install -q -n $STD php artisan storage:link $STD php artisan config:cache +$STD php artisan 2fauth:fix-passport-key-permissions chown -R www-data: /opt/2fauth chmod -R 755 /opt/2fauth msg_ok "Setup 2fauth" diff --git a/install/adventurelog-install.sh b/install/adventurelog-install.sh index 10e664bc6..b480da0d9 100644 --- a/install/adventurelog-install.sh +++ b/install/adventurelog-install.sh @@ -72,6 +72,7 @@ BODY_SIZE_LIMIT=Infinity ORIGIN='http://$LOCAL_IP:3000' EOF cd /opt/adventurelog/frontend +grep -q "^dangerouslyAllowAllBuilds:" ./pnpm-workspace.yaml 2>/dev/null || echo "dangerouslyAllowAllBuilds: true" >>./pnpm-workspace.yaml $STD pnpm i $STD pnpm build msg_ok "Installed AdventureLog" diff --git a/install/affine-install.sh b/install/affine-install.sh new file mode 100644 index 000000000..50144d4e0 --- /dev/null +++ b/install/affine-install.sh @@ -0,0 +1,217 @@ +#!/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/toeverything/AFFiNE + +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 \ + git \ + pkg-config \ + openssl \ + libssl-dev \ + libjemalloc2 \ + redis-server \ + nginx \ + cmake +msg_ok "Installed Dependencies" + +PG_VERSION="16" PG_MODULES="pgvector" setup_postgresql +PG_DB_NAME="affine" PG_DB_USER="affine" setup_postgresql_db +NODE_VERSION="22" setup_nodejs +setup_rust + +fetch_and_deploy_gh_release "affine_app" "toeverything/AFFiNE" "tarball" "v0.27.0" "/opt/affine" + +msg_info "Setting up Directories" +rm -rf /root/.affine +mkdir -p /root/.affine/{storage,config} +msg_ok "Set up Directories" + +msg_info "Configuring Environment" +SECRET_KEY=$(openssl rand -hex 32) +cat </opt/affine/.env +NODE_ENV=production +AFFINE_SERVER_PORT=3010 +AFFINE_SERVER_HOST=${LOCAL_IP} +AFFINE_SERVER_EXTERNAL_URL=http://${LOCAL_IP} +DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME} +REDIS_SERVER_HOST=localhost +REDIS_SERVER_PORT=6379 +AFFINE_INDEXER_ENABLED=false +SECRET_KEY=${SECRET_KEY} +EOF +msg_ok "Configured Environment" + +msg_info "Building AFFiNE (Patience)" +cd /opt/affine +source /root/.profile +export PATH="/root/.cargo/bin:$PATH" +export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 +export VITE_CORE_COMMIT_SHA=$(cat ~/.affine_app) +# # Initialize git repo (required for build process) +export HUSKY=0 +$STD git init -q +$STD git config user.email "build@local" +$STD git config user.name "Build" +$STD git add -A +$STD git commit -q -m "update" --no-verify --allow-empty +mkdir -p /opt/affine/.turbo +cat </opt/affine/.turbo/config.json +{ + "concurrency": 1 +} +TURBO +$STD corepack enable +$STD corepack prepare yarn@4.13.0 --activate +$STD yarn config set enableTelemetry 0 +export NODE_OPTIONS="--max-old-space-size=4096" +export TSC_COMPILE_ON_ERROR=true +$STD yarn install +$STD npm install -g typescript +$STD yarn affine @affine/native build +$STD yarn affine @affine/server-native build + +# Create architecture-specific symlinks for server-native +ln -sf /opt/affine/packages/backend/native/server-native.node \ + /opt/affine/packages/backend/native/server-native.x64.node +ln -sf /opt/affine/packages/backend/native/server-native.node \ + /opt/affine/packages/backend/native/server-native.arm64.node +ln -sf /opt/affine/packages/backend/native/server-native.node \ + /opt/affine/packages/backend/native/server-native.armv7.node + +$STD yarn affine init +$STD yarn affine build -p @affine/reader +$STD yarn affine build -p @affine/server +export NODE_OPTIONS="--max-old-space-size=4096" +$STD yarn affine build -p @affine/web +$STD yarn affine build -p @affine/admin +mkdir -p /opt/affine/packages/backend/server/static +cp -r /opt/affine/packages/frontend/apps/web/dist/* /opt/affine/packages/backend/server/static/ +mkdir -p /opt/affine/packages/backend/server/static/admin +cp -r /opt/affine/packages/frontend/admin/dist/* /opt/affine/packages/backend/server/static/admin/ +# Create empty mobile manifest (server expects it but we don't build mobile) +mkdir -p /opt/affine/packages/backend/server/static/mobile +cat <<'MANIFEST' >/opt/affine/packages/backend/server/static/mobile/assets-manifest.json +{"publicPath":"/","js":[],"css":[],"gitHash":"","description":""} +MANIFEST +msg_ok "Built AFFiNE" + +msg_info "Running Initial Migration" +cd /opt/affine/packages/backend/server +set -a && source /opt/affine/.env && set +a +$STD node ./scripts/self-host-predeploy.js +msg_ok "Ran Initial Migration" + +msg_info "Creating Services" +cat </etc/systemd/system/affine-web.service +[Unit] +Description=AFFiNE Web Server +After=network.target postgresql.service redis-server.service +Requires=postgresql.service redis-server.service + +[Service] +Type=simple +WorkingDirectory=/opt/affine/packages/backend/server +EnvironmentFile=/opt/affine/.env +Environment=LD_PRELOAD=libjemalloc.so.2 +Environment=NODE_OPTIONS=--max-old-space-size=1024 +ExecStart=/usr/bin/node ./dist/main.js +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +EOF + +cat </etc/systemd/system/affine-worker.service +[Unit] +Description=AFFiNE Background Worker +After=network.target postgresql.service redis-server.service +Requires=postgresql.service redis-server.service + +[Service] +Type=simple +WorkingDirectory=/opt/affine/packages/backend/server +EnvironmentFile=/opt/affine/.env +Environment=LD_PRELOAD=libjemalloc.so.2 +Environment=NODE_OPTIONS=--max-old-space-size=1024 +ExecStart=/usr/bin/node ./dist/main.js --worker +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +EOF + +systemctl enable -q --now redis-server affine-web affine-worker +msg_ok "Created Services" + +msg_info "Creating Admin User" +ADMIN_PASS=$(openssl rand -base64 12) +for i in {1..30}; do + if curl -s http://localhost:3010/info >/dev/null 2>&1; then + break + fi + sleep 2 +done +# Create admin via API +ADMIN_RESPONSE=$(curl -s -X POST http://localhost:3010/api/setup/create-admin-user \ + -H "Content-Type: application/json" \ + -d "{\"email\":\"admin@affine.local\",\"password\":\"${ADMIN_PASS}\"}") +if echo "$ADMIN_RESPONSE" | grep -q '"id"'; then + { + echo "AFFiNE Credentials" + echo "==================" + echo "Email: admin@affine.local" + echo "Password: ${ADMIN_PASS}" + } >~/affine.creds + msg_ok "Created Admin User" +else + msg_warn "Admin creation skipped (may already exist)" +fi + +msg_info "Configuring Nginx" +cat </etc/nginx/sites-available/affine.conf +upstream affine_backend { + server 127.0.0.1:3010; +} + +server { + listen 80; + server_name _; + + client_max_body_size 100M; + + location / { + proxy_pass http://affine_backend; + 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"; + proxy_redirect off; + proxy_buffering off; + } +} +EOF +ln -sf /etc/nginx/sites-available/affine.conf /etc/nginx/sites-enabled/ +rm -f /etc/nginx/sites-enabled/default +systemctl enable -q --now nginx +msg_ok "Configured Nginx" + +motd_ssh +customize +cleanup_lxc diff --git a/install/beaverhabits-install.sh b/install/beaverhabits-install.sh new file mode 100644 index 000000000..f58f4003a --- /dev/null +++ b/install/beaverhabits-install.sh @@ -0,0 +1,53 @@ +#!/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/daya0576/beaverhabits + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +PYTHON_VERSION="3.14" setup_uv + +fetch_and_deploy_gh_release "beaverhabits" "daya0576/beaverhabits" "tarball" + +msg_info "Installing Dependencies" +cd /opt/beaverhabits +$STD uv sync --no-dev +msg_ok "Installed Dependencies" + +msg_info "Configuring BeaverHabits" +mkdir -p /opt/beaverhabits/.user +msg_ok "Configured BeaverHabits" + +msg_info "Creating Service" +cat </etc/systemd/system/beaverhabits.service +[Unit] +Description=BeaverHabits Habit Tracker +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/beaverhabits +Environment=HABITS_STORAGE=USER_DISK +Environment=NICEGUI_STORAGE_PATH=/opt/beaverhabits/.user/.nicegui +ExecStart=/opt/beaverhabits/.venv/bin/gunicorn beaverhabits.main:app --bind 0.0.0.0:8080 -w 1 -k uvicorn_worker.UvicornWorker --max-requests 10000 --log-level info +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now beaverhabits +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc diff --git a/install/birdnet-go-install.sh b/install/birdnet-go-install.sh index b3dfcab8e..df0a96058 100644 --- a/install/birdnet-go-install.sh +++ b/install/birdnet-go-install.sh @@ -21,7 +21,7 @@ $STD apt install -y \ ffmpeg msg_ok "Installed Dependencies" -fetch_and_deploy_gh_release "birdnet" "tphakala/birdnet-go" "prebuild" "latest" "/opt/birdnet" "birdnet-go-linux-$(arch_resolve).tar.gz" +fetch_and_deploy_gh_release "birdnet" "tphakala/birdnet-go" "prebuild" "latest" "/opt/birdnet" "birdnet-go-linux-$(arch_resolve)*.tar.gz" msg_info "Setting up BirdNET-Go" cp /opt/birdnet/birdnet-go /usr/local/bin/birdnet-go diff --git a/install/docuseal-install.sh b/install/docuseal-install.sh index 82c89bfc6..e502256e3 100644 --- a/install/docuseal-install.sh +++ b/install/docuseal-install.sh @@ -23,6 +23,8 @@ $STD apt install -y \ libreadline-dev \ zlib1g-dev \ libffi-dev \ + libleptonica-dev \ + libleptonica6 \ libvips42 \ libvips-dev \ libheif1 \ diff --git a/install/esphome-install.sh b/install/esphome-install.sh index 1b5b7367d..02c46b155 100644 --- a/install/esphome-install.sh +++ b/install/esphome-install.sh @@ -14,7 +14,8 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt install -y git +$STD apt install -y git \ + libusb-1.0-0 msg_ok "Installed Dependencies" PYTHON_VERSION="3.12" setup_uv diff --git a/install/fileflows-install.sh b/install/fileflows-install.sh index af93424b6..14efb6582 100644 --- a/install/fileflows-install.sh +++ b/install/fileflows-install.sh @@ -26,8 +26,8 @@ msg_info "Installing ASP.NET Core Runtime" if [[ "$(arch_resolve)" == "arm64" ]]; then # packages.microsoft.com only ships amd64 debs for Debian; use dotnet-install on arm64 curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh - $STD bash /tmp/dotnet-install.sh --channel 8.0 --runtime aspnetcore --install-dir /usr/lib/dotnet8 - ln -sf /usr/lib/dotnet8/dotnet /usr/bin/dotnet + $STD bash /tmp/dotnet-install.sh --channel 10.0 --runtime aspnetcore --install-dir /usr/lib/dotnet10 + ln -sf /usr/lib/dotnet10/dotnet /usr/bin/dotnet rm -f /tmp/dotnet-install.sh else setup_deb822_repo \ @@ -35,7 +35,7 @@ else "https://packages.microsoft.com/keys/microsoft-2025.asc" \ "https://packages.microsoft.com/debian/13/prod/" \ "trixie" - $STD apt install -y aspnetcore-runtime-8.0 + $STD apt install -y aspnetcore-runtime-10.0 fi msg_ok "Installed ASP.NET Core Runtime" @@ -55,9 +55,12 @@ if [[ "$install_server" =~ ^[Ss]$ ]]; then msg_ok "Installed FileFlows Server" else msg_info "Installing FileFlows Node" + read -r -p "${TAB3}Enter FileFlows Server URL (e.g. http://192.168.1.10:19200): " server_url + while [[ -z "${server_url// /}" ]]; do + read -r -p "${TAB3}Enter FileFlows Server URL (e.g. http://192.168.1.10:19200): " server_url + done cd /opt/fileflows/Node - $STD dotnet FileFlows.Node.dll - $STD dotnet FileFlows.Node.dll --systemd install --root true + $STD dotnet FileFlows.Node.dll --server "$server_url" --systemd install --root true systemctl enable -q --now fileflows-node msg_ok "Installed FileFlows Node" fi diff --git a/install/fireshare-install.sh b/install/fireshare-install.sh index 982679c6e..311ba230b 100644 --- a/install/fireshare-install.sh +++ b/install/fireshare-install.sh @@ -141,6 +141,9 @@ cp /opt/fireshare/app/nginx/prod.conf /etc/nginx/nginx.conf sed -i 's|root /processed/|root /opt/fireshare-processed/|g' /etc/nginx/nginx.conf sed -i 's/^user[[:space:]]\+nginx;/user root;/' /etc/nginx/nginx.conf sed -i 's|root[[:space:]]\+/app/build;|root /opt/fireshare/app/client/build;|' /etc/nginx/nginx.conf +sed -i 's/__FIRESHARE_PORT__/80/g' /etc/nginx/nginx.conf +cp /opt/fireshare/app/nginx/error.html /etc/nginx/ +cp /opt/fireshare/app/nginx/api_unavailable.html /etc/nginx/ systemctl start nginx cat <~/fireshare.creds diff --git a/install/grav-install.sh b/install/grav-install.sh new file mode 100644 index 000000000..8b3c396cf --- /dev/null +++ b/install/grav-install.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: Raffaele (rafspiny) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://getgrav.org/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y \ + nginx \ + logrotate +msg_ok "Installed Dependencies" + +PHP_FPM="YES" setup_php + +fetch_and_deploy_gh_release "grav" "getgrav/grav" "prebuild" "latest" "/opt/grav" "grav-admin-v*zip" +chown -R www-data:www-data /opt/grav + +msg_info "Configuring Nginx" +PHP_VER=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;') +PHP_FPM_SOCK=$(find /run/php -maxdepth 1 -name "php*-fpm.sock" -type s | sort -V | tail -1) +unlink /etc/nginx/sites-enabled/default +rm -f /etc/nginx/sites-available/default +cat </etc/nginx/sites-available/grav +server { + listen 80; + server_name _; + root /opt/grav; + index index.html index.htm index.php; + + location / { + try_files \$uri \$uri/ /index.html /index.htm /index.php\$is_args\$args; + } + + ## Begin - Security + location ~* /(\.git|cache|bin|logs|backup|tests)/.*$ { return 403; } + location ~* /(system|vendor)/.*\.(txt|xml|md|html|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ { return 403; } + location ~* /user/.*\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ { return 403; } + location ~ /(LICENSE\.txt|composer\.lock|composer\.json|nginx\.conf|web\.config|htaccess\.txt|\.htaccess) { return 403; } + ## End - Security + + ## Begin - API + location ^~ /api/ { + try_files \$uri \$uri/ /index.php\$is_args\$args; + } + ## End - API + + # deny all direct access to these sensitive user folders, whatever the file type + location ~* /user/(accounts|config|env)/.*$ { return 403; } + # allow public media uploads under user/data to be served directly; + # this must come before the user/data deny so it wins the match + location ~* /user/data/.*\.(jpe?g|png|gif|webp|avif|bmp|ico|mp4|webm|ogg|ogv|mov|mp3|wav|m4a|flac|pdf)$ { try_files \$uri =404; } + # deny everything else under user/data + location ~* /user/data/.*$ { return 403; } + + + ## Begin - Caching + location ~* ^/forms-basic-captcha-image.jpg$ { + try_files \$uri \$uri/ /index.php\$is_args\$args; + } + + location ~* \.(?:ico|css|js|gif|jpe?g|png)$ { + expires 30d; + add_header Vary Accept-Encoding; + log_not_found off; + } + + location ~* ^.+\.(?:css|cur|js|jpe?g|gif|htc|ico|png|html|xml|otf|ttf|eot|woff|woff2|svg)$ { + access_log off; + expires 30d; + add_header Cache-Control public; + tcp_nodelay off; + open_file_cache max=3000 inactive=120s; + open_file_cache_valid 45s; + open_file_cache_min_uses 2; + open_file_cache_errors off; + } + ## End - Caching + + location ~ ^(.+\.php)(.*)$ { + fastcgi_split_path_info ^(.+\.php)(.*)$; + if (!-f \$document_root\$fastcgi_script_name) { return 404; } + fastcgi_pass unix:${PHP_FPM_SOCK}; + fastcgi_index index.php; + include /etc/nginx/fastcgi_params; + fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name; + } + + location ~ /\.ht { + deny all; + } +} +EOF +ln -sf /etc/nginx/sites-available/grav /etc/nginx/sites-enabled/grav +systemctl enable -q --now php${PHP_VER}-fpm +$STD nginx -t +systemctl enable -q --now nginx +$STD nginx -s reload +msg_ok "Configured Nginx" + +motd_ssh +customize +cleanup_lxc diff --git a/install/hyperion-install.sh b/install/hyperion-install.sh index db5bf301f..b58798f96 100644 --- a/install/hyperion-install.sh +++ b/install/hyperion-install.sh @@ -24,6 +24,12 @@ msg_ok "Set up Hyperion repository" msg_info "Installing Hyperion" $STD apt install -y hyperion +mkdir -p /etc/systemd/system/hyperion@.service.d +cat </etc/systemd/system/hyperion@.service.d/override.conf +[Unit] +Requisite= +EOF +systemctl daemon-reload systemctl enable -q --now hyperion@root msg_ok "Installed Hyperion" diff --git a/install/immich-install.sh b/install/immich-install.sh index fcaad3e0b..688ae657e 100644 --- a/install/immich-install.sh +++ b/install/immich-install.sh @@ -162,7 +162,7 @@ PG_VERSION="16" PG_MODULES="pgvector" setup_postgresql ACTUAL_PG_VERSION=$(ls /etc/postgresql/ 2>/dev/null | sort -V | tail -1) ACTUAL_PG_VERSION=${ACTUAL_PG_VERSION:-16} -VCHORD_RELEASE="1.0.0" +VCHORD_RELEASE="1.1.1" fetch_and_deploy_gh_release "VectorChord" "tensorchord/VectorChord" "binary" "${VCHORD_RELEASE}" "/tmp" "postgresql-${ACTUAL_PG_VERSION}-vchord_*_$(arch_resolve).deb" sed -i "s/^#shared_preload.*/shared_preload_libraries = 'vchord.so'/" /etc/postgresql/${ACTUAL_PG_VERSION}/main/postgresql.conf @@ -286,6 +286,7 @@ LIBVIPS_REVISION="e01a4797cabe77d457fdfa7d776b7a7e7ca6d6a7" $STD git clone https://github.com/libvips/libvips.git "$SOURCE" cd "$SOURCE" $STD git reset --hard "$LIBVIPS_REVISION" +$STD git apply "$BASE_DIR"/server/sources/libvips-patches/0001-put-other-loaders-ahead-of-dcrawload.patch $STD meson setup build --buildtype=release --libdir=lib -Dintrospection=disabled -Dtiff=disabled cd build $STD ninja install @@ -311,7 +312,7 @@ ML_DIR="${APP_DIR}/machine-learning" GEO_DIR="${INSTALL_DIR}/geodata" mkdir -p {"${APP_DIR}","${UPLOAD_DIR}","${GEO_DIR}","${INSTALL_DIR}"/cache} -fetch_and_deploy_gh_release "Immich" "immich-app/immich" "tarball" "v3.0.1" "$SRC_DIR" +fetch_and_deploy_gh_release "Immich" "immich-app/immich" "tarball" "v3.0.3" "$SRC_DIR" PNPM_VERSION="$(jq -r '.packageManager | split("@")[1] | split("+")[0]' ${SRC_DIR}/package.json)" export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 NODE_VERSION="24" NODE_MODULE="corepack" setup_nodejs @@ -437,6 +438,27 @@ cd "$INSTALL_DIR" ln -s "$GEO_DIR" "$APP_DIR" msg_ok "Installed GeoNames data" +# MickLesk temporary patch for HEIC thumbnail gen +msg_info "Patching media.repository.js" +MEDIA_REPO_JS="/opt/immich/app/dist/repositories/media.repository.js" +if [[ -f "$MEDIA_REPO_JS" ]]; then + python3 - <<'PY' +from pathlib import Path +p = Path('/opt/immich/app/dist/repositories/media.repository.js') +s = p.read_text() +old = "(0, sharp_1.default)(input).metadata()" +new = "(0, sharp_1.default)(input, { unlimited: true, limitInputPixels: false }).metadata()" +if new in s: + print('hotfix already there') +elif old in s: + p.write_text(s.replace(old, new, 1)) + print('hotfix applied') +else: + print('pattern not found, skipped') +PY +fi +msg_ok "Patched media.repository.js" + mkdir -p /var/log/immich touch /var/log/immich/{web.log,ml.log} msg_ok "Installed Immich" diff --git a/install/invidious-install.sh b/install/invidious-install.sh new file mode 100644 index 000000000..798b09e92 --- /dev/null +++ b/install/invidious-install.sh @@ -0,0 +1,121 @@ +#!/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/iv-org/invidious + +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 \ + git \ + pkg-config \ + libssl-dev \ + libxml2-dev \ + libyaml-dev \ + libgmp-dev \ + libreadline-dev \ + librsvg2-bin \ + libsqlite3-dev \ + zlib1g-dev \ + libpcre2-dev \ + libevent-dev \ + fonts-open-sans +msg_ok "Installed Dependencies" + +if [[ "$(arch_resolve amd64 arm64)" == "amd64" ]]; then + setup_deb822_repo "crystal" "https://download.opensuse.org/repositories/devel:/languages:/crystal/Debian_13/Release.key" "https://download.opensuse.org/repositories/devel:/languages:/crystal/Debian_13/" "./" + $STD apt install -y crystal +else + fetch_and_deploy_gh_release "Crystal" "crystal-lang/crystal" "prebuild" "latest" "/opt/crystal" "crystal-*-linux-aarch64-bundled.tar.gz" + ln -sf /opt/crystal/bin/crystal /usr/local/bin/crystal + ln -sf /opt/crystal/bin/shards /usr/local/bin/shards +fi + +PG_VERSION="17" setup_postgresql +PG_DB_NAME="invidious" PG_DB_USER="invidious" setup_postgresql_db +fetch_and_deploy_gh_release "Invidious" "iv-org/invidious" "tarball" "latest" "/opt/invidious" +fetch_and_deploy_gh_release "Invidious Companion" "iv-org/invidious-companion" "prebuild" "latest" "/opt/invidious-companion" "invidious_companion-$(arch_resolve x86_64 aarch64)-unknown-linux-gnu.tar.gz" + +msg_info "Building Invidious" +cd /opt/invidious +INVIDIOUS_VERSION="$(cat ~/.invidious 2>/dev/null || echo "unknown")" +INVIDIOUS_VERSION="${INVIDIOUS_VERSION#v}" +sed -i \ + -e "s~^\(\s*CURRENT_BRANCH\s*=\).*~\1 \"master\"~" \ + -e "s~^\(\s*CURRENT_COMMIT\s*=\).*~\1 \"\"~" \ + -e "s~^\(\s*CURRENT_VERSION\s*=\).*~\1 \"${INVIDIOUS_VERSION}\"~" \ + -e "s~^\(\s*CURRENT_TAG\s*=\).*~\1 \"${INVIDIOUS_VERSION}\"~" \ + -e "s~^\(\s*ASSET_COMMIT\s*=\).*~\1 \"\"~" \ + src/invidious.cr +$STD make +msg_ok "Built Invidious" + +msg_info "Configuring Invidious" +SECRET_KEY="$(openssl rand -hex 8)" +HMAC_KEY="$(openssl rand -hex 32)" +sed -e '\~^db:~,\~dbname:~d' \ + -e "s~^#database_.*~database_url: postgres://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME}~" \ + -e 's~^#check_tables.*~check_tables: true~' \ + -e 's~^#invidious_companion:~invidious_companion:~' \ + -e 's~^# - private_~ - private_~' \ + -e "s~^#invidious_companion_key:.*~invidious_companion_key: \"${SECRET_KEY}\"~" \ + -e "s~^hmac_key:.*~hmac_key: \"${HMAC_KEY}\"~" \ + /opt/invidious/config/config.example.yml >/opt/invidious/config/config.yml +chmod 600 /opt/invidious/config/config.yml + +cat </etc/logrotate.d/invidious.logrotate +/opt/invidious/invidious.log { + rotate 4 + weekly + notifempty + missingok + compress + minsize 1048576 +} +EOF +chmod 0644 /etc/logrotate.d/invidious.logrotate +msg_ok "Configured Invidious" + +msg_info "Migrating database" +$STD ./invidious --migrate +msg_ok "Migrated database" + +msg_info "Configuring services" +sed -e 's|^User=invidious|User=root|' \ + -e 's|^Group=invidious|Group=root|' \ + -e 's|/home/invidious/invidious|/opt/invidious|g' \ + /opt/invidious/invidious.service >/etc/systemd/system/invidious.service +mkdir -p /var/tmp/youtubei.js +cat </etc/systemd/system/invidious-companion.service +[Unit] +Description=Invidious Companion +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/invidious-companion +Environment=SERVER_SECRET_KEY=${SECRET_KEY} +Environment=CACHE_DIRECTORY=/var/tmp/youtubei.js +ExecStart=/opt/invidious-companion/invidious_companion +Restart=always +RestartSec=2s + +[Install] +WantedBy=multi-user.target +EOF +systemctl -q enable --now invidious invidious-companion +msg_ok "Configured services" + +motd_ssh +customize +cleanup_lxc diff --git a/install/leafwiki-install.sh b/install/leafwiki-install.sh new file mode 100644 index 000000000..228ffc702 --- /dev/null +++ b/install/leafwiki-install.sh @@ -0,0 +1,55 @@ +#!/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/perber/leafwiki + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +fetch_and_deploy_gh_release "leafwiki" "perber/leafwiki" "singlefile" "latest" "/usr/local/bin" "leafwiki-v*-linux-$(arch_resolve)" + +msg_info "Configuring LeafWiki" +mkdir -p /opt/leafwiki/data +mkdir -p /etc/leafwiki +JWT_SECRET=$(openssl rand -hex 32) +ADMIN_PASS=$(openssl rand -base64 12 | tr -dc 'a-zA-Z0-9' | head -c12) +cat </etc/leafwiki/.env +LEAFWIKI_DATA_DIR=/opt/leafwiki/data +LEAFWIKI_HOST=0.0.0.0 +LEAFWIKI_PORT=8080 +LEAFWIKI_JWT_SECRET=${JWT_SECRET} +LEAFWIKI_ADMIN_PASSWORD=${ADMIN_PASS} +LEAFWIKI_ALLOW_INSECURE=true +EOF +msg_ok "Configured LeafWiki" + +msg_info "Creating Service" +cat </etc/systemd/system/leafwiki.service +[Unit] +Description=LeafWiki +After=network.target + +[Service] +Type=simple +User=root +EnvironmentFile=/etc/leafwiki/.env +ExecStart=/usr/local/bin/leafwiki +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now leafwiki +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc diff --git a/install/localagi-install.sh b/install/localagi-install.sh new file mode 100644 index 000000000..3589d1b8b --- /dev/null +++ b/install/localagi-install.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: BillyOutlast +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/mudler/LocalAGI + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +APP="LocalAGI" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y build-essential +msg_ok "Installed Dependencies" + +NODE_VERSION="24" setup_nodejs +setup_go + +msg_info "Installing Bun" +export BUN_INSTALL="/root/.bun" +curl -fsSL https://bun.sh/install | $STD bash +ln -sf /root/.bun/bin/bun /usr/local/bin/bun +ln -sf /root/.bun/bin/bunx /usr/local/bin/bunx +msg_ok "Installed Bun" + +fetch_and_deploy_gh_release "localagi" "mudler/LocalAGI" "tarball" "latest" "/opt/localagi" + +msg_info "Configuring LocalAGI" +mkdir -p /opt/localagi/pool +cat <<'EOF' >/opt/localagi/.env +LOCALAGI_MODEL=gemma-3-4b-it-qat +LOCALAGI_MULTIMODAL_MODEL=moondream2-20250414 +LOCALAGI_IMAGE_MODEL=sd-1.5-ggml +LOCALAGI_LLM_API_URL=http://127.0.0.1:11434/v1 +LOCALAGI_STATE_DIR=/opt/localagi/pool +EOF +msg_ok "Configured LocalAGI" + +msg_info "Setting up LocalAGI" +cd /opt/localagi/webui/react-ui +$STD bun install +$STD bun run build +cd /opt/localagi +$STD go build -o /usr/local/bin/localagi +msg_ok "Set up LocalAGI" + +msg_info "Creating LocalAGI systemd service" +cat </etc/systemd/system/localagi.service +[Unit] +Description=LocalAGI +After=network.target + +[Service] +User=root +Type=simple +EnvironmentFile=/opt/localagi/.env + +WorkingDirectory=/opt/localagi +ExecStart=/usr/local/bin/localagi +Restart=on-failure + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now localagi +msg_ok "Created LocalAGI systemd service" + +motd_ssh +customize +cleanup_lxc diff --git a/install/n8n-install.sh b/install/n8n-install.sh index 8ddf37d0b..c6f0d421a 100644 --- a/install/n8n-install.sh +++ b/install/n8n-install.sh @@ -16,7 +16,6 @@ update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ - python3 \ python3-setuptools \ graphicsmagick msg_ok "Installed Dependencies" @@ -24,7 +23,7 @@ msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs msg_info "Installing n8n (Patience)" -$STD npm install -g n8n@2.27.5 +$STD npm install -g n8n@latest msg_ok "Installed n8n" msg_info "Creating Service" diff --git a/install/nexterm-install.sh b/install/nexterm-install.sh new file mode 100644 index 000000000..1ef0ecfe8 --- /dev/null +++ b/install/nexterm-install.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: Mathias Wagner (gnmyt) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://nexterm.dev/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +fetch_and_deploy_gh_release "nexterm-engine" "gnmyt/Nexterm" "prebuild" "latest" "/opt/nexterm/engine" "nexterm-engine-linux-$(arch_resolve "x64" "arm64").tar.gz" +fetch_and_deploy_gh_release "nexterm-server" "gnmyt/Nexterm" "singlefile" "latest" "/opt/nexterm/server" "nexterm-server-linux-$(arch_resolve "x64" "arm64")" + +msg_info "Configuring Nexterm" +LOCAL_ENGINE_TOKEN=$(tr -d '-' /etc/nexterm-engine/config.yaml +server_host: "127.0.0.1" +server_port: 7800 +registration_token: "${LOCAL_ENGINE_TOKEN}" +tls: false +EOF +cat </etc/nexterm-server/server.env +NODE_ENV=production +SERVER_PORT=6989 +LOCAL_ENGINE_TOKEN=${LOCAL_ENGINE_TOKEN} +ENCRYPTION_KEY=${ENCRYPTION_KEY} +EOF +chmod 0640 /etc/nexterm-engine/config.yaml /etc/nexterm-server/server.env +msg_ok "Configured Nexterm" + +msg_info "Creating Services" +cat </etc/systemd/system/nexterm-server.service +[Unit] +Description=Nexterm Server +Documentation=https://docs.nexterm.dev/ +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/nexterm/data +EnvironmentFile=/etc/nexterm-server/server.env +ExecStart=/opt/nexterm/server/nexterm-server +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +cat </etc/systemd/system/nexterm-engine.service +[Unit] +Description=Nexterm Engine +Documentation=https://docs.nexterm.dev/ +After=network-online.target nexterm-server.service +Wants=network-online.target + +[Service] +Type=simple +User=root +WorkingDirectory=/etc/nexterm-engine +Environment=FREERDP_EXTENSION_PATH=/opt/nexterm/engine/lib/freerdp2 +Environment=LD_LIBRARY_PATH=/opt/nexterm/engine/lib +ExecStart=/opt/nexterm/engine/nexterm-engine +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now nexterm-server +sleep 5 +systemctl enable -q --now nexterm-engine +msg_ok "Created Services" + +motd_ssh +customize +cleanup_lxc diff --git a/install/notediscovery-install.sh b/install/notediscovery-install.sh new file mode 100644 index 000000000..26687f8c6 --- /dev/null +++ b/install/notediscovery-install.sh @@ -0,0 +1,51 @@ +#!/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/gamosoft/NoteDiscovery + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +setup_uv + +fetch_and_deploy_gh_release "notediscovery" "gamosoft/NoteDiscovery" "tarball" + +msg_info "Installing Dependencies" +cd /opt/notediscovery +$STD uv sync --no-dev +msg_ok "Installed Dependencies" + +msg_info "Configuring NoteDiscovery" +mkdir -p /opt/notediscovery/data +msg_ok "Configured NoteDiscovery" + +msg_info "Creating Service" +cat </etc/systemd/system/notediscovery.service +[Unit] +Description=NoteDiscovery Knowledge Base +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/notediscovery +ExecStart=/opt/notediscovery/.venv/bin/python /opt/notediscovery/run.py +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now notediscovery +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc diff --git a/install/opencloud-install.sh b/install/opencloud-install.sh index 608440062..d0cb26994 100644 --- a/install/opencloud-install.sh +++ b/install/opencloud-install.sh @@ -64,7 +64,7 @@ $STD sudo -u cool coolconfig set-admin-password --user=admin --password="$COOLPA echo "$COOLPASS" >~/.coolpass msg_ok "Installed Collabora Online" -fetch_and_deploy_gh_release "OpenCloud" "opencloud-eu/opencloud" "singlefile" "v7.2.0" "/usr/bin" "opencloud-*-linux-$(arch_resolve)" +fetch_and_deploy_gh_release "OpenCloud" "opencloud-eu/opencloud" "singlefile" "v7.3.0" "/usr/bin" "opencloud-*-linux-$(arch_resolve)" mv /usr/bin/OpenCloud /usr/bin/opencloud msg_info "Configuring OpenCloud" diff --git a/install/oxicloud-install.sh b/install/oxicloud-install.sh new file mode 100644 index 000000000..1b80f103c --- /dev/null +++ b/install/oxicloud-install.sh @@ -0,0 +1,88 @@ +#!/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/DioCrafts/OxiCloud + +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 +msg_ok "Installed Dependencies" + +NODE_VERSION="24" setup_nodejs +PG_VERSION="17" setup_postgresql +PG_DB_NAME="oxicloud" PG_DB_USER="oxicloud" setup_postgresql_db +fetch_and_deploy_gh_release "OxiCloud" "DioCrafts/OxiCloud" "tarball" "latest" "/opt/oxicloud" +TOOLCHAIN="$(grep -oP 'FROM\s+rust:\K[0-9]+\.[0-9]+(\.[0-9]+)?' /opt/oxicloud/Dockerfile | head -1)" +RUST_TOOLCHAIN="${TOOLCHAIN:-stable}" setup_rust + +msg_info "Building Frontend SPA" +cd /opt/oxicloud/frontend +$STD npm ci +$STD npm run build +msg_ok "Built Frontend SPA" + +msg_info "Building OxiCloud (Patience)" +cd /opt/oxicloud +export DATABASE_URL="postgres://${PG_DB_USER}:${PG_DB_PASS}@localhost/${PG_DB_NAME}" +export RUSTFLAGS="-C target-cpu=native" +RAM_MB=$(awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo) +CARGO_JOBS=$((RAM_MB / 2560)) +[[ $CARGO_JOBS -lt 1 ]] && CARGO_JOBS=1 +[[ $CARGO_JOBS -gt $(nproc) ]] && CARGO_JOBS=$(nproc) +$STD cargo build --release -j "$CARGO_JOBS" --bin oxicloud --bin migrate-nfc-filenames +mv target/release/oxicloud /usr/local/bin/oxicloud +mv target/release/migrate-nfc-filenames /usr/local/bin/migrate-nfc-filenames +rm -rf /opt/oxicloud/static +mv /opt/oxicloud/static-dist /opt/oxicloud/static +rm -rf /opt/oxicloud/target /opt/oxicloud/frontend/node_modules +msg_ok "Built OxiCloud" + +msg_info "Configuring OxiCloud" +mkdir -p {/mnt/oxicloud,/etc/oxicloud} +sed -e 's|OXICLOUD_STORAGE_PATH=.*|OXICLOUD_STORAGE_PATH=/mnt/oxicloud|' \ + -e 's|OXICLOUD_SERVER_HOST=.*|OXICLOUD_SERVER_HOST=0.0.0.0|' \ + -e 's|OXICLOUD_STATIC_PATH=.*|OXICLOUD_STATIC_PATH=/opt/oxicloud/static|' \ + -e "s|^#OXICLOUD_BASE_URL=.*|OXICLOUD_BASE_URL=http://${LOCAL_IP}:8086|" \ + -e "s|OXICLOUD_DB_CONNECTION_STRING=.*|OXICLOUD_DB_CONNECTION_STRING=${DATABASE_URL}|" \ + -e "s|^DATABASE_URL=.*|DATABASE_URL=${DATABASE_URL}|" \ + -e "s|^#OXICLOUD_JWT_SECRET=.*|OXICLOUD_JWT_SECRET=$(openssl rand -hex 32)|" \ + /opt/oxicloud/example.env >/etc/oxicloud/.env +chmod 600 /etc/oxicloud/.env +msg_ok "Configured OxiCloud" + +msg_info "Creating OxiCloud Service" +cat </etc/systemd/system/oxicloud.service +[Unit] +Description=OxiCloud Service +After=network.target postgresql.service +Requires=postgresql.service + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/oxicloud +EnvironmentFile=/etc/oxicloud/.env +ExecStart=/usr/local/bin/oxicloud +Restart=always +RestartSec=5 +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now oxicloud +msg_ok "Created OxiCloud Service" + +motd_ssh +customize +cleanup_lxc diff --git a/install/pangolin-install.sh b/install/pangolin-install.sh index ab3fc5095..0a8f008af 100644 --- a/install/pangolin-install.sh +++ b/install/pangolin-install.sh @@ -16,18 +16,19 @@ update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ - python3 \ - sqlite3 \ iptables msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs -PANGOLIN_VERSION="${PANGOLIN_VERSION:-1.18.4}" +PG_VERSION="17" setup_postgresql +PG_DB_NAME="pangolin" PG_DB_USER="pangolin" setup_postgresql_db +PANGOLIN_VERSION="${PANGOLIN_VERSION:-1.20.0}" fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball" "$PANGOLIN_VERSION" fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_$(arch_resolve)" fetch_and_deploy_gh_release "traefik" "traefik/traefik" "prebuild" "latest" "/usr/bin" "traefik_v*_linux_$(arch_resolve).tar.gz" read -rp "${TAB3}Enter your Pangolin URL (ex: https://pangolin.example.com): " pango_url +[[ "$pango_url" != https://* && "$pango_url" != http://* ]] && pango_url="https://${pango_url}" read -rp "${TAB3}Enter your email address: " pango_email msg_info "Setup Pangolin" @@ -36,13 +37,14 @@ BADGER_VERSION=$(get_latest_github_release "fosrl/badger" "false") cd /opt/pangolin mkdir -p /opt/pangolin/config/{traefik,db,letsencrypt,logs} $STD npm ci -$STD npm run set:sqlite +$STD npm run set:pg $STD npm run set:oss rm -rf server/private -$STD npm run db:generate +DATABASE_URL="postgresql://pangolin:${PG_DB_PASS}@localhost:5432/pangolin" $STD npm run db:generate $STD npm run build $STD npm run build:cli cp -R .next/standalone ./ +cp -r server/migrations ./dist/init cat </usr/local/bin/pangctl #!/bin/sh @@ -74,6 +76,9 @@ flags: require_email_verification: false disable_signup_without_invite: false disable_user_create_org: false + +postgres: + connection_string: "postgresql://pangolin:${PG_DB_PASS}@localhost:5432/pangolin" EOF cat </opt/pangolin/config/traefik/traefik_config.yml @@ -181,7 +186,8 @@ http: servers: - url: "http://$LOCAL_IP:3000" EOF -$STD npm run db:push +export ENVIRONMENT=prod +$STD node dist/migrations.mjs . /etc/os-release if [ "$VERSION_CODENAME" = "trixie" ]; then @@ -197,7 +203,8 @@ msg_info "Creating Services" cat </etc/systemd/system/pangolin.service [Unit] Description=Pangolin Service -After=network.target +After=network.target postgresql.service +Wants=postgresql.service [Service] Type=simple diff --git a/install/pihole-install.sh b/install/pihole-install.sh index c88a7c997..547d79f23 100644 --- a/install/pihole-install.sh +++ b/install/pihole-install.sh @@ -119,7 +119,8 @@ edns-packet-max=1232 EOF if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then - cat </etc/unbound/unbound.conf.d/pi-hole.conf + cat <>/etc/unbound/unbound.conf.d/pi-hole.conf +server: tls-cert-bundle: "/etc/ssl/certs/ca-certificates.crt" forward-zone: name: "." diff --git a/install/pocketid-install.sh b/install/pocketid-install.sh index 47f945d3b..11b8fa591 100644 --- a/install/pocketid-install.sh +++ b/install/pocketid-install.sh @@ -14,7 +14,7 @@ network_check update_os read -r -p "${TAB3}What public URL do you want to use (e.g. pocketid.mydomain.com)? " public_url -fetch_and_deploy_gh_release "pocket-id" "pocket-id/pocket-id" "singlefile" "latest" "/opt/pocket-id/" "pocket-id-linux-$(arch_resolve)" +fetch_and_deploy_gh_release "pocket-id" "pocket-id/pocket-id" "singlefile" "latest" "/opt/pocket-id/" "pocket-id_linux_$(arch_resolve)" msg_info "Configuring Pocket ID" ENCRYPTION_KEY=$(openssl rand -base64 32) diff --git a/install/reitti-install.sh b/install/reitti-install.sh index 2810b1495..512b0cd13 100644 --- a/install/reitti-install.sh +++ b/install/reitti-install.sh @@ -30,27 +30,30 @@ mv /opt/reitti/reitti-*.jar /opt/reitti/reitti.jar msg_info "Installing Nginx Tile Cache" mkdir -p /var/cache/nginx/tiles -cat </etc/nginx/nginx.conf +cat <<'NGINXEOF' >/etc/nginx/nginx.conf user www-data; events { worker_connections 1024; } http { + resolver 1.1.1.1 8.8.8.8 valid=30s ipv6=off; proxy_cache_path /var/cache/nginx/tiles levels=1:2 keys_zone=tiles:10m max_size=1g inactive=30d use_temp_path=off; server { listen 80; - location / { - proxy_pass https://tile.openstreetmap.org/; - proxy_set_header Host tile.openstreetmap.org; + location /custom/ { + set $upstream_url $http_x_reitti_upstream_url; + proxy_pass $upstream_url; + proxy_set_header Host $proxy_host; proxy_set_header User-Agent "Reitti/1.0"; proxy_cache tiles; + proxy_cache_key $upstream_url; proxy_cache_valid 200 30d; proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; } } } -EOF +NGINXEOF chown -R www-data:www-data /var/cache/nginx chmod -R 750 /var/cache/nginx systemctl restart nginx @@ -71,6 +74,7 @@ server.compression.mime-types=text/plain,application/json logging.level.root=INFO logging.level.org.hibernate.engine.jdbc.spi.SqlExceptionHelper=FATAL logging.level.com.dedicatedcode.reitti=INFO +logging.level.org.quartz.core.ErrorLogger=FATAL # Internationalization spring.messages.basename=messages @@ -82,7 +86,7 @@ spring.messages.fallback-to-system-locale=false spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/$PG_DB_NAME spring.datasource.username=$PG_DB_USER spring.datasource.password=$PG_DB_PASS -spring.datasource.hikari.maximum-pool-size=20 +spring.datasource.hikari.maximum-pool-size=30 # Redis configuration spring.data.redis.host=127.0.0.1 @@ -92,20 +96,23 @@ spring.data.redis.password= spring.data.redis.database=0 spring.cache.redis.key-prefix= -spring.cache.cache-names=processed-visits,significant-places,users,magic-links,configurations,transport-mode-configs,avatarThumbnails,avatarData,user-settings +spring.cache.cache-names=processed-visits,significant-places,users,magic-links,configurations,transport-mode-configs,avatarThumbnails,avatarData,user-settings,devices,mapStyles,mapStyleJson spring.cache.redis.time-to-live=1d # Upload configuration spring.servlet.multipart.max-file-size=5GB spring.servlet.multipart.max-request-size=5GB +spring.servlet.multipart.resolve-lazily=true server.tomcat.max-part-count=100 +spring.mvc.async.request-timeout=600000 -# Rqueue configuration -rqueue.web.enable=false -rqueue.job.enabled=false -rqueue.message.durability.in-terminal-state=0 -rqueue.key.prefix=\${spring.cache.redis.key-prefix} -rqueue.message.converter.provider.class=com.dedicatedcode.reitti.config.RQueueCustomMessageConverter +# Quartz Scheduler configuration +spring.quartz.job-store-type=jdbc +spring.quartz.jdbc.initialize-schema=never +spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate +spring.quartz.properties.org.quartz.jobStore.isClustered=false +spring.quartz.properties.org.quartz.jobStore.tablePrefix=qrtz_ +spring.quartz.properties.org.quartz.threadPool.threadCount=5 # Application-specific settings reitti.server.advertise-uri= @@ -117,18 +124,25 @@ reitti.security.oidc.enabled=false reitti.security.oidc.registration.enabled=false reitti.import.batch-size=10000 -reitti.import.processing-idle-start-time=10 +reitti.import.grace-time-seconds=30 +reitti.import.staging.cleanup.cron=0 0 4 * * * + +reitti.batching.max-batch-size=100 +reitti.batching.max-wait-time=5 reitti.geo-point-filter.max-speed-kmh=1000 reitti.geo-point-filter.max-accuracy-meters=100 reitti.geo-point-filter.history-lookback-hours=24 reitti.geo-point-filter.window-size=50 -reitti.process-data.schedule=0 */10 * * * * reitti.process-data.refresh-views.schedule=0 0 4 * * * reitti.imports.schedule=0 5/10 * * * * reitti.imports.owntracks-recorder.schedule=\${reitti.imports.schedule} +reitti.jobs.cleanup.cron=0 0 4 * * ? +reitti.jobs.cleanup.max-age-hours=24 +reitti.db-janitor.schedule=0 0 4 * * ? + # Geocoding service configuration reitti.geocoding.max-errors=10 reitti.geocoding.photon.base-url= diff --git a/install/sftpgo-install.sh b/install/sftpgo-install.sh index d27d4f761..5547d2e59 100644 --- a/install/sftpgo-install.sh +++ b/install/sftpgo-install.sh @@ -19,8 +19,8 @@ msg_ok "Installed Dependencies" setup_deb822_repo \ "sftpgo" \ - "https://ftp.osuosl.org/pub/sftpgo/apt/gpg.key" \ - "https://ftp.osuosl.org/pub/sftpgo/apt" \ + "https://oss.sftpgo.com/apt/gpg.key" \ + "https://oss.sftpgo.com/apt" \ "trixie" msg_info "Installing SFTPGo" diff --git a/install/shinobi-install.sh b/install/shinobi-install.sh index 383325016..6107c6488 100644 --- a/install/shinobi-install.sh +++ b/install/shinobi-install.sh @@ -54,7 +54,7 @@ cronKey=$(head -c 1024 /etc/systemd/system/silverbullet.service [Unit] @@ -25,6 +36,7 @@ After=syslog.target network.target [Service] User=root Type=simple +${RUNTIME_API_ENV} ExecStart=/opt/silverbullet/bin/silverbullet --hostname 0.0.0.0 --port 3000 /opt/silverbullet/space WorkingDirectory=/opt/silverbullet Restart=on-failure diff --git a/install/snapotter-install.sh b/install/snapotter-install.sh index 7878c441a..b8330b7ca 100644 --- a/install/snapotter-install.sh +++ b/install/snapotter-install.sh @@ -39,6 +39,19 @@ msg_ok "Installed Dependencies" PYTHON_VERSION="3.11" setup_uv NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs +PG_VERSION="17" setup_postgresql +PG_DB_NAME="snapotter" PG_DB_USER="snapotter" setup_postgresql_db + +msg_info "Installing Redis" +$STD apt install -y redis-server +if grep -q '^appendonly ' /etc/redis/redis.conf; then + sed -i 's/^appendonly .*/appendonly yes/' /etc/redis/redis.conf +else + echo 'appendonly yes' >>/etc/redis/redis.conf +fi +$STD systemctl enable --now redis-server +msg_ok "Installed Redis" + fetch_and_deploy_gh_release "caire" "esimov/caire" "prebuild" "latest" "/usr/local/bin" "caire-*-linux-amd64.tar.gz" fetch_and_deploy_gh_release "snapotter" "snapotter-hq/SnapOtter" "prebuild" "latest" "/opt/snapotter" "snapotter-*-linux-amd64.tar.gz" @@ -61,7 +74,8 @@ mkdir -p /tmp/snapotter-workspace cat </opt/snapotter_data/.env PORT=1349 NODE_ENV=production -DB_PATH=/opt/snapotter_data/snapotter.db +DATABASE_URL=postgres://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME} +REDIS_URL=redis://127.0.0.1:6379 WORKSPACE_PATH=/tmp/snapotter-workspace FILES_STORAGE_PATH=/opt/snapotter_data/files PYTHON_VENV_PATH=/opt/snapotter_data/ai/venv @@ -85,7 +99,9 @@ PNPM_BIN="$(command -v pnpm)" cat </etc/systemd/system/snapotter.service [Unit] Description=SnapOtter Service -After=network.target +Wants=network-online.target +After=network-online.target postgresql.service redis-server.service +Requires=postgresql.service redis-server.service [Service] Type=simple @@ -99,6 +115,7 @@ RestartSec=5 [Install] WantedBy=multi-user.target EOF +systemctl daemon-reload systemctl enable -q --now snapotter msg_ok "Created Service" diff --git a/install/squid-install.sh b/install/squid-install.sh new file mode 100644 index 000000000..aa0110820 --- /dev/null +++ b/install/squid-install.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: 007hacky007 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.squid-cache.org/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Configuring Squid" +mkdir -p /etc/squid +cat </etc/squid/squid.conf +acl localnet src 0.0.0.1-0.255.255.255 +acl localnet src 10.0.0.0/8 +acl localnet src 100.64.0.0/10 +acl localnet src 169.254.0.0/16 +acl localnet src 172.16.0.0/12 +acl localnet src 192.168.0.0/16 +acl localnet src fc00::/7 +acl localnet src fe80::/10 + +acl SSL_ports port 443 +acl Safe_ports port 80 +acl Safe_ports port 21 +acl Safe_ports port 443 +acl Safe_ports port 70 +acl Safe_ports port 210 +acl Safe_ports port 1025-65535 +acl Safe_ports port 280 +acl Safe_ports port 488 +acl Safe_ports port 591 +acl Safe_ports port 777 +acl CONNECT method CONNECT + +http_access deny !Safe_ports +http_access deny CONNECT !SSL_ports +http_access allow localhost manager +http_access deny manager + +auth_param basic program /usr/lib/squid/basic_ncsa_auth /etc/squid/passwords +auth_param basic realm proxy +acl authenticated proxy_auth REQUIRED +http_access allow authenticated +http_access deny all + +http_port 3128 + +coredump_dir /var/spool/squid + +refresh_pattern ^ftp: 1440 20% 10080 +refresh_pattern ^gopher: 1440 0% 1440 +refresh_pattern -i (/cgi-bin/|\\?) 0 0% 0 +refresh_pattern . 0 20% 4320 + +# Privacy / hardening +httpd_suppress_version_string on +visible_hostname $(hostname) +forwarded_for delete +request_header_access X-Forwarded-For deny all +EOF +msg_ok "Configured Squid" + +msg_info "Installing Dependencies" +$STD apt install -y \ + squid \ + apache2-utils +msg_ok "Installed Dependencies" + +msg_info "Configuring Squid Authentication" +touch /etc/squid/passwords +chown proxy:proxy /etc/squid/passwords +chmod 640 /etc/squid/passwords +$STD squid -k parse +msg_ok "Configured Squid Authentication" + +msg_info "Starting Service" +systemctl enable -q --now squid +msg_ok "Started Service" + +motd_ssh +customize +cleanup_lxc diff --git a/install/storyteller-install.sh b/install/storyteller-install.sh index b3b343da6..1df32ec27 100644 --- a/install/storyteller-install.sh +++ b/install/storyteller-install.sh @@ -28,7 +28,7 @@ NODE_VERSION="24" NODE_MODULE="corepack,yarn" setup_nodejs fetch_and_deploy_gh_release "readium" "readium/cli" "prebuild" "latest" "/opt/readium" "readium_linux_$(arch_resolve "x86_64" "arm64").tar.gz" ln -sf /opt/readium/readium /usr/local/bin/readium -fetch_and_deploy_gl_release "storyteller" "storyteller-platform/storyteller" "tarball" "latest" "/opt/storyteller" +fetch_and_deploy_gl_release "storyteller" "storyteller-platform/storyteller" "tarball" "latest" "/opt/storyteller" "" "web-v2" msg_info "Setting up Storyteller" cd /opt/storyteller diff --git a/install/sync-in-install.sh b/install/sync-in-install.sh new file mode 100644 index 000000000..0550fecca --- /dev/null +++ b/install/sync-in-install.sh @@ -0,0 +1,82 @@ +#!/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/Sync-in/server + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +NODE_VERSION="22" setup_nodejs +setup_mariadb +MARIADB_DB_NAME="sync_in" MARIADB_DB_USER="sync_in" setup_mariadb_db + +msg_info "Installing Sync-in" +mkdir -p /opt/sync-in/data +$STD npm install --prefix /opt/sync-in @sync-in/server +msg_ok "Installed Sync-in" + +msg_info "Configuring Sync-in" +ENCRYPT_KEY=$(openssl rand -hex 32) +ACCESS_SECRET=$(openssl rand -hex 32) +REFRESH_SECRET=$(openssl rand -hex 32) +cat </opt/sync-in/environment.yaml +server: + port: 8080 +mysql: + url: 'mysql://${MARIADB_DB_USER}:${MARIADB_DB_PASS}@localhost:3306/${MARIADB_DB_NAME}' +auth: + encryptionKey: '${ENCRYPT_KEY}' + token: + access: + secret: '${ACCESS_SECRET}' + refresh: + secret: '${REFRESH_SECRET}' +applications: + files: + dataPath: '/opt/sync-in/data' +EOF +msg_ok "Configured Sync-in" + +msg_info "Running Database Migrations" +cd /opt/sync-in +$STD npx sync-in-server migrate-db +msg_ok "Ran Database Migrations" + +msg_info "Creating Admin User" +cd /opt/sync-in +$STD npx sync-in-server create-user +msg_ok "Created Admin User" + +VERSION=$(node -pe "require('/opt/sync-in/node_modules/@sync-in/server/package.json').version" 2>/dev/null || echo "") +[[ -n "$VERSION" ]] && echo "$VERSION" >~/.sync-in + +msg_info "Creating Service" +cat </etc/systemd/system/sync-in.service +[Unit] +Description=Sync-in Server +After=network.target mariadb.service + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/sync-in +ExecStart=/opt/sync-in/node_modules/.bin/sync-in-server start +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now sync-in +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc diff --git a/install/wanderer-install.sh b/install/wanderer-install.sh index 3cebc9fef..6a8571bbf 100644 --- a/install/wanderer-install.sh +++ b/install/wanderer-install.sh @@ -20,7 +20,8 @@ if [[ "$(arch_resolve)" == "arm64" ]]; then else fetch_and_deploy_gh_release "meilisearch" "meilisearch/meilisearch" "binary" "latest" "/opt/wanderer/source/search" fi -mkdir -p /opt/wanderer/{source,data/pb_data,data/meili_data} +mkdir -p /opt/wanderer/{source,data/pb_data,data/meili_data,data/plugins} +[[ -e /data/plugins ]] || ln -sfn /opt/wanderer/data/plugins /data/plugins fetch_and_deploy_gh_release "wanderer" "open-wanderer/wanderer" "tarball" "latest" "/opt/wanderer/source" msg_info "Installing wanderer (patience)" @@ -32,6 +33,12 @@ $STD npm ci $STD npm run build msg_ok "Installed wanderer" +msg_info "Installing wanderer plugins" +for plugin in hammerhead komoot strava; do + fetch_and_deploy_gh_release "wanderer-plugin-${plugin}" "open-wanderer/wanderer" "prebuild" "latest" "/opt/wanderer/data/plugins" "wanderer-plugin-${plugin}.tar.gz" || msg_warn "Failed to install wanderer plugin: ${plugin}" +done +msg_ok "Installed wanderer plugins" + msg_info "Creating Service" MEILI_KEY=$(openssl rand -hex 32) POCKETBASE_KEY=$(openssl rand -hex 16) diff --git a/install/webtrees-install.sh b/install/webtrees-install.sh index c2be2e79e..69b735e46 100644 --- a/install/webtrees-install.sh +++ b/install/webtrees-install.sh @@ -47,6 +47,8 @@ msg_ok "Configured Caddy" msg_info "Automating Webtrees Setup" cd /opt/webtrees +mkdir -p /opt/webtrees/data +chown -R www-data:www-data /opt/webtrees/data WT_ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c15) $STD sudo -u www-data php /opt/webtrees/index.php config-ini \ --dbhost=127.0.0.1 \ @@ -56,6 +58,15 @@ $STD sudo -u www-data php /opt/webtrees/index.php config-ini \ --dbname=webtrees \ --tblpfx=wt_ \ --base-url="http://${LOCAL_IP}" +msg_info "Initializing Webtrees database schema" +for i in {1..15}; do + if curl -sf "http://127.0.0.1/" >/dev/null 2>&1; then + break + fi + sleep 2 +done +$STD mariadb -u webtrees -p"${MARIADB_DB_PASS}" -h 127.0.0.1 webtrees -e "SHOW TABLES LIKE 'wt_user';" | grep -q wt_user +msg_ok "Initialized Webtrees database schema" $STD sudo -u www-data php /opt/webtrees/index.php user Admin \ --create \ --real-name="Administrator" \ diff --git a/install/yuvomi-install.sh b/install/yuvomi-install.sh new file mode 100644 index 000000000..521049586 --- /dev/null +++ b/install/yuvomi-install.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/ulsklyc/yuvomi + +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 \ + python3 \ + make \ + g++ \ + libsqlcipher-dev +msg_ok "Installed Dependencies" + +NODE_VERSION="22" setup_nodejs + +fetch_and_deploy_gh_release "yuvomi" "ulsklyc/yuvomi" "tarball" + +msg_info "Installing Node.js Dependencies" +cd /opt/yuvomi +$STD npm ci --omit=dev +msg_ok "Installed Node.js Dependencies" + +msg_info "Configuring Yuvomi" +mkdir -p /opt/yuvomi/data /opt/yuvomi/backups +SESSION_SECRET=$(openssl rand -hex 32) +DB_ENCRYPT_KEY=$(openssl rand -hex 32) +cat </opt/yuvomi/.env +PORT=3000 +NODE_ENV=production +DB_PATH=/opt/yuvomi/data/yuvomi.db +DB_ENCRYPTION_KEY=${DB_ENCRYPT_KEY} +SESSION_SECRET=${SESSION_SECRET} +RATE_LIMIT_WINDOW_MS=60000 +RATE_LIMIT_MAX_ATTEMPTS=5 +RATE_LIMIT_BLOCK_DURATION_MS=900000 +EOF +msg_ok "Configured Yuvomi" + +msg_info "Creating Service" +cat </etc/systemd/system/yuvomi.service +[Unit] +Description=Yuvomi Family Planner +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/yuvomi +EnvironmentFile=/opt/yuvomi/.env +ExecStart=/usr/bin/node server/index.js +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now yuvomi +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc diff --git a/misc/build.func b/misc/build.func index bf74bd3b6..53509780f 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3753,30 +3753,32 @@ run_addon_updates() { } runtime_script_status_guard() { + local mode="${1:-}" local script_slug="${SCRIPT_SLUG:-${NSAPP:-}}" script_slug="$(echo "$script_slug" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')" [[ -z "$script_slug" ]] && return 0 - local api_url="https://db.community-scripts.org/api/collections/script_scripts/records?filter=(slug='${script_slug}')&perPage=1&fields=slug,is_disabled,is_deleted,disable_message,deleted_message" + local api_url="https://db.community-scripts.org/api/collections/script_scripts/records?filter=(slug='${script_slug}')&perPage=1&fields=slug,is_disabled,is_deleted,disable_message,deleted_message,pinned_version,pin_reason" local response if ! response=$(curl -fsSL --connect-timeout 2 --max-time 3 "$api_url" 2>/dev/null); then msg_warn "Script status check is unavailable. Continuing without status verification." return 0 fi - if ! command -v jq >/dev/null 2>&1; then - msg_warn "Missing jq for script status check. Continuing without status verification." + local is_deleted is_disabled deleted_message disable_message pinned_version pin_reason info_url + if printf '%s' "$response" | grep -qE '"items":[[:space:]]*\[[[:space:]]*\]'; then return 0 fi - local has_record is_deleted is_disabled deleted_message disable_message info_url - has_record=$(printf '%s' "$response" | jq -r '.items | length') - [[ "$has_record" == "0" ]] && return 0 - - is_deleted=$(printf '%s' "$response" | jq -r '.items[0].is_deleted // false') - is_disabled=$(printf '%s' "$response" | jq -r '.items[0].is_disabled // false') - deleted_message=$(printf '%s' "$response" | jq -r '.items[0].deleted_message // ""') - disable_message=$(printf '%s' "$response" | jq -r '.items[0].disable_message // ""') + # PocketBase returns a flat, fixed-field JSON blob; sed is enough here (no jq needed). + is_deleted=$(printf '%s' "$response" | sed -n 's/.*"is_deleted"[[:space:]]*:[[:space:]]*\(true\|false\).*/\1/p' | head -1) + is_disabled=$(printf '%s' "$response" | sed -n 's/.*"is_disabled"[[:space:]]*:[[:space:]]*\(true\|false\).*/\1/p' | head -1) + deleted_message=$(printf '%s' "$response" | sed -n 's/.*"deleted_message"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1) + disable_message=$(printf '%s' "$response" | sed -n 's/.*"disable_message"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1) + pinned_version=$(printf '%s' "$response" | sed -n 's/.*"pinned_version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1) + pin_reason=$(printf '%s' "$response" | sed -n 's/.*"pin_reason"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1) + is_deleted=${is_deleted:-false} + is_disabled=${is_disabled:-false} info_url="https://community-scripts.org/scripts/${script_slug}" if [[ "$is_deleted" == "true" ]]; then @@ -3806,6 +3808,26 @@ runtime_script_status_guard() { return 1 fi + if [[ "$mode" == "update" && -n "$pinned_version" && -n "${NSAPP:-}" ]]; then + local current_file="$HOME/.${NSAPP}" + if [[ -f "$current_file" ]]; then + local installed pinned_clean installed_clean + installed="$(<"$current_file")" + pinned_clean="$pinned_version" + installed_clean="$installed" + [[ "$pinned_clean" =~ ^v[0-9] ]] && pinned_clean="${pinned_clean:1}" + [[ "$installed_clean" =~ ^v[0-9] ]] && installed_clean="${installed_clean:1}" + if [[ "$installed_clean" == "$pinned_clean" ]]; then + if [[ -n "$pin_reason" ]]; then + msg_info "You are already on the pinned version (${pinned_version}). ${pin_reason}" + else + msg_info "You are already on the pinned version (${pinned_version}). No newer update is offered intentionally — please do not open an issue unless you see an actual error." + fi + msg_info "More info: ${info_url}" + fi + fi + fi + return 0 } @@ -3821,7 +3843,7 @@ runtime_script_status_guard() { start() { source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then - runtime_script_status_guard || return 0 + runtime_script_status_guard install || return 0 install_script || return 0 return 0 elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then @@ -3829,7 +3851,7 @@ start() { set_std_mode ensure_profile_loaded get_lxc_ip - runtime_script_status_guard || return 0 + runtime_script_status_guard update || return 0 update_script run_addon_updates update_motd_ip @@ -3840,7 +3862,7 @@ start() { set_std_mode ensure_profile_loaded get_lxc_ip - runtime_script_status_guard || return 0 + runtime_script_status_guard update || return 0 update_script run_addon_updates update_motd_ip @@ -3870,7 +3892,7 @@ start() { esac ensure_profile_loaded get_lxc_ip - runtime_script_status_guard || return 0 + runtime_script_status_guard update || return 0 update_script run_addon_updates update_motd_ip diff --git a/misc/tools.func b/misc/tools.func index e8e18f4f3..adb7a5ac4 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -36,6 +36,10 @@ # # ============================================================================== +# Guard against double-sourcing (core.func uses the same pattern) +[[ -n "${_TOOLS_FUNC_LOADED:-}" ]] && return 0 +_TOOLS_FUNC_LOADED=1 + # ------------------------------------------------------------------------------ # Debug helper - outputs to stderr when TOOLS_DEBUG is enabled # Usage: debug_log "message" @@ -91,16 +95,17 @@ curl_with_retry() { while [[ $attempt -le $retries ]]; do debug_log "curl attempt $attempt/$retries: $url" - local curl_cmd="curl -fsSL --connect-timeout $connect_timeout --max-time $timeout" - [[ -n "$extra_opts" ]] && curl_cmd="$curl_cmd $extra_opts" + # Build curl command as array to avoid command injection via extra_opts + local -a curl_args=(curl -fsSL --connect-timeout "$connect_timeout" --max-time "$timeout") + [[ -n "$extra_opts" ]] && read -ra _extra <<<"$extra_opts" && curl_args+=("${_extra[@]}") if [[ "$output" == "-" ]]; then - if $curl_cmd "$url"; then + if "${curl_args[@]}" "$url"; then success=true break fi else - if $curl_cmd -o "$output" "$url"; then + if "${curl_args[@]}" -o "$output" "$url"; then success=true break fi @@ -153,15 +158,16 @@ curl_api_with_retry() { while [[ $attempt -le $retries ]]; do debug_log "curl API attempt $attempt/$retries: $url" - local curl_cmd="curl -fsSL --connect-timeout $connect_timeout --max-time $timeout -w '%{http_code}'" - [[ -n "$extra_opts" ]] && curl_cmd="$curl_cmd $extra_opts" + # Build curl command as array to avoid command injection via extra_opts + local -a curl_args=(curl -fsSL --connect-timeout "$connect_timeout" --max-time "$timeout" -w '%{http_code}') + [[ -n "$extra_opts" ]] && read -ra _extra <<<"$extra_opts" && curl_args+=("${_extra[@]}") if [[ -n "$body_file" ]]; then - http_code=$($curl_cmd -o "$body_file" "$url" 2>/dev/null) || true + http_code=$("${curl_args[@]}" -o "$body_file" "$url" 2>/dev/null) || true else # Capture body and http_code separately local tmp_body="/tmp/curl_api_body_$$" - http_code=$($curl_cmd -o "$tmp_body" "$url" 2>/dev/null) || true + http_code=$("${curl_args[@]}" -o "$tmp_body" "$url" 2>/dev/null) || true if [[ -f "$tmp_body" ]]; then cat "$tmp_body" rm -f "$tmp_body" @@ -304,7 +310,10 @@ edit_yaml_config() { return 1 fi - sed -i "s|^\([[:space:]]*${key}[[:space:]]*:\).*|\1 ${value}|" "$file" + # Escape sed metacharacters in value (| and &) to prevent injection + local escaped_value="${value//|/\\|}" + escaped_value="${escaped_value//&/\\&}" + sed -i "s|^\([[:space:]]*${key}[[:space:]]*:\).*|\1 ${escaped_value}|" "$file" } # ------------------------------------------------------------------------------ @@ -1687,10 +1696,6 @@ is_ubuntu() { [[ "$(get_os_info id)" == "ubuntu" ]] } -is_alpine() { - [[ "$(get_os_info id)" == "alpine" ]] -} - # ------------------------------------------------------------------------------ # Get Debian/Ubuntu major version # ------------------------------------------------------------------------------ @@ -2454,8 +2459,10 @@ start_timer() { end_timer() { local start_time="$1" local label="${2:-Operation}" - local end_time=$(date +%s) + local end_time + end_time=$(date +%s) local duration=$((end_time - start_time)) + echo "${label} took ${duration}s" } # ------------------------------------------------------------------------------ @@ -2559,6 +2566,199 @@ fetch_and_deploy_gh_tag() { return 0 } +# ------------------------------------------------------------------------------ +# Get the latest GitLab repository tag matching a glob pattern. +# +# Description: +# - Queries the GitLab repository tags API (up to 100 tags per page) +# - Filters tag names against a shell glob pattern (e.g. "web-v*" or "mobile-v*") +# - Always excludes pre-release tags (those containing alpha, beta, or rc) +# - Sorts matching tags with `sort -V` and returns the highest one +# - Supports GITLAB_TOKEN for private/rate-limited projects +# +# Usage: +# get_latest_gl_tag "owner/repo" "web-v*" +# get_latest_gl_tag "owner/repo" "mobile-v*" +# get_latest_gl_tag "owner/repo" # returns newest tag (no filter) +# +# Arguments: +# $1 - GitLab repo path (namespace/project, e.g. "mygroup/myapp") +# $2 - Optional glob pattern to filter tag names (e.g. "web-v*") +# +# Returns: +# Latest matching tag name on stdout, or non-zero on failure +# ------------------------------------------------------------------------------ +get_latest_gl_tag() { + local repo="$1" + local pattern="${2:-}" + + local repo_encoded + repo_encoded=$(printf '%s' "$repo" | sed 's|/|%2F|g') + + local api_base="https://gitlab.com/api/v4/projects/${repo_encoded}/repository/tags" + local api_timeout="--connect-timeout 10 --max-time 60" + + local header=() + [[ -n "${GITLAB_TOKEN:-}" ]] && header=(-H "PRIVATE-TOKEN: $GITLAB_TOKEN") + + # If a pattern is given, pass it as a regex search to reduce server-side results. + # GitLab ?search= supports anchored regex; convert leading glob prefix to regex anchor. + local search_param="" + if [[ -n "$pattern" ]]; then + # Strip trailing wildcard for the search hint (server-side prefix filter). + local prefix="${pattern%%\**}" + [[ -n "$prefix" ]] && search_param="?search=^${prefix}&per_page=100" || search_param="?per_page=100" + else + search_param="?per_page=100" + fi + + local temp_file + temp_file=$(mktemp) || return 1 + + local http_code + http_code=$(curl $api_timeout -sSL -w "%{http_code}" -o "$temp_file" \ + "${header[@]}" "${api_base}${search_param}" 2>/dev/null) || true + + if [[ "$http_code" != "200" ]]; then + rm -f "$temp_file" + msg_error "GitLab tags API returned HTTP $http_code for $repo" + return 22 + fi + + local tag="" + if [[ -n "$pattern" ]]; then + # Client-side glob filter + pre-release exclusion, then version-sort to pick the highest match. + tag=$(jq -r '.[].name' "$temp_file" 2>/dev/null | while IFS= read -r t; do + case "$t" in + *alpha* | *beta* | *rc*) continue ;; + $pattern) echo "$t" ;; + esac + done | sort -V | tail -n1) + else + # No pattern: skip pre-release tags, take the first remaining (newest) one. + tag=$(jq -r '.[].name' "$temp_file" 2>/dev/null | + grep -Eiv '(alpha|beta|rc)' | + head -n1) + fi + + rm -f "$temp_file" + + if [[ -z "$tag" ]]; then + msg_error "No tags matching '${pattern:-*}' found for ${repo}" + return 250 + fi + + echo "$tag" +} + +# ------------------------------------------------------------------------------ +# Fetches and deploys a GitLab tag-based source tarball. +# +# Description: +# - Resolves the latest tag matching the given glob pattern via get_latest_gl_tag +# (or uses the exact tag if one is provided instead of "latest") +# - Downloads the GitLab source tarball for that tag +# - Extracts it to the target directory +# - Writes the resolved tag to ~/. for update-checking +# +# Usage: +# fetch_and_deploy_gl_tag "myapp" "mygroup/myrepo" "web-v*" +# fetch_and_deploy_gl_tag "myapp" "mygroup/myrepo" "mobile-v*" "/opt/myapp" +# fetch_and_deploy_gl_tag "myapp" "mygroup/myrepo" "v*" # any v-tag +# fetch_and_deploy_gl_tag "myapp" "mygroup/myrepo" "web-v3.0*" # narrow version range +# +# Arguments: +# $1 - App name (used for version file ~/. and lowercase tarball name) +# $2 - GitLab repo path (namespace/project, e.g. "mygroup/myapp") +# $3 - Tag pattern: glob (e.g. "web-v*") or exact tag (e.g. "web-v3.0.0"). +# Use "latest" to fetch the single newest tag with no pattern filter. +# $4 - Target directory (default: /opt/$app) +# +# Notes: +# - Supports CLEAN_INSTALL=1 to wipe target before extracting +# - Supports GITLAB_TOKEN for private/rate-limited projects +# - For repos that only publish tags, not formal GitLab Releases +# (use fetch_and_deploy_gl_release for proper Releases with assets) +# ------------------------------------------------------------------------------ +fetch_and_deploy_gl_tag() { + local app="$1" + local repo="$2" + local tag_pattern="${3:-latest}" + local target="${4:-/opt/$app}" + + local app_lc="" + 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 header=() + [[ -n "${GITLAB_TOKEN:-}" ]] && header=(-H "PRIVATE-TOKEN: $GITLAB_TOKEN") + + # Resolve the tag: if caller passed a glob/latest, query the API. + # If caller passed an exact tag (no wildcards), use it directly. + local resolved_tag="$tag_pattern" + if [[ "$tag_pattern" == "latest" || "$tag_pattern" == *"*"* || "$tag_pattern" == *"?"* ]]; then + local glob_arg="" + [[ "$tag_pattern" != "latest" ]] && glob_arg="$tag_pattern" + resolved_tag=$(get_latest_gl_tag "$repo" "$glob_arg") || { + msg_error "Failed to determine latest tag matching '${tag_pattern}' for ${repo}" + return 250 + } + fi + + local current_version="" + [[ -f "$version_file" ]] && current_version=$(<"$version_file") + + if [[ "$current_version" == "$resolved_tag" ]]; then + msg_ok "$app is already up-to-date ($resolved_tag)" + return 0 + fi + + local repo_encoded + repo_encoded=$(printf '%s' "$repo" | sed 's|/|%2F|g') + + # GitLab source tarball URL (no release needed, works for any tag). + local version_safe="${resolved_tag//\//-}" + local tarball_url="https://gitlab.com/${repo}/-/archive/${resolved_tag}/${app_lc}-${version_safe}.tar.gz" + + local tmpdir + tmpdir=$(mktemp -d) || return 1 + local filename="${app_lc}-${version_safe}.tar.gz" + + msg_info "Fetching GitLab tag: ${app} (${resolved_tag})" + + curl $download_timeout -fsSL "${header[@]}" -o "$tmpdir/$filename" "$tarball_url" || { + msg_error "Download failed: $tarball_url" + rm -rf "$tmpdir" + return 7 + } + + 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 251 + } + + 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 + + rm -rf "$tmpdir" + echo "$resolved_tag" >"$version_file" + msg_ok "Deployed ${app} ${resolved_tag} to ${target}" + return 0 +} + # ------------------------------------------------------------------------------ # Checks for new GitHub tag (for repos without releases). # @@ -2633,6 +2833,7 @@ check_for_gh_release() { local source="$2" local pinned_version_in="${3:-}" # optional local pin_reason="${4:-}" # optional reason shown to user + local tag_prefix="${5:-}" # optional tag prefix filter (e.g. web-v2) local app_lc="" app_lc="$(echo "${app,,}" | tr -d ' ')" local current_file="$HOME/.${app_lc}" @@ -2688,7 +2889,7 @@ check_for_gh_release() { rm -f "$gh_check_json" fi - if [[ -z "$pinned_version_in" ]]; then + if [[ -z "$pinned_version_in" && -z "$tag_prefix" ]]; then http_code=$(curl -sSL --max-time 20 -w "%{http_code}" -o "$gh_check_json" \ -H 'Accept: application/vnd.github+json' \ -H 'X-GitHub-Api-Version: 2022-11-28' \ @@ -2716,7 +2917,7 @@ check_for_gh_release() { rm -f "$gh_check_json" fi - # If no releases yet (pinned version OR /latest failed), fetch up to 100 + # If no releases yet (pinned version, tag prefix, OR /latest failed), fetch up to 100 if [[ -z "$releases_json" ]]; then http_code=$(curl -sSL --max-time 20 -w "%{http_code}" -o "$gh_check_json" \ -H 'Accept: application/vnd.github+json' \ @@ -2754,9 +2955,18 @@ check_for_gh_release() { rm -f "$gh_check_json" fi - mapfile -t raw_tags < <(jq -r '.[] | select(.draft==false and .prerelease==false) | .tag_name' <<<"$releases_json") + if [[ -n "$tag_prefix" ]]; then + mapfile -t raw_tags < <(jq -r --arg p "$tag_prefix" \ + '.[] | select(.draft==false and .prerelease==false) | select(.tag_name | startswith($p)) | .tag_name' <<<"$releases_json") + else + mapfile -t raw_tags < <(jq -r '.[] | select(.draft==false and .prerelease==false) | .tag_name' <<<"$releases_json") + fi if ((${#raw_tags[@]} == 0)); then - msg_error "No stable releases found for ${app}" + if [[ -n "$tag_prefix" ]]; then + msg_error "No stable releases matching prefix '${tag_prefix}' found for ${app}" + else + msg_error "No stable releases found for ${app}" + fi return 250 fi @@ -3760,6 +3970,7 @@ fetch_and_deploy_gh_release() { local version="${var_appversion:-${4:-latest}}" local target="${5:-/opt/$app}" local asset_pattern="${6:-}" + local tag_prefix="${7:-}" # Validate app name to prevent /root/. directory issues if [[ -z "$app" ]]; then @@ -3789,7 +4000,13 @@ fetch_and_deploy_gh_release() { TOOLS_GH_REL_JSON="$gh_rel_json" local api_url="https://api.github.com/repos/$repo/releases" - [[ "$version" != "latest" ]] && api_url="$api_url/tags/$version" || api_url="$api_url/latest" + if [[ "$version" != "latest" ]]; then + api_url="$api_url/tags/$version" + elif [[ -n "$tag_prefix" ]]; then + api_url="$api_url?per_page=100" + else + api_url="$api_url/latest" + fi local header=() [[ -n "${GITHUB_TOKEN:-}" ]] && header=(-H "Authorization: token $GITHUB_TOKEN") @@ -3852,6 +4069,14 @@ fetch_and_deploy_gh_release() { local json tag_name json=$(<"$gh_rel_json") + if [[ "$version" == "latest" && -n "$tag_prefix" ]]; then + json=$(echo "$json" | jq --arg p "$tag_prefix" \ + '[.[] | select(.draft==false and .prerelease==false) | select(.tag_name | startswith($p))][0] // empty') + if [[ -z "$json" || "$json" == "null" ]]; then + msg_error "No stable release matching prefix '${tag_prefix}' found for $repo on GitHub" + return 1 + fi + fi tag_name=$(echo "$json" | jq -r '.tag_name // .name // empty') # Only strip leading 'v' when followed by a digit (e.g. v1.2.3), not words like "version/..." [[ "$tag_name" =~ ^v[0-9] ]] && version="${tag_name:1}" || version="$tag_name" @@ -4438,20 +4663,20 @@ setup_composer() { # - Cleans up legacy repository files # # Usage: -# setup_docker # Uses distro package (recommended) -# USE_DOCKER_REPO=true setup_docker # Uses official Docker repo +# setup_docker # Uses official Docker repo (recommended) +# USE_DOCKER_REPO=false setup_docker # Uses distro docker.io package # DOCKER_PORTAINER="true" setup_docker # DOCKER_LOG_DRIVER="json-file" setup_docker # # Variables: -# USE_DOCKER_REPO - Set to "true" to use official Docker repository -# (default: false, uses distro docker.io package) +# USE_DOCKER_REPO - Set to "false" to use distro docker.io package +# (default: true, uses official Docker repository) # DOCKER_PORTAINER - Install Portainer CE (optional, "true" to enable) # DOCKER_LOG_DRIVER - Log driver (optional, default: "journald") # DOCKER_SKIP_UPDATES - Skip container update check (optional, "true" to skip) # # Features: -# - Uses stable distro packages by default +# - Uses official Docker repository by default # - Migrates from get.docker.com to repository-based installation # - Updates Docker Engine if newer version available # - Interactive per-container update prompt (Y/N, 60 s auto-no) @@ -4467,7 +4692,7 @@ _docker_is_noninteractive() { setup_docker() { local docker_installed=false local portainer_installed=false - local USE_DOCKER_REPO="${USE_DOCKER_REPO:-false}" + local USE_DOCKER_REPO="${USE_DOCKER_REPO:-true}" # Check if Docker is already installed if command -v docker &>/dev/null; then @@ -4482,7 +4707,7 @@ setup_docker() { msg_info "Portainer container detected" fi - # Scenario 1: Use distro repository (default, most stable) + # Scenario 1: Use distro repository (opt-out via USE_DOCKER_REPO=false) if [[ "$USE_DOCKER_REPO" != "true" && "$USE_DOCKER_REPO" != "TRUE" && "$USE_DOCKER_REPO" != "1" ]]; then # Install or upgrade Docker from distro repo @@ -6746,9 +6971,11 @@ setup_mariadb_db() { msg_info "Setting up MariaDB Database" - $STD mariadb -u root -e "CREATE DATABASE \`$MARIADB_DB_NAME\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" - $STD mariadb -u root -e "CREATE USER '$MARIADB_DB_USER'@'localhost' IDENTIFIED BY '$MARIADB_DB_PASS';" - $STD mariadb -u root -e "GRANT ALL ON \`$MARIADB_DB_NAME\`.* TO '$MARIADB_DB_USER'@'localhost';" + # Use --defaults-extra-file to pass credentials safely and escape identifiers + # to prevent SQL injection via DB name / user / password + $STD mariadb -u root -e "CREATE DATABASE \`${MARIADB_DB_NAME//\`/\`\`}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" + $STD mariadb -u root -e "CREATE USER '${MARIADB_DB_USER//\'/\'\'}'@'localhost' IDENTIFIED BY '${MARIADB_DB_PASS//\'/\'\'}';" + $STD mariadb -u root -e "GRANT ALL ON \`${MARIADB_DB_NAME//\`/\`\`}\`.* TO '${MARIADB_DB_USER//\'/\'\'}'@'localhost';" # Optional extra grants if [[ -n "${MARIADB_DB_EXTRA_GRANTS:-}" ]]; then @@ -7204,6 +7431,18 @@ setup_mongodb() { mkdir -p /var/lib/mongodb chown -R mongodb:mongodb /var/lib/mongodb + local KERNEL_VERSION MONGO_MAJOR + KERNEL_VERSION=$(uname -r | cut -d- -f1) + MONGO_MAJOR="${MONGO_VERSION%%.*}" + if ((MONGO_MAJOR >= 8)) && [[ "$(printf '%s\n' "6.19" "$KERNEL_VERSION" | sort -V | head -n1)" == "6.19" ]]; then + mkdir -p /etc/systemd/system/mongod.service.d + cat </etc/systemd/system/mongod.service.d/rseq.conf +[Service] +Environment=GLIBC_TUNABLES=glibc.pthread.rseq=1 +EOF + systemctl daemon-reload + fi + $STD systemctl enable mongod || { msg_warn "Failed to enable mongod service" } @@ -8378,8 +8617,14 @@ setup_postgresql_db() { fi msg_info "Setting up PostgreSQL Database" - $STD sudo -u postgres psql -c "CREATE ROLE $PG_DB_USER WITH LOGIN PASSWORD '$PG_DB_PASS';" - $STD sudo -u postgres psql -c "CREATE DATABASE $PG_DB_NAME WITH OWNER $PG_DB_USER ENCODING 'UTF8' TEMPLATE template0;" + # Escape single quotes in identifiers to prevent SQL injection + local _pg_user_escaped _pg_pass_escaped _pg_db_escaped + _pg_user_escaped="${PG_DB_USER//\'/\'\'}" + _pg_pass_escaped="${PG_DB_PASS//\'/\'\'}" + _pg_db_escaped="${PG_DB_NAME//\'/\'\'}" + + $STD sudo -u postgres psql -c "CREATE ROLE $_pg_user_escaped WITH LOGIN PASSWORD '$_pg_pass_escaped';" + $STD sudo -u postgres psql -c "CREATE DATABASE $_pg_db_escaped WITH OWNER $_pg_user_escaped ENCODING 'UTF8' TEMPLATE template0;" # Configure pg_cron database BEFORE creating the extension (must be set before pg_cron loads) if [[ -n "${PG_DB_EXTENSIONS:-}" ]] && [[ ",${PG_DB_EXTENSIONS//[[:space:]]/}," == *",pg_cron,"* ]]; then @@ -8444,6 +8689,7 @@ setup_postgresql_db() { export PG_DB_USER export PG_DB_PASS } + # ------------------------------------------------------------------------------ # Installs rbenv and ruby-build, installs Ruby and optionally Rails. # @@ -8463,8 +8709,26 @@ setup_ruby() { local RBENV_DIR="$HOME/.rbenv" local RBENV_BIN="$RBENV_DIR/bin/rbenv" local PROFILE_FILE="$HOME/.profile" + local BASH_PROFILE_FILE="$HOME/.bash_profile" + local BASHRC_FILE="$HOME/.bashrc" local TMP_DIR=$(mktemp -d) + if ! grep -q 'rbenv init' "$PROFILE_FILE" 2>/dev/null; then + cat <<'EOF' >>"$PROFILE_FILE" +export PATH="$HOME/.rbenv/bin:$PATH" +eval "$(rbenv init -)" +EOF + fi + if ! grep -q '.rbenv/shims' "$PROFILE_FILE" 2>/dev/null; then + echo 'export PATH="$HOME/.rbenv/shims:$HOME/.rbenv/bin:$PATH"' >>"$PROFILE_FILE" + fi + if [[ -f "$BASH_PROFILE_FILE" ]] && ! grep -q '.rbenv/shims' "$BASH_PROFILE_FILE"; then + echo 'export PATH="$HOME/.rbenv/shims:$HOME/.rbenv/bin:$PATH"' >>"$BASH_PROFILE_FILE" + fi + if [[ -f "$BASHRC_FILE" ]] && ! grep -q '.rbenv/shims' "$BASHRC_FILE"; then + echo 'export PATH="$HOME/.rbenv/shims:$HOME/.rbenv/bin:$PATH"' >>"$BASHRC_FILE" + fi + # Get currently installed Ruby version local CURRENT_RUBY_VERSION="" if [[ -x "$RBENV_BIN" ]]; then @@ -9276,6 +9540,7 @@ check_for_gl_release() { local source="$2" local pinned_version_in="${3:-}" # optional local pin_reason="${4:-}" # optional reason shown to user + local tag_prefix="${5:-}" # optional tag prefix filter (e.g. web-v2) local app_lc="${app,,}" local current_file="$HOME/.${app_lc}" @@ -9352,9 +9617,18 @@ check_for_gl_release() { rm -f "$gl_check_json" fi - mapfile -t raw_tags < <(jq -r '.[] | .tag_name' <<<"$releases_json") + if [[ -n "$tag_prefix" ]]; then + mapfile -t raw_tags < <(jq -r --arg p "$tag_prefix" \ + '.[] | select(.tag_name | startswith($p)) | .tag_name' <<<"$releases_json") + else + mapfile -t raw_tags < <(jq -r '.[] | .tag_name' <<<"$releases_json") + fi if ((${#raw_tags[@]} == 0)); then - msg_error "No releases found for ${app} on GitLab" + if [[ -n "$tag_prefix" ]]; then + msg_error "No releases matching prefix '${tag_prefix}' found for ${app} on GitLab" + else + msg_error "No releases found for ${app} on GitLab" + fi return 250 fi @@ -9549,6 +9823,7 @@ fetch_and_deploy_gl_release() { local version="${var_appversion:-${4:-latest}}" local target="${5:-/opt/$app}" local asset_pattern="${6:-}" + local tag_prefix="${7:-}" if [[ -z "$app" ]]; then app="${repo##*/}" @@ -9580,6 +9855,8 @@ fetch_and_deploy_gl_release() { local api_url if [[ "$version" != "latest" ]]; then api_url="$api_base/$version" + elif [[ -n "$tag_prefix" ]]; then + api_url="$api_base?per_page=100&order_by=released_at&sort=desc" else api_url="$api_base?per_page=1&order_by=released_at&sort=desc" fi @@ -9634,9 +9911,18 @@ fetch_and_deploy_gl_release() { json=$(<"$gl_rel_json") if [[ "$version" == "latest" ]]; then - json=$(echo "$json" | jq '.[0] // empty') + if [[ -n "$tag_prefix" ]]; then + json=$(echo "$json" | jq --arg p "$tag_prefix" \ + '[.[] | select(.tag_name | startswith($p))][0] // empty') + else + json=$(echo "$json" | jq '.[0] // empty') + fi if [[ -z "$json" || "$json" == "null" ]]; then - msg_error "No releases found for $repo on GitLab" + if [[ -n "$tag_prefix" ]]; then + msg_error "No release matching prefix '${tag_prefix}' found for $repo on GitLab" + else + msg_error "No releases found for $repo on GitLab" + fi return 1 fi fi diff --git a/tools/pve/update-lxcs.sh b/tools/pve/update-lxcs.sh index 52e9d2528..e083a4fc2 100644 --- a/tools/pve/update-lxcs.sh +++ b/tools/pve/update-lxcs.sh @@ -78,7 +78,7 @@ function update_container() { alpine) pct exec "$container" -- ash -c "apk -U upgrade" ;; archlinux) pct exec "$container" -- bash -c "pacman -Syyu --noconfirm" ;; fedora | rocky | centos | alma) pct exec "$container" -- bash -c "dnf -y update && dnf -y upgrade" ;; - ubuntu | debian | devuan) pct exec "$container" -- bash -c "apt-get update 2>/dev/null | grep 'packages.*upgraded'; apt list --upgradable 2>/dev/null | cat && apt-get -yq dist-upgrade 2>&1; rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED || true" ;; + ubuntu | debian | devuan) pct exec "$container" -- bash -c "apt-get update 2>/dev/null | grep 'packages.*upgraded'; apt list --upgradable 2>/dev/null | cat && apt-get -yq dist-upgrade 2>&1; apt-get -yq autoremove 2>&1; apt-get -yq autoclean 2>&1; rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED || true" ;; opensuse) pct exec "$container" -- bash -c "zypper ref && zypper --non-interactive dup" ;; esac } diff --git a/vm/haos-vm.sh b/vm/haos-vm.sh index 85d582170..df5189614 100644 --- a/vm/haos-vm.sh +++ b/vm/haos-vm.sh @@ -475,7 +475,7 @@ function advanced_settings() { done while true; do - if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan (leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VLAN1" ]; then VLAN1="Default" VLAN=""