From b92931f6ee614d0f7ed508f2455377e96c8eee56 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Thu, 15 Jan 2026 22:50:31 -0500 Subject: [PATCH 01/10] nextExplorer: disable shell for user; fix version display --- ct/nextexplorer.sh | 1 + install/nextexplorer-install.sh | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ct/nextexplorer.sh b/ct/nextexplorer.sh index 6b2b651b4..27fcf564f 100644 --- a/ct/nextexplorer.sh +++ b/ct/nextexplorer.sh @@ -54,6 +54,7 @@ function update_script() { mv backend/{node_modules,src,package.json} "$APP_DIR" mv frontend/dist/ "$APP_DIR"/src/public chown -R explorer:explorer "$APP_DIR" /etc/nextExplorer + sed -i "\|version|s|$(jq -cr '.version' ${APP_DIR}/package.json)|$(cat ~/.nextexplorer)|" "$APP_DIR"/package.json msg_ok "Updated nextExplorer" msg_info "Starting nextExplorer" diff --git a/install/nextexplorer-install.sh b/install/nextexplorer-install.sh index 8ed03263a..940cdec67 100644 --- a/install/nextexplorer-install.sh +++ b/install/nextexplorer-install.sh @@ -119,8 +119,9 @@ SHARES_ENABLED=true # SHARES_ALLOW_ANONYMOUS=true EOF chmod 600 /etc/nextExplorer/.env -$STD useradd -U -s /bin/bash -m -d /home/explorer explorer +$STD useradd -U -s /usr/sbin/nologin -m -d /home/explorer explorer chown -R explorer:explorer "$APP_DIR" /etc/nextExplorer +sed -i "\|version|s|$(jq -cr '.version' ${APP_DIR}/package.json)|$(cat ~/.nextexplorer)|" "$APP_DIR"/package.json msg_ok "Configured nextExplorer" msg_info "Creating nextExplorer Service" @@ -131,6 +132,8 @@ After=network.target [Service] Type=simple +User=explorer +Group=explorer WorkingDirectory=/opt/nextExplorer/app EnvironmentFile=/etc/nextExplorer/.env ExecStart=/usr/bin/node ./src/app.js From 6c2322443c75190bc744df4d17f359edf86ccadd Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Fri, 16 Jan 2026 08:33:01 +0100 Subject: [PATCH 02/10] Add workflow to update version sources and fetch versions This workflow automates the extraction of version sources from install scripts, fetches their versions, and generates a pull request if changes are detected in version information. --- .github/workflows/update-versions-github.yml | 512 +++++++++++++++++++ 1 file changed, 512 insertions(+) create mode 100644 .github/workflows/update-versions-github.yml diff --git a/.github/workflows/update-versions-github.yml b/.github/workflows/update-versions-github.yml new file mode 100644 index 000000000..5c18ef2e5 --- /dev/null +++ b/.github/workflows/update-versions-github.yml @@ -0,0 +1,512 @@ +name: Update Versions from GitHub + +on: + workflow_dispatch: + schedule: + # Runs at 06:00 and 18:00 UTC + - cron: "0 6,18 * * *" + +permissions: + contents: write + pull-requests: write + +env: + SOURCES_FILE: frontend/public/json/version-sources.json + VERSIONS_FILE: frontend/public/json/versions.json + +jobs: + update-versions: + if: github.repository == 'community-scripts/ProxmoxVE' + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + ref: main + + - name: Generate GitHub App Token + id: generate-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ vars.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Extract version sources from install scripts + run: | + set -euo pipefail + + echo "=========================================" + echo " Extracting version sources from scripts" + echo "=========================================" + + # Initialize sources array + sources_json="[]" + + # Function to add a source entry + add_source() { + local slug="$1" + local type="$2" + local source="$3" + local script="$4" + + # Check if slug already exists (avoid duplicates) + if echo "$sources_json" | jq -e ".[] | select(.slug == \"$slug\")" > /dev/null 2>&1; then + return + fi + + sources_json=$(echo "$sources_json" | jq \ + --arg slug "$slug" \ + --arg type "$type" \ + --arg source "$source" \ + --arg script "$script" \ + '. += [{"slug": $slug, "type": $type, "source": $source, "script": $script, "version": null, "date": null}]') + } + + echo "" + echo "=== Method 1: fetch_and_deploy_gh_release calls ===" + count=0 + for script in install/*-install.sh; do + [[ ! -f "$script" ]] && continue + slug=$(basename "$script" | sed 's/-install\.sh$//') + + # Extract repo from fetch_and_deploy_gh_release "app" "owner/repo" + while IFS= read -r line; do + if [[ "$line" =~ fetch_and_deploy_gh_release[[:space:]]+\"[^\"]*\"[[:space:]]+\"([^\"]+)\" ]]; then + repo="${BASH_REMATCH[1]}" + add_source "$slug" "github" "$repo" "$script" + ((count++)) + break # Only first match per script + fi + done < <(grep 'fetch_and_deploy_gh_release' "$script" 2>/dev/null || true) + done + echo "Found $count scripts with fetch_and_deploy_gh_release" + + echo "" + echo "=== Method 2: GitHub URLs in scripts (fallback) ===" + count=0 + for script in install/*-install.sh; do + [[ ! -f "$script" ]] && continue + slug=$(basename "$script" | sed 's/-install\.sh$//') + + # Skip if already found + if echo "$sources_json" | jq -e ".[] | select(.slug == \"$slug\")" > /dev/null 2>&1; then + continue + fi + + # Look for github.com/owner/repo patterns + repo=$(grep -oE 'github\.com/([a-zA-Z0-9_-]+/[a-zA-Z0-9_.-]+)' "$script" 2>/dev/null \ + | sed 's|github\.com/||' \ + | sed 's/\.git$//' \ + | grep -v 'community-scripts/ProxmoxVE' \ + | grep -v '^repos/' \ + | head -1 || true) + + if [[ -n "$repo" && "$repo" =~ ^[a-zA-Z0-9_-]+/[a-zA-Z0-9_.-]+$ ]]; then + add_source "$slug" "github" "$repo" "$script" + ((count++)) + fi + done + echo "Found $count additional scripts with GitHub URLs" + + echo "" + echo "=== Method 3: npm packages ===" + # Detect npm install --global + for script in install/*-install.sh; do + [[ ! -f "$script" ]] && continue + slug=$(basename "$script" | sed 's/-install\.sh$//') + + # Skip if already found + if echo "$sources_json" | jq -e ".[] | select(.slug == \"$slug\")" > /dev/null 2>&1; then + continue + fi + + # Look for npm install --global + pkg=$(grep -oE 'npm install[^|;]*--global[^|;]*' "$script" 2>/dev/null \ + | grep -oE '\s[a-z][a-z0-9_-]+(@[^\s]+)?$' \ + | tr -d ' ' \ + | sed 's/@.*//' \ + | tail -1 || true) + + if [[ -n "$pkg" ]]; then + add_source "$slug" "npm" "$pkg" "$script" + fi + done + + echo "" + echo "=== Method 4: Docker images ===" + # Known Docker-based apps (from docker pull or docker run) + declare -A docker_mappings=( + ["homeassistant"]="homeassistant/home-assistant" + ["portainer"]="portainer/portainer-ce" + ["dockge"]="louislam/dockge" + ["immich"]="ghcr.io/immich-app/immich-server" + ["audiobookshelf"]="ghcr.io/advplyr/audiobookshelf" + ["podman-homeassistant"]="homeassistant/home-assistant" + ) + + for slug in "${!docker_mappings[@]}"; do + image="${docker_mappings[$slug]}" + if ! echo "$sources_json" | jq -e ".[] | select(.slug == \"$slug\")" > /dev/null 2>&1; then + add_source "$slug" "docker" "$image" "install/${slug}-install.sh" + fi + done + + echo "" + echo "=== Method 5: Manual GitHub mappings (apt-based apps) ===" + # Apps that install via apt but have GitHub releases for version tracking + declare -A manual_github_mappings=( + ["actualbudget"]="actualbudget/actual" + ["apache-cassandra"]="apache/cassandra" + ["apache-couchdb"]="apache/couchdb" + ["apache-guacamole"]="apache/guacamole-server" + ["apache-tomcat"]="apache/tomcat" + ["archivebox"]="ArchiveBox/ArchiveBox" + ["aria2"]="aria2/aria2" + ["asterisk"]="asterisk/asterisk" + ["casaos"]="IceWhaleTech/CasaOS" + ["checkmk"]="Checkmk/checkmk" + ["cloudflared"]="cloudflare/cloudflared" + ["coolify"]="coollabsio/coolify" + ["crafty-controller"]="crafty-controller/crafty-4" + ["cross-seed"]="cross-seed/cross-seed" + ["deconz"]="dresden-elektronik/deconz-rest-plugin" + ["deluge"]="deluge-torrent/deluge" + ["dokploy"]="Dokploy/dokploy" + ["emqx"]="emqx/emqx" + ["esphome"]="esphome/esphome" + ["flowiseai"]="FlowiseAI/Flowise" + ["forgejo"]="forgejo/forgejo" + ["garage"]="deuxfleurs-org/garage" + ["ghost"]="TryGhost/Ghost" + ["grafana"]="grafana/grafana" + ["graylog"]="Graylog2/graylog2-server" + ["homebridge"]="homebridge/homebridge" + ["hyperhdr"]="awawa-dev/HyperHDR" + ["hyperion"]="hyperion-project/hyperion.ng" + ["influxdb"]="influxdata/influxdb" + ["iobroker"]="ioBroker/ioBroker" + ["jenkins"]="jenkinsci/jenkins" + ["komodo"]="moghingold/komodo" + ["lazylibrarian"]="lazylibrarian/LazyLibrarian" + ["limesurvey"]="LimeSurvey/LimeSurvey" + ["mariadb"]="MariaDB/server" + ["mattermost"]="mattermost/mattermost" + ["meshcentral"]="Ylianst/MeshCentral" + ["metabase"]="metabase/metabase" + ["mongodb"]="mongodb/mongo" + ["mysql"]="mysql/mysql-server" + ["neo4j"]="neo4j/neo4j" + ["node-red"]="node-red/node-red" + ["ntfy"]="binwiederhier/ntfy" + ["nzbget"]="nzbgetcom/nzbget" + ["octoprint"]="OctoPrint/OctoPrint" + ["onedev"]="theonedev/onedev" + ["onlyoffice"]="ONLYOFFICE/DocumentServer" + ["openhab"]="openhab/openhab-distro" + ["openobserve"]="openobserve/openobserve" + ["openwebui"]="open-webui/open-webui" + ["passbolt"]="passbolt/passbolt_api" + ["pihole"]="pi-hole/pi-hole" + ["postgresql"]="postgres/postgres" + ["rabbitmq"]="rabbitmq/rabbitmq-server" + ["readarr"]="Readarr/Readarr" + ["redis"]="redis/redis" + ["runtipi"]="runtipi/runtipi" + ["sftpgo"]="drakkan/sftpgo" + ["shinobi"]="ShinobiCCTV/Shinobi" + ["sonarqube"]="SonarSource/sonarqube" + ["sonarr"]="Sonarr/Sonarr" + ["syncthing"]="syncthing/syncthing" + ["tdarr"]="HaveAGitGat/Tdarr" + ["technitiumdns"]="TechnitiumSoftware/DnsServer" + ["transmission"]="transmission/transmission" + ["typesense"]="typesense/typesense" + ["unmanic"]="Unmanic/unmanic" + ["valkey"]="valkey-io/valkey" + ["verdaccio"]="verdaccio/verdaccio" + ["vikunja"]="go-vikunja/vikunja" + ["wazuh"]="wazuh/wazuh" + ["wordpress"]="WordPress/WordPress" + ["zabbix"]="zabbix/zabbix" + ["zammad"]="zammad/zammad" + ["zerotier-one"]="zerotier/ZeroTierOne" + # Apps without known GitHub repos (use "-" as placeholder) + ["agentdvr"]="-" + ["apt-cacher-ng"]="-" + ["channels"]="-" + ["daemonsync"]="-" + ["dotnetaspwebapi"]="-" + ["fhem"]="-" + ["fileflows"]="-" + ["fumadocs"]="-" + ["infisical"]="-" + ["itsm-ng"]="-" + ["jupyternotebook"]="-" + ["kasm"]="-" + ["lyrionmusicserver"]="-" + ["minarca"]="-" + ["mqtt"]="-" + ["nextcloudpi"]="-" + ["nextpvr"]="-" + ["notifiarr"]="-" + ["nxwitness"]="-" + ["omada"]="-" + ["omv"]="-" + ["plex"]="-" + ["podman"]="-" + ["readeck"]="-" + ["resiliosync"]="-" + ["smokeping"]="-" + ["splunk-enterprise"]="-" + ["sqlserver2022"]="-" + ["swizzin"]="-" + ["teamspeak-server"]="-" + ["twingate-connector"]="-" + ["unifi"]="-" + ["urbackupserver"]="-" + ["yunohost"]="-" + ) + + for slug in "${!manual_github_mappings[@]}"; do + repo="${manual_github_mappings[$slug]}" + if ! echo "$sources_json" | jq -e ".[] | select(.slug == \"$slug\")" > /dev/null 2>&1; then + # Skip placeholder entries in extraction, they get added in Method 8 + [[ "$repo" == "-" ]] && continue + add_source "$slug" "github" "$repo" "install/${slug}-install.sh" + fi + done + + echo "" + echo "=== Method 6: Proxmox LXC templates ===" + # Base OS versions from Proxmox template index + declare -A pveam_mappings=( + ["debian"]="pveam:debian" + ["ubuntu"]="pveam:ubuntu" + ["alpine"]="pveam:alpine" + ) + + for slug in "${!pveam_mappings[@]}"; do + template="${pveam_mappings[$slug]}" + if ! echo "$sources_json" | jq -e ".[] | select(.slug == \"$slug\")" > /dev/null 2>&1; then + add_source "$slug" "pveam" "$template" "ct/${slug}.sh" + fi + done + + echo "" + echo "=== Method 7: Special sources ===" + # Home Assistant OS VM + if ! echo "$sources_json" | jq -e ".[] | select(.slug == \"haos-vm\")" > /dev/null 2>&1; then + add_source "haos-vm" "github" "home-assistant/operating-system" "vm/haos-vm.sh" + fi + + echo "" + echo "=== Method 8: Unknown/Manual apps ===" + # Apps without known version sources - add with type "manual" for manual updates + unknown_apps=( + "agentdvr" "apt-cacher-ng" "channels" "daemonsync" "dotnetaspwebapi" + "fhem" "fileflows" "fumadocs" "infisical" "itsm-ng" "jupyternotebook" + "kasm" "lyrionmusicserver" "minarca" "mqtt" "nextcloudpi" "nextpvr" + "notifiarr" "nxwitness" "omada" "omv" "plex" "podman" "readeck" + "resiliosync" "smokeping" "splunk-enterprise" "sqlserver2022" "swizzin" + "teamspeak-server" "twingate-connector" "unifi" "urbackupserver" "yunohost" + ) + + for slug in "${unknown_apps[@]}"; do + if ! echo "$sources_json" | jq -e ".[] | select(.slug == \"$slug\")" > /dev/null 2>&1; then + add_source "$slug" "manual" "-" "install/${slug}-install.sh" + fi + done + + # Save sources file + echo "$sources_json" | jq --arg date "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ + '{generated: $date, sources: (. | sort_by(.slug))}' > "$SOURCES_FILE" + + total=$(echo "$sources_json" | jq 'length') + echo "" + echo "=========================================" + echo " Total sources extracted: $total" + echo "=========================================" + + - name: Fetch versions for all sources + env: + GH_TOKEN: ${{ steps.generate-token.outputs.token }} + run: | + set -euo pipefail + + echo "=========================================" + echo " Fetching versions from sources" + echo "=========================================" + + success=0 + failed=0 + manual=0 + total=$(jq '.sources | length' "$SOURCES_FILE") + + # Process each source + for i in $(seq 0 $((total - 1))); do + entry=$(jq -r ".sources[$i]" "$SOURCES_FILE") + slug=$(echo "$entry" | jq -r '.slug') + type=$(echo "$entry" | jq -r '.type') + source=$(echo "$entry" | jq -r '.source') + + echo -n "[$((i+1))/$total] $slug ($type: $source) ... " + + version="" + date="" + + case "$type" in + github) + # Try releases first + response=$(gh api "repos/${source}/releases/latest" 2>/dev/null || echo '{"message": "Not Found"}') + + if echo "$response" | jq -e '.tag_name' > /dev/null 2>&1; then + version=$(echo "$response" | jq -r '.tag_name') + date=$(echo "$response" | jq -r '.published_at // empty') + else + # Fallback to tags + version=$(gh api "repos/${source}/tags" --jq '.[0].name // empty' 2>/dev/null || echo "") + fi + ;; + + npm) + response=$(curl -fsSL "https://registry.npmjs.org/${source}/latest" 2>/dev/null || echo '{}') + version=$(echo "$response" | jq -r '.version // empty') + ;; + + docker) + if [[ "$source" == ghcr.io/* ]]; then + # GitHub Container Registry + ghcr_path="${source#ghcr.io/}" + owner="${ghcr_path%%/*}" + pkg="${ghcr_path##*/}" + version=$(gh api "users/${owner}/packages/container/${pkg}/versions" --jq '.[0].metadata.container.tags[] | select(. != "latest")' 2>/dev/null | head -1 || echo "") + else + # Docker Hub + version=$(curl -fsSL "https://hub.docker.com/v2/repositories/${source}/tags?page_size=10&ordering=last_updated" 2>/dev/null \ + | jq -r '.results[] | select(.name != "latest") | .name' | head -1 || echo "") + fi + ;; + + pveam) + # Proxmox LXC template versions from download.proxmox.com + os_name="${source#pveam:}" + # Fetch the template index and get latest version + version=$(curl -fsSL "http://download.proxmox.com/images/system/" 2>/dev/null \ + | grep -oE "${os_name}-[0-9]+\.[0-9]+-default_[0-9]+_amd64" \ + | sed "s/${os_name}-//" | sed 's/-default.*//' \ + | sort -V | tail -1 || echo "") + ;; + + manual) + # Manual entries - no automatic version fetching + # These need to be updated manually or have their source type changed + version="-" + ((manual++)) + echo -n "(manual) " + ;; + esac + + if [[ -n "$version" && "$version" != "null" ]]; then + # Update the source entry with version + jq --arg idx "$i" --arg version "$version" --arg date "${date:-}" \ + '.sources[$idx | tonumber].version = $version | .sources[$idx | tonumber].date = $date' \ + "$SOURCES_FILE" > "${SOURCES_FILE}.tmp" && mv "${SOURCES_FILE}.tmp" "$SOURCES_FILE" + echo "✓ $version" + ((success++)) + else + echo "⚠ no version found" + ((failed++)) + fi + done + + echo "" + echo "=========================================" + echo " SUMMARY" + echo "=========================================" + echo "Success: $success (automated)" + echo "Manual: $manual (placeholder)" + echo "Failed: $failed" + echo "Total: $total" + echo "=========================================" + + - name: Generate versions.json for compatibility + run: | + # Convert version-sources.json to versions.json format for backward compatibility + jq '[.sources[] | select(.version != null) | {name: .source, version: .version, date: .date}]' \ + "$SOURCES_FILE" > "$VERSIONS_FILE" + + echo "Generated versions.json with $(jq length "$VERSIONS_FILE") entries" + + - name: Check for changes + id: check-changes + run: | + if git diff --quiet "$SOURCES_FILE" "$VERSIONS_FILE" 2>/dev/null; then + echo "changed=false" >> "$GITHUB_OUTPUT" + echo "No changes detected" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + echo "Changes detected:" + git diff --stat "$SOURCES_FILE" "$VERSIONS_FILE" 2>/dev/null || true + fi + + - name: Create Pull Request + if: steps.check-changes.outputs.changed == 'true' + env: + GH_TOKEN: ${{ steps.generate-token.outputs.token }} + run: | + BRANCH_NAME="automated/update-versions-$(date +%Y%m%d)" + + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "GitHub Actions[bot]" + + # Check if branch exists and delete it + git push origin --delete "$BRANCH_NAME" 2>/dev/null || true + + git checkout -b "$BRANCH_NAME" + git add "$SOURCES_FILE" "$VERSIONS_FILE" + git commit -m "chore: update version-sources.json and versions.json + + Sources: $(jq '.sources | length' "$SOURCES_FILE") + With versions: $(jq '[.sources[] | select(.version != null)] | length' "$SOURCES_FILE") + Generated: $(jq -r '.generated' "$SOURCES_FILE")" + + git push origin "$BRANCH_NAME" --force + + # Check if PR already exists + existing_pr=$(gh pr list --head "$BRANCH_NAME" --state open --json number --jq '.[0].number // empty') + + if [[ -n "$existing_pr" ]]; then + echo "PR #$existing_pr already exists, updating..." + else + gh pr create \ + --title "[Automated] Update version-sources.json" \ + --body "This PR updates version information from multiple sources. + + ## Sources + - **GitHub Releases**: Direct from \`fetch_and_deploy_gh_release\` calls + - **GitHub URLs**: Extracted from install scripts + - **npm Registry**: For Node.js based apps + - **Docker Hub/GHCR**: For container-based apps + + ## Stats + - Total sources: $(jq '.sources | length' "$SOURCES_FILE") + - With versions: $(jq '[.sources[] | select(.version != null)] | length' "$SOURCES_FILE") + + --- + *Automatically generated from install scripts*" \ + --base main \ + --head "$BRANCH_NAME" \ + --label "automated pr" + fi + + - name: Auto-approve PR + if: steps.check-changes.outputs.changed == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + BRANCH_NAME="automated/update-versions-$(date +%Y%m%d)" + pr_number=$(gh pr list --head "$BRANCH_NAME" --state open --json number --jq '.[0].number') + if [[ -n "$pr_number" ]]; then + gh pr review "$pr_number" --approve + fi From e59a39cde58d4a1731d9f0e2687770715331bdc7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 16 Jan 2026 08:36:50 +0100 Subject: [PATCH 03/10] Update workflow and script for improved release handling Upgraded actions/checkout to v4 in the workflow and removed redundant re-approval step. Improved get-gh-release.sh by fixing whitespace, enabling output file update, and adding a completion message. --- .github/workflows/get-versions-from-gh.yaml | 12 +----------- .github/workflows/scripts/get-gh-release.sh | 11 ++++++----- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/.github/workflows/get-versions-from-gh.yaml b/.github/workflows/get-versions-from-gh.yaml index 0cf632d11..0a990efdd 100644 --- a/.github/workflows/get-versions-from-gh.yaml +++ b/.github/workflows/get-versions-from-gh.yaml @@ -17,7 +17,7 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: repository: community-scripts/ProxmoxVED ref: main @@ -70,13 +70,3 @@ jobs: if [ -n "$PR_NUMBER" ]; then gh pr review $PR_NUMBER --approve fi - - - name: Re-approve pull request after update - if: env.changed == 'true' - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - PR_NUMBER=$(gh pr list --head "update_versions" --json number --jq '.[].number') - if [ -n "$PR_NUMBER" ]; then - gh pr review $PR_NUMBER --approve - fi diff --git a/.github/workflows/scripts/get-gh-release.sh b/.github/workflows/scripts/get-gh-release.sh index ddd966df4..55ae9d8a6 100644 --- a/.github/workflows/scripts/get-gh-release.sh +++ b/.github/workflows/scripts/get-gh-release.sh @@ -7,7 +7,7 @@ TMP_FILE="releases_tmp.json" if [ -f "$OUTPUT_FILE" ]; then cp "$OUTPUT_FILE" "$TMP_FILE" else - echo "[]" > "$TMP_FILE" + echo "[]" >"$TMP_FILE" fi while IFS= read -r repo; do @@ -26,14 +26,15 @@ while IFS= read -r repo; do if [[ "$existing_version" != "$tag" ]]; then echo "New release for $repo: $tag" - jq --arg name "$repo" 'del(.[] | select(.name == $name))' "$TMP_FILE" > "$TMP_FILE.tmp" && mv "$TMP_FILE.tmp" "$TMP_FILE" + jq --arg name "$repo" 'del(.[] | select(.name == $name))' "$TMP_FILE" >"$TMP_FILE.tmp" && mv "$TMP_FILE.tmp" "$TMP_FILE" jq --arg name "$repo" --arg version "$tag" --arg date "$date" \ - '. += [{"name": $name, "version": $version, "date": $date}]' "$TMP_FILE" > "$TMP_FILE.tmp" && mv "$TMP_FILE.tmp" "$TMP_FILE" + '. += [{"name": $name, "version": $version, "date": $date}]' "$TMP_FILE" >"$TMP_FILE.tmp" && mv "$TMP_FILE.tmp" "$TMP_FILE" else echo "No change for $repo" fi -done < "$INPUT_FILE" +done <"$INPUT_FILE" -#mv "$TMP_FILE" "$OUTPUT_FILE" +mv "$TMP_FILE" "$OUTPUT_FILE" +echo "Updated $OUTPUT_FILE" From 08b8ac2589ed5b29783411d10f70e0f10a7139af Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 16 Jan 2026 08:39:44 +0100 Subject: [PATCH 04/10] frontend cleanup --- frontend/public/json/gwn-manager.json | 40 ------------------- .../public/json/transmission-openvpn.json | 40 ------------------- 2 files changed, 80 deletions(-) delete mode 100644 frontend/public/json/gwn-manager.json delete mode 100644 frontend/public/json/transmission-openvpn.json diff --git a/frontend/public/json/gwn-manager.json b/frontend/public/json/gwn-manager.json deleted file mode 100644 index 39f0f53ae..000000000 --- a/frontend/public/json/gwn-manager.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "GWN Manager", - "slug": "gwn-manager", - "categories": [ - 9 - ], - "date_created": "2025-12-02", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 8443, - "documentation": "https://documentation.grandstream.com/article-categories/gwn-mgmt/", - "website": "https://www.grandstream.com/products/networking-solutions/wi-fi-management/product/gwn-manager", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/gwn-manager.webp", - "config_path": "/gwn/conf/gwn.conf", - "description": "GWN Manager is a free on-premise enterprise-grade, management platform for Grandstream GWN series devices. Typically deployed on a customer’s private network, this flexible, scalable solution offers simplified configuration and management.", - "install_methods": [ - { - "type": "default", - "script": "ct/gwn-manager.sh", - "resources": { - "cpu": 2, - "ram": 6144, - "hdd": 8, - "os": "debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Installation package is pulled from GrandStream website. Installation may take a while.", - "type": "info" - } - ] -} diff --git a/frontend/public/json/transmission-openvpn.json b/frontend/public/json/transmission-openvpn.json deleted file mode 100644 index 319973502..000000000 --- a/frontend/public/json/transmission-openvpn.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Transmission-Openvpn", - "slug": "transmission-openvpn", - "categories": [ - 11 - ], - "date_created": "2025-09-04", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 9091, - "documentation": "https://haugene.github.io/docker-transmission-openvpn/", - "config_path": "/opt/transmission-openvpn/", - "website": "https://github.com/haugene/docker-transmission-openvpn", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/transmission.webp", - "description": "This project runs Transmission + OpenVPN natively in an LXC container, using the popular docker-transmission-openvpn image as a base. It ensures all torrent traffic is securely routed through a VPN tunnel, supports a wide range of VPN providers, and offers flexible configuration options", - "install_methods": [ - { - "type": "default", - "script": "ct/transmission-openvpn.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 8, - "os": "Debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "This application requires a VPN provider to work. Please refer to your VPN provider’s documentation for setting up OpenVPN.", - "type": "warning" - } - ] -} From 8f07df73af61a06d3ae1195271207fd4fcd1d9c1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 16 Jan 2026 08:54:00 +0100 Subject: [PATCH 05/10] Delete rybbit.json --- frontend/public/json/rybbit.json | 35 -------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 frontend/public/json/rybbit.json diff --git a/frontend/public/json/rybbit.json b/frontend/public/json/rybbit.json deleted file mode 100644 index 931e09443..000000000 --- a/frontend/public/json/rybbit.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Rybbit", - "slug": "rybbit", - "categories": [ - 9 - ], - "date_created": "2025-01-15", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3000, - "documentation": "https://github.com/rybbit-io/rybbit", - "website": "https://github.com/rybbit-io/rybbit", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/rybbit-light.webp", - "config_path": "/opt/rybbit", - "description": "Rybbit is a privacy-focused, open-source web analytics platform. Track your website visitors without compromising their privacy or using cookies.", - "install_methods": [ - { - "type": "default", - "script": "ct/rybbit.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 5, - "os": "debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} From f4fb75e9fe7107e81a6affe2f66d0f99ef78d1fe Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 16 Jan 2026 10:42:05 +0100 Subject: [PATCH 06/10] termix --- ct/termix.sh | 71 ++++++++++++++++++++ install/termix-install.sh | 134 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 ct/termix.sh create mode 100644 install/termix-install.sh diff --git a/ct/termix.sh b/ct/termix.sh new file mode 100644 index 000000000..9e6d2a0ee --- /dev/null +++ b/ct/termix.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/Termix-SSH/Termix + +APP="Termix" +var_tags="${var_tags:-ssh;terminal;management}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-10}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/termix ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "termix" "Termix-SSH/Termix"; then + msg_info "Stopping ${APP}" + systemctl stop termix + msg_ok "Stopped ${APP}" + + msg_info "Backing up Data" + cp -r /opt/termix/data /opt/termix_data_backup + msg_ok "Backed up Data" + + fetch_and_deploy_gh_release "termix" "Termix-SSH/Termix" + + msg_info "Restoring Data" + cp -r /opt/termix_data_backup/. /opt/termix/data + rm -rf /opt/termix_data_backup + msg_ok "Restored Data" + + msg_info "Rebuilding ${APP}" + cd /opt/termix + $STD npm install --ignore-scripts --force + $STD npm rebuild better-sqlite3 --force + $STD npm run build + $STD npm run build:backend + msg_ok "Rebuilt ${APP}" + + msg_info "Starting ${APP}" + systemctl start termix + msg_ok "Started ${APP}" + msg_ok "Updated successfully!" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" diff --git a/install/termix-install.sh b/install/termix-install.sh new file mode 100644 index 000000000..9bd9caf0f --- /dev/null +++ b/install/termix-install.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/Termix-SSH/Termix + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + build-essential \ + python3 \ + make \ + g++ \ + nginx \ + openssl \ + gettext-base +msg_ok "Installed Dependencies" + +NODE_VERSION="22" setup_nodejs +fetch_and_deploy_gh_release "termix" "Termix-SSH/Termix" + +msg_info "Building ${APPLICATION} (Patience)" +cd /opt/termix +export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 +$STD npm install --ignore-scripts --force +$STD npm rebuild better-sqlite3 --force +$STD npm run build +$STD npm run build:backend +mkdir -p /opt/termix/data /opt/termix/uploads +msg_ok "Built ${APPLICATION}" + +msg_info "Configuring Nginx" +cat <<'EOF' >/etc/nginx/sites-available/termix.conf +server { + listen 8080; + server_name _; + + add_header X-Content-Type-Options nosniff always; + add_header X-XSS-Protection "1; mode=block" always; + + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + root /opt/termix/dist; + expires 1y; + add_header Cache-Control "public, immutable"; + try_files $uri =404; + } + + location / { + root /opt/termix/dist; + index index.html; + try_files $uri $uri/ /index.html; + } + + location ~ ^/(users|version|releases|alerts|rbac|credentials|snippets|terminal|database|db|encryption|ssh|health)(/.*)?$ { + proxy_pass http://127.0.0.1:30001; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_connect_timeout 60s; + proxy_send_timeout 300s; + proxy_read_timeout 300s; + } + + location /ssh/websocket/ { + proxy_pass http://127.0.0.1:30002/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + proxy_read_timeout 86400s; + proxy_send_timeout 86400s; + proxy_buffering off; + } + + location ~ ^/status(/.*)?$ { + proxy_pass http://127.0.0.1:30005; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + location ~ ^/docker(/.*)?$ { + proxy_pass http://127.0.0.1:30007; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_connect_timeout 60s; + proxy_send_timeout 300s; + proxy_read_timeout 300s; + } +} +EOF +ln -sf /etc/nginx/sites-available/termix.conf /etc/nginx/sites-enabled/ +rm -f /etc/nginx/sites-enabled/default +$STD systemctl reload nginx +msg_ok "Configured Nginx" + +msg_info "Creating Service" +cat </etc/systemd/system/termix.service +[Unit] +Description=Termix Backend +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/termix +Environment=NODE_ENV=production +Environment=DATA_DIR=/opt/termix/data +Environment=PORT=30001 +ExecStart=/usr/bin/node /opt/termix/dist/backend/index.js +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now termix +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc From 5c030a948c40b9ccc4930f3f7e2bc68b3d0715c2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 16 Jan 2026 10:43:57 +0100 Subject: [PATCH 07/10] Create termix.json --- frontend/public/json/termix.json | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 frontend/public/json/termix.json diff --git a/frontend/public/json/termix.json b/frontend/public/json/termix.json new file mode 100644 index 000000000..e76fd9022 --- /dev/null +++ b/frontend/public/json/termix.json @@ -0,0 +1,35 @@ +{ + "name": "Termix", + "slug": "termix", + "categories": [ + 6 + ], + "date_created": "2026-01-16", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8080, + "documentation": "https://docs.termix.site/", + "website": "https://termix.site/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/termix.webp", + "config_path": "", + "description": "Termix is an open-source, self-hosted server management platform with SSH terminal access, SSH tunneling, remote file management, Docker management, and multi-platform support.", + "install_methods": [ + { + "type": "default", + "script": "ct/termix.sh", + "resources": { + "cpu": 4, + "ram": 4096, + "hdd": 10, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} From 957e3b06a4391cddc2c385fe5ec0a43e4a0df0e1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 16 Jan 2026 10:52:33 +0100 Subject: [PATCH 08/10] Revamp Termix install script and Nginx config Refactors the Termix installation script to separate frontend and backend build steps, improve dependency handling, and enhance directory setup. Replaces the Nginx site config with a more comprehensive standalone configuration, updates service port to 8080, and ensures Nginx is enabled alongside Termix. Also adds font cleanup and more robust asset copying for production. --- install/termix-install.sh | 253 ++++++++++++++++++++++++++++---------- 1 file changed, 191 insertions(+), 62 deletions(-) diff --git a/install/termix-install.sh b/install/termix-install.sh index 9bd9caf0f..9d4828167 100644 --- a/install/termix-install.sh +++ b/install/termix-install.sh @@ -27,83 +27,212 @@ msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "termix" "Termix-SSH/Termix" -msg_info "Building ${APPLICATION} (Patience)" +msg_info "Building ${APPLICATION} Frontend (Patience)" cd /opt/termix export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 + +find public/fonts -name "*.ttf" ! -name "*Regular.ttf" ! -name "*Bold.ttf" ! -name "*Italic.ttf" -delete 2>/dev/null || true + $STD npm install --ignore-scripts --force -$STD npm rebuild better-sqlite3 --force +$STD npm cache clean --force $STD npm run build +msg_ok "Built ${APPLICATION} Frontend" + +msg_info "Building ${APPLICATION} Backend" +$STD npm rebuild better-sqlite3 --force $STD npm run build:backend -mkdir -p /opt/termix/data /opt/termix/uploads -msg_ok "Built ${APPLICATION}" +msg_ok "Built ${APPLICATION} Backend" + +msg_info "Setting up Production Dependencies" +cd /opt/termix +$STD npm ci --only=production --ignore-scripts --force +$STD npm rebuild better-sqlite3 bcryptjs --force +$STD npm cache clean --force +msg_ok "Set up Production Dependencies" + +msg_info "Setting up Directories" +mkdir -p /opt/termix/data \ + /opt/termix/uploads \ + /opt/termix/html \ + /opt/termix/nginx \ + /opt/termix/nginx/logs \ + /opt/termix/nginx/cache \ + /opt/termix/nginx/client_body + +cp -r /opt/termix/dist/* /opt/termix/html/ 2>/dev/null || true +cp -r /opt/termix/src/locales /opt/termix/html/locales 2>/dev/null || true +cp -r /opt/termix/public/fonts /opt/termix/html/fonts 2>/dev/null || true +msg_ok "Set up Directories" msg_info "Configuring Nginx" -cat <<'EOF' >/etc/nginx/sites-available/termix.conf -server { - listen 8080; - server_name _; +cat <<'NGINXEOF' >/etc/nginx/sites-available/termix.conf +pid /opt/termix/nginx/nginx.pid; +error_log /opt/termix/nginx/logs/error.log warn; - add_header X-Content-Type-Options nosniff always; - add_header X-XSS-Protection "1; mode=block" always; +events { + worker_connections 1024; +} - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { - root /opt/termix/dist; - expires 1y; - add_header Cache-Control "public, immutable"; - try_files $uri =404; - } +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + access_log /opt/termix/nginx/logs/access.log; - location / { - root /opt/termix/dist; - index index.html; - try_files $uri $uri/ /index.html; - } + client_body_temp_path /opt/termix/nginx/client_body; + proxy_temp_path /opt/termix/nginx/proxy_temp; - location ~ ^/(users|version|releases|alerts|rbac|credentials|snippets|terminal|database|db|encryption|ssh|health)(/.*)?$ { - proxy_pass http://127.0.0.1:30001; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_connect_timeout 60s; - proxy_send_timeout 300s; - proxy_read_timeout 300s; - } + sendfile on; + keepalive_timeout 65; + client_header_timeout 300s; - location /ssh/websocket/ { - proxy_pass http://127.0.0.1:30002/; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $host; - proxy_cache_bypass $http_upgrade; - proxy_read_timeout 86400s; - proxy_send_timeout 86400s; - proxy_buffering off; - } + server { + listen 8080; + server_name _; - location ~ ^/status(/.*)?$ { - proxy_pass http://127.0.0.1:30005; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - } + add_header X-Content-Type-Options nosniff always; + add_header X-XSS-Protection "1; mode=block" always; - location ~ ^/docker(/.*)?$ { - proxy_pass http://127.0.0.1:30007; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_connect_timeout 60s; - proxy_send_timeout 300s; - proxy_read_timeout 300s; + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + root /opt/termix/html; + expires 1y; + add_header Cache-Control "public, immutable"; + try_files $uri =404; + } + + location / { + root /opt/termix/html; + index index.html; + try_files $uri $uri/ /index.html; + } + + location ~ ^/users(/.*)?$ { + proxy_pass http://127.0.0.1:30001; + 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; + } + + location ~ ^/(version|releases|alerts|rbac|credentials|snippets|terminal|encryption)(/.*)?$ { + proxy_pass http://127.0.0.1:30001; + 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; + } + + location ~ ^/(database|db)(/.*)?$ { + client_max_body_size 5G; + client_body_timeout 300s; + proxy_pass http://127.0.0.1:30001; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_connect_timeout 60s; + proxy_send_timeout 300s; + proxy_read_timeout 300s; + proxy_request_buffering off; + proxy_buffering off; + } + + location /ssh/ { + proxy_pass http://127.0.0.1:30001; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + location /ssh/websocket/ { + proxy_pass http://127.0.0.1:30002/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + proxy_set_header X-Real-IP $remote_addr; + proxy_read_timeout 86400s; + proxy_send_timeout 86400s; + proxy_buffering off; + proxy_request_buffering off; + } + + location /ssh/tunnel/ { + proxy_pass http://127.0.0.1:30003; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + location /ssh/file_manager/ssh/ { + client_max_body_size 5G; + client_body_timeout 300s; + proxy_pass http://127.0.0.1:30004; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_connect_timeout 60s; + proxy_send_timeout 300s; + proxy_read_timeout 300s; + proxy_request_buffering off; + proxy_buffering off; + } + + location ~ ^/ssh/file_manager/(recent|pinned|shortcuts)$ { + proxy_pass http://127.0.0.1:30001; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + location /health { + proxy_pass http://127.0.0.1:30001; + proxy_http_version 1.1; + proxy_set_header Host $host; + } + + location ~ ^/(status|metrics)(/.*)?$ { + proxy_pass http://127.0.0.1:30005; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + location ~ ^/(uptime|activity)(/.*)?$ { + proxy_pass http://127.0.0.1:30006; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + location ^~ /docker/console/ { + proxy_pass http://127.0.0.1:30008/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + proxy_read_timeout 86400s; + proxy_send_timeout 86400s; + proxy_buffering off; + proxy_request_buffering off; + } + + location ~ ^/docker(/.*)?$ { + proxy_pass http://127.0.0.1:30007; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_connect_timeout 60s; + proxy_send_timeout 300s; + proxy_read_timeout 300s; + } } } -EOF -ln -sf /etc/nginx/sites-available/termix.conf /etc/nginx/sites-enabled/ +NGINXEOF rm -f /etc/nginx/sites-enabled/default -$STD systemctl reload nginx +rm -f /etc/nginx/nginx.conf +ln -sf /etc/nginx/sites-available/termix.conf /etc/nginx/nginx.conf msg_ok "Configured Nginx" msg_info "Creating Service" @@ -118,7 +247,7 @@ User=root WorkingDirectory=/opt/termix Environment=NODE_ENV=production Environment=DATA_DIR=/opt/termix/data -Environment=PORT=30001 +Environment=PORT=8080 ExecStart=/usr/bin/node /opt/termix/dist/backend/index.js Restart=on-failure RestartSec=5 @@ -126,7 +255,7 @@ RestartSec=5 [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now termix +systemctl enable -q --now termix nginx msg_ok "Created Service" motd_ssh From 08cb68267a1a667ee6c21e497b472404f899a5b4 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> Date: Fri, 16 Jan 2026 11:08:53 +0100 Subject: [PATCH 09/10] Add unifi-os-server.json for Unifi OS Server details --- frontend/public/json/unifi-os-server.json | 35 +++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 frontend/public/json/unifi-os-server.json diff --git a/frontend/public/json/unifi-os-server.json b/frontend/public/json/unifi-os-server.json new file mode 100644 index 000000000..cdd09cc70 --- /dev/null +++ b/frontend/public/json/unifi-os-server.json @@ -0,0 +1,35 @@ +{ + "name": "Unifi OS Server", + "slug": "unifi-os-server", + "categories": [ + 2 + ], + "date_created": "2026-01-16", + "type": "vm", + "updateable": true, + "privileged": false, + "interface_port": 11443, + "documentation": "https://help.ui.com/hc/en-us", + "website": "https://www.ui.com/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/ubiquiti-unifi.webp", + "config_path": "", + "description": "Unifi OS Server is the operating system that powers Ubiquiti's UniFi line of network devices. It provides a centralized platform for managing and monitoring UniFi access points, switches, and security gateways, offering features such as network configuration, device provisioning, and performance analytics.", + "install_methods": [ + { + "type": "default", + "script": "ct/unifi-os-server.sh", + "resources": { + "cpu": 2, + "ram": 4096, + "hdd": 20, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} From f04946cae8e63238db8d195de3cf9a6295c0bd60 Mon Sep 17 00:00:00 2001 From: juronja <101410098+juronja@users.noreply.github.com> Date: Fri, 16 Jan 2026 13:20:18 +0100 Subject: [PATCH 10/10] Adding source in file header Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- vm/truenas-vm.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/vm/truenas-vm.sh b/vm/truenas-vm.sh index b81525ee2..54b5652f9 100644 --- a/vm/truenas-vm.sh +++ b/vm/truenas-vm.sh @@ -3,6 +3,7 @@ # Copyright (c) 2021-2026 community-scripts ORG # Author: juronja # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.truenas.com/truenas-community-edition/ source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/api.func)