mirror of
				https://github.com/community-scripts/ProxmoxVE.git
				synced 2025-11-04 02:12:49 +00:00 
			
		
		
		
	Compare commits
	
		
			26 Commits
		
	
	
		
			2025-04-18
			...
			2025-04-23
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					14313687c9 | ||
| 
						 | 
					d28f33eb91 | ||
| 
						 | 
					0bab7c06a6 | ||
| 
						 | 
					90722de17d | ||
| 
						 | 
					8a4dfa0cc7 | ||
| 
						 | 
					e3c2fba599 | ||
| 
						 | 
					563e73e65e | ||
| 
						 | 
					235690658c | ||
| 
						 | 
					8dddea132c | ||
| 
						 | 
					46ca78abc8 | ||
| 
						 | 
					933539ca7c | ||
| 
						 | 
					2c46695b72 | ||
| 
						 | 
					f46c3005b3 | ||
| 
						 | 
					bad84322ee | ||
| 
						 | 
					3b12c0ca62 | ||
| 
						 | 
					2583c110d2 | ||
| 
						 | 
					22c960c99c | ||
| 
						 | 
					fcb674b755 | ||
| 
						 | 
					0373324653 | ||
| 
						 | 
					3297df1d50 | ||
| 
						 | 
					80f1330a75 | ||
| 
						 | 
					05fda7e56a | ||
| 
						 | 
					73a42eaaae | ||
| 
						 | 
					1ae420eef5 | ||
| 
						 | 
					96fc1ea91d | ||
| 
						 | 
					432c6837e7 | 
							
								
								
									
										107
									
								
								.github/workflows/close-discussion.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										107
									
								
								.github/workflows/close-discussion.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,8 +1,13 @@
 | 
			
		||||
name: Close Discussion on PR Merge
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  pull_request:
 | 
			
		||||
    types: [closed]
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - main
 | 
			
		||||
 | 
			
		||||
permissions:
 | 
			
		||||
  contents: read
 | 
			
		||||
  discussions: write
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  close-discussion:
 | 
			
		||||
@@ -11,56 +16,82 @@ jobs:
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout Repository
 | 
			
		||||
        uses: actions/checkout@v4
 | 
			
		||||
        with:
 | 
			
		||||
          repository: community-scripts/ProxmoxVE
 | 
			
		||||
          ref: main
 | 
			
		||||
          token: ${{ secrets.GITHUB_TOKEN }}  
 | 
			
		||||
 | 
			
		||||
      - name: Set Up Node.js
 | 
			
		||||
        uses: actions/setup-node@v4
 | 
			
		||||
        with:
 | 
			
		||||
          node-version: "20"
 | 
			
		||||
 | 
			
		||||
      - name: Install Dependencies
 | 
			
		||||
        run: npm install zx @octokit/graphql
 | 
			
		||||
 | 
			
		||||
      - name: Close Discussion
 | 
			
		||||
        env:
 | 
			
		||||
          GITHUB_TOKEN: ${{ secrets.PAT_MICHEL }}
 | 
			
		||||
          PR_BODY: ${{ github.event.pull_request.body }}
 | 
			
		||||
          PR_NUMBER: ${{ github.event.pull_request.number }}
 | 
			
		||||
          REPO_OWNER: ${{ github.repository_owner }}
 | 
			
		||||
          REPO_NAME: ${{ github.event.repository.name }}
 | 
			
		||||
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
          GITHUB_SHA: ${{ github.sha }}
 | 
			
		||||
          GITHUB_REPOSITORY: ${{ github.repository }}
 | 
			
		||||
        run: |
 | 
			
		||||
          npx zx << 'EOF'
 | 
			
		||||
          import { graphql } from "@octokit/graphql";
 | 
			
		||||
          (async function() {
 | 
			
		||||
 | 
			
		||||
          (async function () {
 | 
			
		||||
            try {
 | 
			
		||||
              const token = process.env.GITHUB_TOKEN;
 | 
			
		||||
              const prBody = process.env.PR_BODY;
 | 
			
		||||
              const prNumber = process.env.PR_NUMBER;
 | 
			
		||||
              const owner = process.env.REPO_OWNER;
 | 
			
		||||
              const repo = process.env.REPO_NAME;
 | 
			
		||||
              const commitSha = process.env.GITHUB_SHA;
 | 
			
		||||
              const [owner, repo] = process.env.GITHUB_REPOSITORY.split("/");
 | 
			
		||||
 | 
			
		||||
              if (!token || !prBody || !prNumber || !owner || !repo) {
 | 
			
		||||
              if (!token || !commitSha || !owner || !repo) {
 | 
			
		||||
                console.log("Missing required environment variables.");
 | 
			
		||||
                process.exit(1);
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              const graphqlWithAuth = graphql.defaults({
 | 
			
		||||
                headers: { authorization: `Bearer ${token}` },
 | 
			
		||||
              });
 | 
			
		||||
 | 
			
		||||
              // Find PR from commit SHA
 | 
			
		||||
              const searchQuery = `
 | 
			
		||||
                query($owner: String!, $repo: String!, $sha: GitObjectID!) {
 | 
			
		||||
                  repository(owner: $owner, name: $repo) {
 | 
			
		||||
                    object(oid: $sha) {
 | 
			
		||||
                      ... on Commit {
 | 
			
		||||
                        associatedPullRequests(first: 1) {
 | 
			
		||||
                          nodes {
 | 
			
		||||
                            number
 | 
			
		||||
                            body
 | 
			
		||||
                          }
 | 
			
		||||
                        }
 | 
			
		||||
                      }
 | 
			
		||||
                    }
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
              `;
 | 
			
		||||
 | 
			
		||||
              const prResult = await graphqlWithAuth(searchQuery, {
 | 
			
		||||
                owner,
 | 
			
		||||
                repo,
 | 
			
		||||
                sha: commitSha,
 | 
			
		||||
              });
 | 
			
		||||
 | 
			
		||||
              const pr = prResult.repository.object.associatedPullRequests.nodes[0];
 | 
			
		||||
              if (!pr) {
 | 
			
		||||
                console.log("No PR found for this commit.");
 | 
			
		||||
                return;
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              const prNumber = pr.number;
 | 
			
		||||
              const prBody = pr.body;
 | 
			
		||||
 | 
			
		||||
              const match = prBody.match(/#(\d+)/);
 | 
			
		||||
              if (!match) {
 | 
			
		||||
                console.log("No discussion ID found in PR body.");
 | 
			
		||||
                return;
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              const discussionNumber = match[1];
 | 
			
		||||
 | 
			
		||||
              console.log(`Extracted Discussion Number: ${discussionNumber}`);
 | 
			
		||||
              console.log(`PR Number: ${prNumber}`);
 | 
			
		||||
              console.log(`Repository: ${owner}/${repo}`);
 | 
			
		||||
 | 
			
		||||
              const graphqlWithAuth = graphql.defaults({
 | 
			
		||||
                headers: { authorization: `Bearer ${token}` },
 | 
			
		||||
              });
 | 
			
		||||
 | 
			
		||||
              // Fetch GraphQL discussion ID
 | 
			
		||||
              const discussionQuery = `
 | 
			
		||||
                query($owner: String!, $repo: String!, $number: Int!) {
 | 
			
		||||
                  repository(owner: $owner, name: $repo) {
 | 
			
		||||
@@ -70,21 +101,26 @@ jobs:
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
              `;
 | 
			
		||||
              
 | 
			
		||||
              const discussionResponse = await graphqlWithAuth(discussionQuery, {
 | 
			
		||||
 | 
			
		||||
              //
 | 
			
		||||
              try {
 | 
			
		||||
                const discussionResponse = await graphqlWithAuth(discussionQuery, {
 | 
			
		||||
                  owner,
 | 
			
		||||
                  repo,
 | 
			
		||||
                  number: parseInt(discussionNumber, 10),
 | 
			
		||||
              });              
 | 
			
		||||
              
 | 
			
		||||
              const discussionQLId = discussionResponse.repository.discussion.id;
 | 
			
		||||
              if (!discussionQLId) {
 | 
			
		||||
                console.log("Failed to fetch discussion GraphQL ID.");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                const discussionQLId = discussionResponse.repository.discussion.id;
 | 
			
		||||
                if (!discussionQLId) {
 | 
			
		||||
                  console.log("Failed to fetch discussion GraphQL ID.");
 | 
			
		||||
                  return;
 | 
			
		||||
                }
 | 
			
		||||
              } catch (error) {
 | 
			
		||||
                console.error("Discussion not found or error occurred while fetching discussion:", error);
 | 
			
		||||
                return;
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              console.log(`GraphQL Discussion ID: ${discussionQLId}`);
 | 
			
		||||
              
 | 
			
		||||
              // Post comment
 | 
			
		||||
              const commentMutation = `
 | 
			
		||||
                mutation($discussionId: ID!, $body: String!) {
 | 
			
		||||
                  addDiscussionComment(input: { discussionId: $discussionId, body: $body }) {
 | 
			
		||||
@@ -106,6 +142,7 @@ jobs:
 | 
			
		||||
 | 
			
		||||
              console.log(`Comment Posted Successfully! Comment ID: ${commentId}`);
 | 
			
		||||
 | 
			
		||||
              // Mark comment as answer
 | 
			
		||||
              const markAnswerMutation = `
 | 
			
		||||
                mutation($id: ID!) {
 | 
			
		||||
                  markDiscussionCommentAsAnswer(input: { id: $id }) {
 | 
			
		||||
@@ -120,7 +157,7 @@ jobs:
 | 
			
		||||
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
              console.error("Error:", error);
 | 
			
		||||
              return;
 | 
			
		||||
              process.exit(1);
 | 
			
		||||
            }
 | 
			
		||||
          })();
 | 
			
		||||
          EOF
 | 
			
		||||
          EOF
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										51
									
								
								.github/workflows/close-ttek-issues.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								.github/workflows/close-ttek-issues.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
name: Auto-Close tteck Issues
 | 
			
		||||
on:
 | 
			
		||||
  issues:
 | 
			
		||||
    types: [opened]
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  close_tteck_issues:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Auto-close if tteck script detected
 | 
			
		||||
        uses: actions/github-script@v7
 | 
			
		||||
        with:
 | 
			
		||||
          script: |
 | 
			
		||||
            const issue = context.payload.issue;
 | 
			
		||||
            const content = `${issue.title}\n${issue.body}`;
 | 
			
		||||
            const issueNumber = issue.number;
 | 
			
		||||
 | 
			
		||||
            // Check for tteck script mention
 | 
			
		||||
            if (content.includes("tteck") || content.includes("tteck/Proxmox")) {
 | 
			
		||||
              const message = `Hello, it looks like you are referencing the **old tteck repo**.
 | 
			
		||||
 | 
			
		||||
            This repository is no longer used for active scripts.
 | 
			
		||||
            **Please update your bookmarks** and use: [https://helper-scripts.com](https://helper-scripts.com)
 | 
			
		||||
                    
 | 
			
		||||
            Also make sure your Bash command starts with:
 | 
			
		||||
            \`\`\`bash
 | 
			
		||||
            bash <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/...)
 | 
			
		||||
            \`\`\`
 | 
			
		||||
            
 | 
			
		||||
            This issue is being closed automatically.`;
 | 
			
		||||
 | 
			
		||||
              await github.rest.issues.createComment({
 | 
			
		||||
                ...context.repo,
 | 
			
		||||
                issue_number: issueNumber,
 | 
			
		||||
                body: message
 | 
			
		||||
              });
 | 
			
		||||
 | 
			
		||||
              // Optionally apply a label like "not planned"
 | 
			
		||||
              await github.rest.issues.addLabels({
 | 
			
		||||
                ...context.repo,
 | 
			
		||||
                issue_number: issueNumber,
 | 
			
		||||
                labels: ["not planned"]
 | 
			
		||||
              });
 | 
			
		||||
 | 
			
		||||
              // Close the issue
 | 
			
		||||
              await github.rest.issues.update({
 | 
			
		||||
                ...context.repo,
 | 
			
		||||
                issue_number: issueNumber,
 | 
			
		||||
                state: "closed"
 | 
			
		||||
              });
 | 
			
		||||
            }
 | 
			
		||||
							
								
								
									
										61
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -14,6 +14,67 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
 | 
			
		||||
All LXC instances created using this repository come pre-installed with Midnight Commander, which is a command-line tool (`mc`) that offers a user-friendly file and directory management interface for the terminal environment.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 2025-04-23
 | 
			
		||||
 | 
			
		||||
### 🚀 Updated Scripts
 | 
			
		||||
 | 
			
		||||
  - #### 🐞 Bug Fixes
 | 
			
		||||
 | 
			
		||||
    - Zipline: Add new ENV Variable and Change Update [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3997](https://github.com/community-scripts/ProxmoxVE/pull/3997))
 | 
			
		||||
    - karakeep: use nightly channel for yt-dlp [@vhsdream](https://github.com/vhsdream) ([#3992](https://github.com/community-scripts/ProxmoxVE/pull/3992))
 | 
			
		||||
 | 
			
		||||
### 🧰 Maintenance
 | 
			
		||||
 | 
			
		||||
  - #### 📂 Github
 | 
			
		||||
 | 
			
		||||
    - Fix Workflow to close discussions [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3999](https://github.com/community-scripts/ProxmoxVE/pull/3999))
 | 
			
		||||
 | 
			
		||||
## 2025-04-22
 | 
			
		||||
 | 
			
		||||
### 🆕 New Scripts
 | 
			
		||||
 | 
			
		||||
  - reactive-resume ([#3980](https://github.com/community-scripts/ProxmoxVE/pull/3980))
 | 
			
		||||
 | 
			
		||||
### 🚀 Updated Scripts
 | 
			
		||||
 | 
			
		||||
  - #### 🐞 Bug Fixes
 | 
			
		||||
 | 
			
		||||
    - wger: Fix a bug in update procedure and general code maintenance [@tremor021](https://github.com/tremor021) ([#3974](https://github.com/community-scripts/ProxmoxVE/pull/3974))
 | 
			
		||||
 | 
			
		||||
### 🧰 Maintenance
 | 
			
		||||
 | 
			
		||||
  - #### 📂 Github
 | 
			
		||||
 | 
			
		||||
    - Add workflow to close ttek Repo relatate issues [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3981](https://github.com/community-scripts/ProxmoxVE/pull/3981))
 | 
			
		||||
 | 
			
		||||
### 🌐 Website
 | 
			
		||||
 | 
			
		||||
  - #### 🐞 Bug Fixes
 | 
			
		||||
 | 
			
		||||
    - Fix Turnkey Source Link in Button Component [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3978](https://github.com/community-scripts/ProxmoxVE/pull/3978))
 | 
			
		||||
 | 
			
		||||
  - #### 📝 Script Information
 | 
			
		||||
 | 
			
		||||
    - qBittorrent: Update web page [@tremor021](https://github.com/tremor021) ([#3969](https://github.com/community-scripts/ProxmoxVE/pull/3969))
 | 
			
		||||
 | 
			
		||||
## 2025-04-19
 | 
			
		||||
 | 
			
		||||
### 🆕 New Scripts
 | 
			
		||||
 | 
			
		||||
  - LXC Iptag [@DesertGamer](https://github.com/DesertGamer) ([#3531](https://github.com/community-scripts/ProxmoxVE/pull/3531))
 | 
			
		||||
 | 
			
		||||
### 🚀 Updated Scripts
 | 
			
		||||
 | 
			
		||||
  - #### 🐞 Bug Fixes
 | 
			
		||||
 | 
			
		||||
    - seelf: Add missing gpg dependency [@tremor021](https://github.com/tremor021) ([#3953](https://github.com/community-scripts/ProxmoxVE/pull/3953))
 | 
			
		||||
 | 
			
		||||
### 🌐 Website
 | 
			
		||||
 | 
			
		||||
  - #### 📝 Script Information
 | 
			
		||||
 | 
			
		||||
    - Tailscale: Clarify tailscale script instruction on website [@tremor021](https://github.com/tremor021) ([#3952](https://github.com/community-scripts/ProxmoxVE/pull/3952))
 | 
			
		||||
 | 
			
		||||
## 2025-04-18
 | 
			
		||||
 | 
			
		||||
### 🚀 Updated Scripts
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								ct/headers/reactive-resume
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								ct/headers/reactive-resume
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
    ____                  __  _                  ____                               
 | 
			
		||||
   / __ \___  ____ ______/ /_(_)   _____        / __ \___  _______  ______ ___  ___ 
 | 
			
		||||
  / /_/ / _ \/ __ `/ ___/ __/ / | / / _ \______/ /_/ / _ \/ ___/ / / / __ `__ \/ _ \
 | 
			
		||||
 / _, _/  __/ /_/ / /__/ /_/ /| |/ /  __/_____/ _, _/  __(__  ) /_/ / / / / / /  __/
 | 
			
		||||
/_/ |_|\___/\__,_/\___/\__/_/ |___/\___/     /_/ |_|\___/____/\__,_/_/ /_/ /_/\___/ 
 | 
			
		||||
                                                                                    
 | 
			
		||||
@@ -33,6 +33,9 @@ function update_script() {
 | 
			
		||||
    msg_info "Stopping Services"
 | 
			
		||||
    systemctl stop karakeep-web karakeep-workers karakeep-browser
 | 
			
		||||
    msg_ok "Stopped Services"
 | 
			
		||||
    msg_info "Updating yt-dlp"
 | 
			
		||||
    $STD yt-dlp --update-to nightly
 | 
			
		||||
    msg_ok "Updated yt-dlp"
 | 
			
		||||
    msg_info "Updating ${APP} to v${RELEASE}"
 | 
			
		||||
    if [[ $(corepack -v) < "0.31.0" ]]; then
 | 
			
		||||
      $STD npm install -g corepack@0.31.0
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										106
									
								
								ct/reactive-resume.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								ct/reactive-resume.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,106 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
 | 
			
		||||
# Copyright (c) 2021-2025 community-scripts ORG
 | 
			
		||||
# Author: vhsdream
 | 
			
		||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
 | 
			
		||||
# Source: https://rxresu.me
 | 
			
		||||
 | 
			
		||||
APP="Reactive-Resume"
 | 
			
		||||
var_tags="${var_tags:-documents}"
 | 
			
		||||
var_cpu="${var_cpu:-2}"
 | 
			
		||||
var_ram="${var_ram:-3072}"
 | 
			
		||||
var_disk="${var_disk:-8}"
 | 
			
		||||
var_os="${var_os:-debian}"
 | 
			
		||||
var_version="${var_version:-12}"
 | 
			
		||||
var_unprivileged="${var_unprivileged:-1}"
 | 
			
		||||
 | 
			
		||||
header_info "$APP"
 | 
			
		||||
variables
 | 
			
		||||
color
 | 
			
		||||
catch_errors
 | 
			
		||||
 | 
			
		||||
function update_script() {
 | 
			
		||||
    header_info
 | 
			
		||||
    check_container_storage
 | 
			
		||||
    check_container_resources
 | 
			
		||||
 | 
			
		||||
    if [[ ! -f /etc/systemd/system/Reactive-Resume.service ]]; then
 | 
			
		||||
        msg_error "No ${APP} Installation Found!"
 | 
			
		||||
        exit
 | 
			
		||||
    fi
 | 
			
		||||
    RELEASE=$(curl -fsSL https://api.github.com/repos/AmruthPillai/Reactive-Resume/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }')
 | 
			
		||||
    if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then
 | 
			
		||||
        msg_info "Stopping services"
 | 
			
		||||
        systemctl stop Reactive-Resume
 | 
			
		||||
        msg_ok "Stopped services"
 | 
			
		||||
 | 
			
		||||
        msg_info "Updating $APP to v${RELEASE}"
 | 
			
		||||
        cp /opt/${APP}/.env /opt/rxresume.env
 | 
			
		||||
        res_tmp=$(mktemp)
 | 
			
		||||
        rm -rf /opt/${APP}
 | 
			
		||||
        curl -fsSL "https://github.com/AmruthPillai/Reactive-Resume/archive/refs/tags/v${RELEASE}.zip" -O $res_tmp
 | 
			
		||||
        unzip -q $res_tmp
 | 
			
		||||
        mv ${APP}-${RELEASE}/ /opt/${APP}
 | 
			
		||||
        cd /opt/${APP}
 | 
			
		||||
        export PUPPETEER_SKIP_DOWNLOAD="true"
 | 
			
		||||
        export NEXT_TELEMETRY_DISABLED=1
 | 
			
		||||
        export CI="true"
 | 
			
		||||
        export NODE_ENV="production"
 | 
			
		||||
        $STD pnpm install --frozen-lockfile
 | 
			
		||||
        $STD pnpm run build
 | 
			
		||||
        $STD pnpm run prisma:generate
 | 
			
		||||
        mv /opt/rxresume.env /opt/${APP}/.env
 | 
			
		||||
        msg_ok "Updated $APP to v${RELEASE}"
 | 
			
		||||
 | 
			
		||||
        msg_info "Updating Minio"
 | 
			
		||||
        systemctl stop minio
 | 
			
		||||
        cd /tmp
 | 
			
		||||
        curl -fsSL https://dl.min.io/server/minio/release/linux-amd64/minio.deb -o minio.deb
 | 
			
		||||
        $STD dpkg -i minio.deb
 | 
			
		||||
        msg_ok "Updated Minio"
 | 
			
		||||
 | 
			
		||||
        msg_info "Updating Browserless (Patience)"
 | 
			
		||||
        systemctl stop browserless
 | 
			
		||||
        cp /opt/browserless/.env /opt/browserless.env
 | 
			
		||||
        rm -rf browserless
 | 
			
		||||
        brwsr_tmp=$(mktemp)
 | 
			
		||||
        TAG=$(curl -fsSL https://api.github.com/repos/browserless/browserless/tags?per_page=1 | grep "name" | awk '{print substr($2, 3, length($2)-4) }')
 | 
			
		||||
        curl -fsSL https://github.com/browserless/browserless/archive/refs/tags/v${TAG}.zip -O $brwsr_tmp
 | 
			
		||||
        unzip -q $brwsr_tmp
 | 
			
		||||
        mv browserless-${TAG}/ /opt/browserless
 | 
			
		||||
        cd /opt/browserless
 | 
			
		||||
        $STD npm install
 | 
			
		||||
        rm -rf src/routes/{chrome,edge,firefox,webkit}
 | 
			
		||||
        $STD node_modules/playwright-core/cli.js install --with-deps chromium
 | 
			
		||||
        $STD npm run build
 | 
			
		||||
        $STD npm run build:function
 | 
			
		||||
        $STD npm prune production
 | 
			
		||||
        mv /opt/browserless.env /opt/browserless/.env
 | 
			
		||||
        msg_ok "Updated Browserless"
 | 
			
		||||
 | 
			
		||||
        msg_info "Restarting services"
 | 
			
		||||
        systemctl start minio Reactive-Resume browserless
 | 
			
		||||
        msg_ok "Restarted services"
 | 
			
		||||
 | 
			
		||||
        msg_info "Cleaning Up"
 | 
			
		||||
        rm -f /tmp/minio.deb
 | 
			
		||||
        rm -f $brwsr_tmp
 | 
			
		||||
        rm -f $res_tmp
 | 
			
		||||
        msg_ok "Cleanup Completed"
 | 
			
		||||
 | 
			
		||||
        echo "${RELEASE}" >/opt/${APP}_version.txt
 | 
			
		||||
        msg_ok "Update Successful"
 | 
			
		||||
    else
 | 
			
		||||
        msg_ok "No update required. ${APP} is already at v${RELEASE}"
 | 
			
		||||
    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}:3000${CL}"
 | 
			
		||||
							
								
								
									
										79
									
								
								ct/wger.sh
									
									
									
									
									
								
							
							
						
						
									
										79
									
								
								ct/wger.sh
									
									
									
									
									
								
							@@ -20,46 +20,45 @@ color
 | 
			
		||||
catch_errors
 | 
			
		||||
 | 
			
		||||
function update_script() {
 | 
			
		||||
    header_info
 | 
			
		||||
    check_container_storage
 | 
			
		||||
    check_container_resources
 | 
			
		||||
    if [[ ! -d /home/wger ]]; then
 | 
			
		||||
        msg_error "No ${APP} Installation Found!"
 | 
			
		||||
        exit
 | 
			
		||||
    fi
 | 
			
		||||
    RELEASE=$(curl -fsSL https://api.github.com/repos/wger-project/wger/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}')
 | 
			
		||||
    if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then
 | 
			
		||||
        msg_info "Stopping $APP"
 | 
			
		||||
        systemctl stop wger
 | 
			
		||||
        msg_ok "Stopped $APP"
 | 
			
		||||
 | 
			
		||||
        msg_info "Updating $APP to v${RELEASE}"
 | 
			
		||||
        temp_file=$(mktemp)
 | 
			
		||||
        cd $temp_file
 | 
			
		||||
curl -fsSL "https://github.com/wger-project/wger/archive/refs/tags/$RELEASE.tar.gz" -o "$temp_file"
 | 
			
		||||
        tar xzf $temp_file
 | 
			
		||||
        cp -rf wger-$RELEASE/* /home/wger/src
 | 
			
		||||
        cd /home/wger/src
 | 
			
		||||
        python3 manage.py migrate &>/dev/null
 | 
			
		||||
        yarn install &>/dev/null
 | 
			
		||||
        yarn build:css:sass &>/dev/null
 | 
			
		||||
        python3 manage.py collectstatic --noinput &>/dev/null
 | 
			
		||||
        echo "${RELEASE}" >/opt/${APP}_version.txt
 | 
			
		||||
        msg_ok "Updated $APP to v${RELEASE}"
 | 
			
		||||
 | 
			
		||||
        msg_info "Starting $APP"
 | 
			
		||||
        systemctl start wger
 | 
			
		||||
        msg_ok "Started $APP"
 | 
			
		||||
 | 
			
		||||
        msg_info "Cleaning Up"
 | 
			
		||||
        rm -rf $temp_file
 | 
			
		||||
        msg_ok "Cleanup Completed"
 | 
			
		||||
 | 
			
		||||
        msg_ok "Update Successful"
 | 
			
		||||
    else
 | 
			
		||||
        msg_ok "No update required. ${APP} is already at v${RELEASE}"
 | 
			
		||||
    fi
 | 
			
		||||
  header_info
 | 
			
		||||
  check_container_storage
 | 
			
		||||
  check_container_resources
 | 
			
		||||
  if [[ ! -d /home/wger ]]; then
 | 
			
		||||
    msg_error "No ${APP} Installation Found!"
 | 
			
		||||
    exit
 | 
			
		||||
  fi
 | 
			
		||||
  RELEASE=$(curl -fsSL https://api.github.com/repos/wger-project/wger/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}')
 | 
			
		||||
  if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then
 | 
			
		||||
    msg_info "Stopping $APP"
 | 
			
		||||
    systemctl stop wger
 | 
			
		||||
    msg_ok "Stopped $APP"
 | 
			
		||||
 | 
			
		||||
    msg_info "Updating $APP to v${RELEASE}"
 | 
			
		||||
    temp_file=$(mktemp)
 | 
			
		||||
    curl -fsSL "https://github.com/wger-project/wger/archive/refs/tags/$RELEASE.tar.gz" -o "$temp_file"
 | 
			
		||||
    tar xzf "$temp_file"
 | 
			
		||||
    cp -rf wger-"$RELEASE"/* /home/wger/src
 | 
			
		||||
    cd /home/wger/src
 | 
			
		||||
    python3 manage.py migrate &>/dev/null
 | 
			
		||||
    yarn install &>/dev/null
 | 
			
		||||
    yarn build:css:sass &>/dev/null
 | 
			
		||||
    python3 manage.py collectstatic --noinput &>/dev/null
 | 
			
		||||
    echo "${RELEASE}" >/opt/${APP}_version.txt
 | 
			
		||||
    msg_ok "Updated $APP to v${RELEASE}"
 | 
			
		||||
 | 
			
		||||
    msg_info "Starting $APP"
 | 
			
		||||
    systemctl start wger
 | 
			
		||||
    msg_ok "Started $APP"
 | 
			
		||||
 | 
			
		||||
    msg_info "Cleaning Up"
 | 
			
		||||
    rm -rf "$temp_file"
 | 
			
		||||
    msg_ok "Cleanup Completed"
 | 
			
		||||
 | 
			
		||||
    msg_ok "Update Successful"
 | 
			
		||||
  else
 | 
			
		||||
    msg_ok "No update required. ${APP} is already at v${RELEASE}"
 | 
			
		||||
  fi
 | 
			
		||||
  exit
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
start
 | 
			
		||||
@@ -69,4 +68,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 "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}"
 | 
			
		||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}"
 | 
			
		||||
 
 | 
			
		||||
@@ -40,10 +40,12 @@ function update_script() {
 | 
			
		||||
 | 
			
		||||
    msg_info "Updating ${APP} to ${RELEASE}"
 | 
			
		||||
    cp /opt/zipline/.env /opt/
 | 
			
		||||
    rm -R /opt/zipline
 | 
			
		||||
    mkdir -p /opt/zipline-upload
 | 
			
		||||
    cp -R /opt/zipline/upload/* /opt/zipline-upload/
 | 
			
		||||
    curl -fsSL "https://github.com/diced/zipline/archive/refs/tags/v${RELEASE}.zip" -o $(basename "https://github.com/diced/zipline/archive/refs/tags/v${RELEASE}.zip")
 | 
			
		||||
    unzip -q v${RELEASE}.zip
 | 
			
		||||
    mv zipline-${RELEASE} /opt/zipline
 | 
			
		||||
    unzip -q v"${RELEASE}".zip
 | 
			
		||||
    rm -R /opt/zipline
 | 
			
		||||
    mv zipline-"${RELEASE}" /opt/zipline
 | 
			
		||||
    cd /opt/zipline
 | 
			
		||||
    mv /opt/.env /opt/zipline/.env
 | 
			
		||||
    $STD pnpm install
 | 
			
		||||
@@ -56,7 +58,7 @@ function update_script() {
 | 
			
		||||
    msg_ok "Started ${APP}"
 | 
			
		||||
 | 
			
		||||
    msg_info "Cleaning Up"
 | 
			
		||||
    rm -rf v${RELEASE}.zip
 | 
			
		||||
    rm -rf v"${RELEASE}".zip
 | 
			
		||||
    msg_ok "Cleaned"
 | 
			
		||||
    msg_ok "Updated Successfully"
 | 
			
		||||
  else
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "Tailscale",
 | 
			
		||||
  "slug": "add-tailscale-lxc",
 | 
			
		||||
  "categories": [1],
 | 
			
		||||
  "categories": [
 | 
			
		||||
    1
 | 
			
		||||
  ],
 | 
			
		||||
  "date_created": "2024-05-02",
 | 
			
		||||
  "type": "addon",
 | 
			
		||||
  "updateable": false,
 | 
			
		||||
@@ -38,7 +40,7 @@
 | 
			
		||||
      "type": "info"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "text": "Execute within the Proxmox shell",
 | 
			
		||||
      "text": "Execute within the Proxmox host shell",
 | 
			
		||||
      "type": "info"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
 
 | 
			
		||||
@@ -1,34 +1,34 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "qBittorrent",
 | 
			
		||||
    "slug": "qbittorrent",
 | 
			
		||||
    "categories": [
 | 
			
		||||
        11
 | 
			
		||||
    ],
 | 
			
		||||
    "date_created": "2024-05-02",
 | 
			
		||||
    "type": "ct",
 | 
			
		||||
    "updateable": false,
 | 
			
		||||
    "privileged": false,
 | 
			
		||||
    "interface_port": 8090,
 | 
			
		||||
    "documentation": null,
 | 
			
		||||
    "website": "https://www.qbittorrent.org/",
 | 
			
		||||
    "logo": "https://raw.githubusercontent.com/selfhst/icons/refs/heads/main/svg/qbittorrent.svg",
 | 
			
		||||
    "description": "qBittorrent offers a user-friendly interface that allows users to search for and download torrent files easily. It also supports magnet links, which allow users to start downloading files without the need for a torrent file.",
 | 
			
		||||
    "install_methods": [
 | 
			
		||||
        {
 | 
			
		||||
            "type": "default",
 | 
			
		||||
            "script": "ct/qbittorrent.sh",
 | 
			
		||||
            "resources": {
 | 
			
		||||
                "cpu": 2,
 | 
			
		||||
                "ram": 2048,
 | 
			
		||||
                "hdd": 8,
 | 
			
		||||
                "os": "debian",
 | 
			
		||||
                "version": "12"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "default_credentials": {
 | 
			
		||||
        "username": "admin",
 | 
			
		||||
        "password": "changeme"
 | 
			
		||||
    },
 | 
			
		||||
    "notes": []
 | 
			
		||||
}
 | 
			
		||||
  "name": "qBittorrent",
 | 
			
		||||
  "slug": "qbittorrent",
 | 
			
		||||
  "categories": [
 | 
			
		||||
    11
 | 
			
		||||
  ],
 | 
			
		||||
  "date_created": "2024-05-02",
 | 
			
		||||
  "type": "ct",
 | 
			
		||||
  "updateable": true,
 | 
			
		||||
  "privileged": false,
 | 
			
		||||
  "interface_port": 8090,
 | 
			
		||||
  "documentation": "https://github.com/qbittorrent/qBittorrent/wiki/",
 | 
			
		||||
  "website": "https://www.qbittorrent.org/",
 | 
			
		||||
  "logo": "https://raw.githubusercontent.com/selfhst/icons/refs/heads/main/svg/qbittorrent.svg",
 | 
			
		||||
  "description": "qBittorrent offers a user-friendly interface that allows users to search for and download torrent files easily. It also supports magnet links, which allow users to start downloading files without the need for a torrent file.",
 | 
			
		||||
  "install_methods": [
 | 
			
		||||
    {
 | 
			
		||||
      "type": "default",
 | 
			
		||||
      "script": "ct/qbittorrent.sh",
 | 
			
		||||
      "resources": {
 | 
			
		||||
        "cpu": 2,
 | 
			
		||||
        "ram": 2048,
 | 
			
		||||
        "hdd": 8,
 | 
			
		||||
        "os": "debian",
 | 
			
		||||
        "version": "12"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "default_credentials": {
 | 
			
		||||
    "username": "admin",
 | 
			
		||||
    "password": "changeme"
 | 
			
		||||
  },
 | 
			
		||||
  "notes": []
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										34
									
								
								frontend/public/json/reactive-resume.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								frontend/public/json/reactive-resume.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "Reactive Resume",
 | 
			
		||||
  "slug": "reactive-resume",
 | 
			
		||||
  "categories": [
 | 
			
		||||
    12
 | 
			
		||||
  ],
 | 
			
		||||
  "date_created": "2025-04-22",
 | 
			
		||||
  "type": "ct",
 | 
			
		||||
  "updateable": true,
 | 
			
		||||
  "privileged": false,
 | 
			
		||||
  "interface_port": 3000,
 | 
			
		||||
  "documentation": "https://docs.rxresu.me/",
 | 
			
		||||
  "website": "https://rxresu.me",
 | 
			
		||||
  "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/png/reactive-resume-light.png",
 | 
			
		||||
  "description": "A one-of-a-kind resume builder that keeps your privacy in mind. Completely secure, customizable, portable, open-source and free forever.",
 | 
			
		||||
  "install_methods": [
 | 
			
		||||
    {
 | 
			
		||||
      "type": "default",
 | 
			
		||||
      "script": "ct/reactive-resume.sh",
 | 
			
		||||
      "resources": {
 | 
			
		||||
        "cpu": 2,
 | 
			
		||||
        "ram": 3072,
 | 
			
		||||
        "hdd": 8,
 | 
			
		||||
        "os": "Debian",
 | 
			
		||||
        "version": "12"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "default_credentials": {
 | 
			
		||||
    "username": null,
 | 
			
		||||
    "password": null
 | 
			
		||||
  },
 | 
			
		||||
  "notes": []
 | 
			
		||||
}
 | 
			
		||||
@@ -1,98 +1,13 @@
 | 
			
		||||
[
 | 
			
		||||
  {
 | 
			
		||||
    "name": "runtipi/runtipi",
 | 
			
		||||
    "version": "v3.10.0-beta.9",
 | 
			
		||||
    "date": "2025-04-17T11:46:08Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "prometheus-pve/prometheus-pve-exporter",
 | 
			
		||||
    "version": "v3.5.3",
 | 
			
		||||
    "date": "2025-04-17T10:40:47Z"
 | 
			
		||||
    "name": "Graylog2/graylog2-server",
 | 
			
		||||
    "version": "6.3.0-alpha.1",
 | 
			
		||||
    "date": "2025-04-23T11:25:55Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "fhem/fhem-mirror",
 | 
			
		||||
    "version": "6.2",
 | 
			
		||||
    "date": "2025-04-17T10:34:25Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "karakeep-app/karakeep",
 | 
			
		||||
    "version": "mcp/v0.23.6",
 | 
			
		||||
    "date": "2025-04-17T10:33:10Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "traefik/traefik",
 | 
			
		||||
    "version": "v2.11.23",
 | 
			
		||||
    "date": "2025-04-17T10:14:21Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "IceWhaleTech/CasaOS",
 | 
			
		||||
    "version": "v0.4.15",
 | 
			
		||||
    "date": "2024-12-19T03:19:49Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "morpheus65535/bazarr",
 | 
			
		||||
    "version": "v1.5.1",
 | 
			
		||||
    "date": "2025-01-01T16:15:52Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Jackett/Jackett",
 | 
			
		||||
    "version": "v0.22.1788",
 | 
			
		||||
    "date": "2025-04-17T05:53:12Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "ollama/ollama",
 | 
			
		||||
    "version": "v0.6.6-rc1",
 | 
			
		||||
    "date": "2025-04-17T01:56:29Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "esphome/esphome",
 | 
			
		||||
    "version": "2025.4.0",
 | 
			
		||||
    "date": "2025-04-17T00:55:34Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "dgtlmoon/changedetection.io",
 | 
			
		||||
    "version": "0.49.14",
 | 
			
		||||
    "date": "2025-04-16T21:32:22Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "influxdata/influxdb",
 | 
			
		||||
    "version": "v3.0.0",
 | 
			
		||||
    "date": "2025-04-16T21:21:04Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "moghtech/komodo",
 | 
			
		||||
    "version": "v1.17.1",
 | 
			
		||||
    "date": "2025-04-14T22:35:13Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "docmost/docmost",
 | 
			
		||||
    "version": "v0.10.2",
 | 
			
		||||
    "date": "2025-04-16T20:43:40Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "keycloak/keycloak",
 | 
			
		||||
    "version": "26.2.0",
 | 
			
		||||
    "date": "2025-04-11T12:48:27Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "ellite/Wallos",
 | 
			
		||||
    "version": "v2.50.1",
 | 
			
		||||
    "date": "2025-04-16T19:31:56Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "forgejo/forgejo",
 | 
			
		||||
    "version": "v11.0.0",
 | 
			
		||||
    "date": "2025-04-16T19:25:53Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "openobserve/openobserve",
 | 
			
		||||
    "version": "v0.14.6-rc4",
 | 
			
		||||
    "date": "2025-04-16T18:28:51Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "element-hq/synapse",
 | 
			
		||||
    "version": "v1.129.0rc1",
 | 
			
		||||
    "date": "2025-04-16T15:18:13Z"
 | 
			
		||||
    "date": "2025-04-23T10:36:12Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "nzbgetcom/nzbget",
 | 
			
		||||
@@ -100,50 +15,380 @@
 | 
			
		||||
    "date": "2025-03-18T07:33:51Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "pocket-id/pocket-id",
 | 
			
		||||
    "version": "v0.47.0",
 | 
			
		||||
    "date": "2025-04-16T14:32:31Z"
 | 
			
		||||
    "name": "cockpit-project/cockpit",
 | 
			
		||||
    "version": "337",
 | 
			
		||||
    "date": "2025-04-23T08:26:31Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "mattermost/mattermost",
 | 
			
		||||
    "version": "v10.6.2",
 | 
			
		||||
    "date": "2025-04-15T08:14:23Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "zabbix/zabbix",
 | 
			
		||||
    "version": "7.2.6",
 | 
			
		||||
    "date": "2025-04-23T08:06:23Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "jupyter/notebook",
 | 
			
		||||
    "version": "v7.4.1",
 | 
			
		||||
    "date": "2025-04-23T06:40:34Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Jackett/Jackett",
 | 
			
		||||
    "version": "v0.22.1815",
 | 
			
		||||
    "date": "2025-04-23T05:56:23Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "openobserve/openobserve",
 | 
			
		||||
    "version": "v0.14.6-rc6",
 | 
			
		||||
    "date": "2025-04-23T04:24:27Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "jhuckaby/Cronicle",
 | 
			
		||||
    "version": "v0.9.78",
 | 
			
		||||
    "date": "2025-04-23T01:38:28Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "grafana/grafana",
 | 
			
		||||
    "version": "v11.3.6",
 | 
			
		||||
    "date": "2025-04-23T00:18:05Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "cross-seed/cross-seed",
 | 
			
		||||
    "version": "v6.11.2",
 | 
			
		||||
    "date": "2025-02-26T14:54:49Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "minio/minio",
 | 
			
		||||
    "version": "RELEASE.2025-04-22T22-12-26Z",
 | 
			
		||||
    "date": "2025-04-22T22:44:34Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "docmost/docmost",
 | 
			
		||||
    "version": "v0.20.0",
 | 
			
		||||
    "date": "2025-04-22T22:30:25Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "glanceapp/glance",
 | 
			
		||||
    "version": "v0.7.13",
 | 
			
		||||
    "date": "2025-04-22T22:19:16Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "netbox-community/netbox",
 | 
			
		||||
    "version": "v4.2.8",
 | 
			
		||||
    "date": "2025-04-22T19:44:49Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "keycloak/keycloak",
 | 
			
		||||
    "version": "26.2.0",
 | 
			
		||||
    "date": "2025-04-11T12:48:27Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "donaldzou/WGDashboard",
 | 
			
		||||
    "version": "v4.2.0",
 | 
			
		||||
    "date": "2025-04-22T18:18:28Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "n8n-io/n8n",
 | 
			
		||||
    "version": "n8n@1.86.1",
 | 
			
		||||
    "date": "2025-04-09T09:20:55Z"
 | 
			
		||||
    "version": "n8n@1.90.0",
 | 
			
		||||
    "date": "2025-04-22T08:58:15Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "NodeBB/NodeBB",
 | 
			
		||||
    "version": "v4.2.2",
 | 
			
		||||
    "date": "2025-04-22T16:33:53Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "jenkinsci/jenkins",
 | 
			
		||||
    "version": "jenkins-2.506",
 | 
			
		||||
    "date": "2025-04-15T15:40:50Z"
 | 
			
		||||
    "version": "jenkins-2.507",
 | 
			
		||||
    "date": "2025-04-22T15:22:53Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "wazuh/wazuh",
 | 
			
		||||
    "version": "coverity-w16-4.12.0",
 | 
			
		||||
    "date": "2025-04-15T08:25:08Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "pocketbase/pocketbase",
 | 
			
		||||
    "version": "v0.27.0",
 | 
			
		||||
    "date": "2025-04-16T04:54:37Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "icereed/paperless-gpt",
 | 
			
		||||
    "version": "v0.15.0",
 | 
			
		||||
    "date": "2025-04-16T03:58:02Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "HabitRPG/habitica",
 | 
			
		||||
    "version": "v5.35.3",
 | 
			
		||||
    "date": "2025-04-15T19:32:17Z"
 | 
			
		||||
    "name": "evcc-io/evcc",
 | 
			
		||||
    "version": "0.203.2",
 | 
			
		||||
    "date": "2025-04-22T15:07:28Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Checkmk/checkmk",
 | 
			
		||||
    "version": "v2.4.0b5",
 | 
			
		||||
    "date": "2025-04-15T17:59:06Z"
 | 
			
		||||
    "version": "v2.4.0b6",
 | 
			
		||||
    "date": "2025-04-22T15:00:31Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "zwave-js/zwave-js-ui",
 | 
			
		||||
    "version": "v10.3.0",
 | 
			
		||||
    "date": "2025-04-22T14:57:47Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "OliveTin/OliveTin",
 | 
			
		||||
    "version": "2025.4.22",
 | 
			
		||||
    "date": "2025-04-22T14:18:11Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "mongodb/mongo",
 | 
			
		||||
    "version": "r8.2.0-alpha",
 | 
			
		||||
    "date": "2025-04-22T13:19:07Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "AdguardTeam/AdGuardHome",
 | 
			
		||||
    "version": "v0.107.61",
 | 
			
		||||
    "date": "2025-04-22T12:42:26Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "VictoriaMetrics/VictoriaMetrics",
 | 
			
		||||
    "version": "v1.20.0-victorialogs",
 | 
			
		||||
    "date": "2025-04-22T12:00:23Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "OctoPrint/OctoPrint",
 | 
			
		||||
    "version": "1.11.0",
 | 
			
		||||
    "date": "2025-04-22T09:33:46Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "morpheus65535/bazarr",
 | 
			
		||||
    "version": "v1.5.1",
 | 
			
		||||
    "date": "2025-01-01T16:15:52Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "firefly-iii/firefly-iii",
 | 
			
		||||
    "version": "v6.2.12",
 | 
			
		||||
    "date": "2025-04-20T19:22:17Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "monicahq/monica",
 | 
			
		||||
    "version": "v4.1.2",
 | 
			
		||||
    "date": "2024-05-04T08:06:50Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Suwayomi/Suwayomi-Server",
 | 
			
		||||
    "version": "v2.0.1727",
 | 
			
		||||
    "date": "2025-04-21T17:53:05Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "TasmoAdmin/TasmoAdmin",
 | 
			
		||||
    "version": "v4.3.0",
 | 
			
		||||
    "date": "2025-04-21T17:44:40Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "emqx/emqx",
 | 
			
		||||
    "version": "e5.9.0-beta.4",
 | 
			
		||||
    "date": "2025-04-21T17:08:59Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "runtipi/runtipi",
 | 
			
		||||
    "version": "v3.10.0-beta.9",
 | 
			
		||||
    "date": "2025-04-17T11:46:08Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Prowlarr/Prowlarr",
 | 
			
		||||
    "version": "v1.34.1.5021",
 | 
			
		||||
    "date": "2025-04-20T19:29:50Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "semaphoreui/semaphore",
 | 
			
		||||
    "version": "v2.13.14",
 | 
			
		||||
    "date": "2025-04-19T20:39:23Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "pocket-id/pocket-id",
 | 
			
		||||
    "version": "v0.49.0",
 | 
			
		||||
    "date": "2025-04-20T18:03:03Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "YunoHost/yunohost",
 | 
			
		||||
    "version": "debian/12.0.14",
 | 
			
		||||
    "date": "2025-04-09T10:09:00Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Kareadita/Kavita",
 | 
			
		||||
    "version": "v0.8.6.2",
 | 
			
		||||
    "date": "2025-04-20T16:55:38Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "openhab/openhab-core",
 | 
			
		||||
    "version": "5.0.0.M2",
 | 
			
		||||
    "date": "2025-04-20T13:16:29Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "pocketbase/pocketbase",
 | 
			
		||||
    "version": "v0.27.1",
 | 
			
		||||
    "date": "2025-04-20T11:26:50Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "karakeep-app/karakeep",
 | 
			
		||||
    "version": "extension/v1.2.5",
 | 
			
		||||
    "date": "2025-04-20T10:19:06Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "MediaBrowser/Emby.Releases",
 | 
			
		||||
    "version": "4.8.11.0",
 | 
			
		||||
    "date": "2025-03-10T06:39:11Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Readarr/Readarr",
 | 
			
		||||
    "version": "v2.0.0.4645",
 | 
			
		||||
    "date": "2017-03-07T18:56:06Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Radarr/Radarr",
 | 
			
		||||
    "version": "v5.21.1.9799",
 | 
			
		||||
    "date": "2025-03-24T15:52:12Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "paperless-ngx/paperless-ngx",
 | 
			
		||||
    "version": "v2.15.3",
 | 
			
		||||
    "date": "2025-04-19T23:02:17Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "ellite/Wallos",
 | 
			
		||||
    "version": "v2.52.0",
 | 
			
		||||
    "date": "2025-04-19T20:36:50Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "msgbyte/tianji",
 | 
			
		||||
    "version": "v1.20.7",
 | 
			
		||||
    "date": "2025-04-19T20:35:09Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "theonedev/onedev",
 | 
			
		||||
    "version": "v11.8.7",
 | 
			
		||||
    "date": "2025-04-19T11:19:29Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "home-assistant/core",
 | 
			
		||||
    "version": "2025.4.3",
 | 
			
		||||
    "date": "2025-04-19T10:23:38Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "moghtech/komodo",
 | 
			
		||||
    "version": "v1.17.2",
 | 
			
		||||
    "date": "2025-04-19T06:56:25Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "ollama/ollama",
 | 
			
		||||
    "version": "v0.6.6-rc1",
 | 
			
		||||
    "date": "2025-04-17T01:56:29Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "caddyserver/caddy",
 | 
			
		||||
    "version": "v2.10.0",
 | 
			
		||||
    "date": "2025-04-18T20:46:28Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "homarr-labs/homarr",
 | 
			
		||||
    "version": "v1.17.0",
 | 
			
		||||
    "date": "2025-04-18T19:14:57Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Paymenter/Paymenter",
 | 
			
		||||
    "version": "v1.0.4",
 | 
			
		||||
    "date": "2025-04-18T16:08:02Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "prometheus/prometheus",
 | 
			
		||||
    "version": "v3.3.0",
 | 
			
		||||
    "date": "2025-04-18T13:46:38Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "dgtlmoon/changedetection.io",
 | 
			
		||||
    "version": "0.49.15",
 | 
			
		||||
    "date": "2025-04-18T12:58:26Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "traefik/traefik",
 | 
			
		||||
    "version": "v3.3.6",
 | 
			
		||||
    "date": "2025-04-18T09:28:22Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "TandoorRecipes/recipes",
 | 
			
		||||
    "version": "1.5.34",
 | 
			
		||||
    "date": "2025-03-27T16:17:38Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Luligu/matterbridge",
 | 
			
		||||
    "version": "2.2.9",
 | 
			
		||||
    "date": "2025-04-18T10:05:52Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "HabitRPG/habitica",
 | 
			
		||||
    "version": "v5.35.4",
 | 
			
		||||
    "date": "2025-04-17T21:18:55Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "tailscale/tailscale",
 | 
			
		||||
    "version": "v1.82.5",
 | 
			
		||||
    "date": "2025-04-17T20:59:15Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "TriliumNext/Notes",
 | 
			
		||||
    "version": "v0.93.0",
 | 
			
		||||
    "date": "2025-04-17T20:05:25Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "coder/code-server",
 | 
			
		||||
    "version": "v4.99.3",
 | 
			
		||||
    "date": "2025-04-17T18:33:11Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "duplicati/duplicati",
 | 
			
		||||
    "version": "v2.1.0.116-2.1.0.116_canary_2025-04-17",
 | 
			
		||||
    "date": "2025-04-17T17:52:31Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "benzino77/tasmocompiler",
 | 
			
		||||
    "version": "v12.6.1",
 | 
			
		||||
    "date": "2025-04-17T17:35:02Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "docker/compose",
 | 
			
		||||
    "version": "v2.35.1",
 | 
			
		||||
    "date": "2025-04-17T14:29:11Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "influxdata/influxdb",
 | 
			
		||||
    "version": "v3.0.1",
 | 
			
		||||
    "date": "2025-04-17T14:06:09Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "documenso/documenso",
 | 
			
		||||
    "version": "v1.10.0-rc.5",
 | 
			
		||||
    "date": "2025-04-17T13:01:43Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "prometheus-pve/prometheus-pve-exporter",
 | 
			
		||||
    "version": "v3.5.3",
 | 
			
		||||
    "date": "2025-04-17T10:40:47Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "IceWhaleTech/CasaOS",
 | 
			
		||||
    "version": "v0.4.15",
 | 
			
		||||
    "date": "2024-12-19T03:19:49Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "esphome/esphome",
 | 
			
		||||
    "version": "2025.4.0",
 | 
			
		||||
    "date": "2025-04-17T00:55:34Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "forgejo/forgejo",
 | 
			
		||||
    "version": "v11.0.0",
 | 
			
		||||
    "date": "2025-04-16T19:25:53Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "element-hq/synapse",
 | 
			
		||||
    "version": "v1.129.0rc1",
 | 
			
		||||
    "date": "2025-04-16T15:18:13Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "wazuh/wazuh",
 | 
			
		||||
    "version": "coverity-w17-4.12.0",
 | 
			
		||||
    "date": "2025-04-16T11:20:57Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "icereed/paperless-gpt",
 | 
			
		||||
    "version": "v0.15.0",
 | 
			
		||||
    "date": "2025-04-16T03:58:02Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "rabbitmq/rabbitmq-server",
 | 
			
		||||
    "version": "v4.1.0",
 | 
			
		||||
@@ -164,21 +409,6 @@
 | 
			
		||||
    "version": "v0.45.6",
 | 
			
		||||
    "date": "2025-04-15T14:16:52Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "prometheus/prometheus",
 | 
			
		||||
    "version": "v0.303.0",
 | 
			
		||||
    "date": "2025-04-15T13:30:05Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "mattermost/mattermost",
 | 
			
		||||
    "version": "v10.6.2",
 | 
			
		||||
    "date": "2025-04-15T08:14:23Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "zabbix/zabbix",
 | 
			
		||||
    "version": "7.0.12rc1",
 | 
			
		||||
    "date": "2025-04-15T08:00:11Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "slskd/slskd",
 | 
			
		||||
    "version": "0.22.5",
 | 
			
		||||
@@ -189,41 +419,6 @@
 | 
			
		||||
    "version": "v0.14.1",
 | 
			
		||||
    "date": "2024-08-29T22:32:51Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Kareadita/Kavita",
 | 
			
		||||
    "version": "v0.8.6",
 | 
			
		||||
    "date": "2025-04-14T22:09:30Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "paperless-ngx/paperless-ngx",
 | 
			
		||||
    "version": "v2.15.2",
 | 
			
		||||
    "date": "2025-04-14T20:48:52Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "semaphoreui/semaphore",
 | 
			
		||||
    "version": "v2.13.13",
 | 
			
		||||
    "date": "2025-04-11T10:15:13Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "netbox-community/netbox",
 | 
			
		||||
    "version": "v4.2.7",
 | 
			
		||||
    "date": "2025-04-10T20:08:13Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "duplicati/duplicati",
 | 
			
		||||
    "version": "v2.1.0.115-2.1.0.115_canary_2025-04-14",
 | 
			
		||||
    "date": "2025-04-14T18:03:36Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "OliveTin/OliveTin",
 | 
			
		||||
    "version": "2025.4.14",
 | 
			
		||||
    "date": "2025-04-14T16:53:53Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "msgbyte/tianji",
 | 
			
		||||
    "version": "v1.20.0",
 | 
			
		||||
    "date": "2025-04-14T16:19:23Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "home-assistant/operating-system",
 | 
			
		||||
    "version": "15.2",
 | 
			
		||||
@@ -234,16 +429,6 @@
 | 
			
		||||
    "version": "v0.0.7-hf1",
 | 
			
		||||
    "date": "2025-03-10T20:49:39Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "AdguardTeam/AdGuardHome",
 | 
			
		||||
    "version": "v0.107.60",
 | 
			
		||||
    "date": "2025-04-14T11:46:19Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Graylog2/graylog2-server",
 | 
			
		||||
    "version": "6.2.0-rc.1",
 | 
			
		||||
    "date": "2025-04-14T11:26:18Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "bluenviron/mediamtx",
 | 
			
		||||
    "version": "v1.12.0",
 | 
			
		||||
@@ -254,26 +439,6 @@
 | 
			
		||||
    "version": "v0.6.5",
 | 
			
		||||
    "date": "2025-04-14T09:13:36Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "zwave-js/zwave-js-ui",
 | 
			
		||||
    "version": "v10.2.0",
 | 
			
		||||
    "date": "2025-04-14T08:53:44Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "evcc-io/evcc",
 | 
			
		||||
    "version": "0.203.1",
 | 
			
		||||
    "date": "2025-04-14T07:23:02Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "firefly-iii/firefly-iii",
 | 
			
		||||
    "version": "v6.2.10",
 | 
			
		||||
    "date": "2025-03-22T13:02:26Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "glanceapp/glance",
 | 
			
		||||
    "version": "v0.7.12",
 | 
			
		||||
    "date": "2025-04-14T00:16:15Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "rogerfar/rdt-client",
 | 
			
		||||
    "version": "v2.0.108",
 | 
			
		||||
@@ -304,21 +469,6 @@
 | 
			
		||||
    "version": "v2.10.3.4602",
 | 
			
		||||
    "date": "2025-03-23T11:00:37Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Readarr/Readarr",
 | 
			
		||||
    "version": "v2.0.0.4645",
 | 
			
		||||
    "date": "2017-03-07T18:56:06Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Prowlarr/Prowlarr",
 | 
			
		||||
    "version": "v1.33.3.5008",
 | 
			
		||||
    "date": "2025-04-09T17:58:37Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Radarr/Radarr",
 | 
			
		||||
    "version": "v5.21.1.9799",
 | 
			
		||||
    "date": "2025-03-24T15:52:12Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Tautulli/Tautulli",
 | 
			
		||||
    "version": "v2.15.2",
 | 
			
		||||
@@ -329,11 +479,6 @@
 | 
			
		||||
    "version": "v0.2.11",
 | 
			
		||||
    "date": "2025-04-12T21:13:08Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "home-assistant/core",
 | 
			
		||||
    "version": "2025.4.2",
 | 
			
		||||
    "date": "2025-04-12T09:46:22Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "readeck/readeck",
 | 
			
		||||
    "version": "0.18.0",
 | 
			
		||||
@@ -344,31 +489,6 @@
 | 
			
		||||
    "version": "v5.5.2",
 | 
			
		||||
    "date": "2025-04-11T22:00:06Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "homarr-labs/homarr",
 | 
			
		||||
    "version": "v1.16.0",
 | 
			
		||||
    "date": "2025-04-11T19:15:24Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "tailscale/tailscale",
 | 
			
		||||
    "version": "v1.82.4",
 | 
			
		||||
    "date": "2025-04-11T17:58:09Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "coder/code-server",
 | 
			
		||||
    "version": "v4.99.2",
 | 
			
		||||
    "date": "2025-04-11T17:57:47Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "TriliumNext/Notes",
 | 
			
		||||
    "version": "v0.0.0",
 | 
			
		||||
    "date": "2025-04-11T14:18:00Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "emqx/emqx",
 | 
			
		||||
    "version": "e5.9.0-beta.3",
 | 
			
		||||
    "date": "2025-04-11T14:17:53Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "NLnetLabs/unbound",
 | 
			
		||||
    "version": "release-1.23.0rc2",
 | 
			
		||||
@@ -384,11 +504,6 @@
 | 
			
		||||
    "version": "v0.83.0",
 | 
			
		||||
    "date": "2025-04-11T03:53:10Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Luligu/matterbridge",
 | 
			
		||||
    "version": "2.2.8",
 | 
			
		||||
    "date": "2025-04-10T20:30:49Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "gristlabs/grist-core",
 | 
			
		||||
    "version": "v1.5.1",
 | 
			
		||||
@@ -399,36 +514,6 @@
 | 
			
		||||
    "version": "cassandra-5.0.4",
 | 
			
		||||
    "date": "2025-04-10T16:32:00Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "VictoriaMetrics/VictoriaMetrics",
 | 
			
		||||
    "version": "v1.18.0-victorialogs",
 | 
			
		||||
    "date": "2025-04-10T15:05:20Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "NodeBB/NodeBB",
 | 
			
		||||
    "version": "v4.2.1",
 | 
			
		||||
    "date": "2025-04-10T14:03:47Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Paymenter/Paymenter",
 | 
			
		||||
    "version": "v1.0.3",
 | 
			
		||||
    "date": "2025-04-10T13:57:39Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "docker/compose",
 | 
			
		||||
    "version": "v2.35.0",
 | 
			
		||||
    "date": "2025-04-10T13:45:22Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "mongodb/mongo",
 | 
			
		||||
    "version": "r8.0.5-rc2",
 | 
			
		||||
    "date": "2025-04-09T22:37:52Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "jupyter/notebook",
 | 
			
		||||
    "version": "v7.4.0",
 | 
			
		||||
    "date": "2025-04-09T17:36:14Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "glpi-project/glpi",
 | 
			
		||||
    "version": "10.0.18",
 | 
			
		||||
@@ -439,16 +524,6 @@
 | 
			
		||||
    "version": "v2.69.10",
 | 
			
		||||
    "date": "2025-04-09T12:16:51Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "YunoHost/yunohost",
 | 
			
		||||
    "version": "debian/12.0.14",
 | 
			
		||||
    "date": "2025-04-09T10:09:00Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "minio/minio",
 | 
			
		||||
    "version": "RELEASE.2025-04-08T15-41-24Z",
 | 
			
		||||
    "date": "2025-04-08T19:51:06Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "goauthentik/authentik",
 | 
			
		||||
    "version": "version/2025.2.4",
 | 
			
		||||
@@ -509,11 +584,6 @@
 | 
			
		||||
    "version": "2.0.3",
 | 
			
		||||
    "date": "2025-04-06T17:35:41Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "TandoorRecipes/recipes",
 | 
			
		||||
    "version": "1.5.34",
 | 
			
		||||
    "date": "2025-03-27T16:17:38Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "TechnitiumSoftware/DnsServer",
 | 
			
		||||
    "version": "v13.5.0",
 | 
			
		||||
@@ -579,11 +649,6 @@
 | 
			
		||||
    "version": "9.0.104",
 | 
			
		||||
    "date": "2025-04-04T12:58:11Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "OctoPrint/OctoPrint",
 | 
			
		||||
    "version": "1.10.3",
 | 
			
		||||
    "date": "2024-11-05T09:20:50Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "BookStackApp/BookStack",
 | 
			
		||||
    "version": "v25.02.2",
 | 
			
		||||
@@ -609,11 +674,6 @@
 | 
			
		||||
    "version": "v2.31.0",
 | 
			
		||||
    "date": "2025-04-01T18:12:45Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "theonedev/onedev",
 | 
			
		||||
    "version": "v11.8.6",
 | 
			
		||||
    "date": "2025-04-01T13:52:03Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "neo4j/neo4j",
 | 
			
		||||
    "version": "5.26.5",
 | 
			
		||||
@@ -629,11 +689,6 @@
 | 
			
		||||
    "version": "v2.2.0",
 | 
			
		||||
    "date": "2025-03-31T21:31:48Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "documenso/documenso",
 | 
			
		||||
    "version": "v1.10.0-rc.4",
 | 
			
		||||
    "date": "2025-03-31T09:02:22Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "mysql/mysql-server",
 | 
			
		||||
    "version": "mysql-cluster-9.3.0",
 | 
			
		||||
@@ -654,11 +709,6 @@
 | 
			
		||||
    "version": "v1.17.0",
 | 
			
		||||
    "date": "2025-03-30T14:21:53Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "openhab/openhab-core",
 | 
			
		||||
    "version": "4.3.4",
 | 
			
		||||
    "date": "2025-03-30T13:32:38Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "aceberg/WatchYourLAN",
 | 
			
		||||
    "version": "2.1.2-alpine",
 | 
			
		||||
@@ -679,11 +729,6 @@
 | 
			
		||||
    "version": "v1.6.1",
 | 
			
		||||
    "date": "2025-03-15T17:29:17Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "TasmoAdmin/TasmoAdmin",
 | 
			
		||||
    "version": "v4.2.3",
 | 
			
		||||
    "date": "2025-02-09T23:07:48Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "grocy/grocy",
 | 
			
		||||
    "version": "v4.5.0",
 | 
			
		||||
@@ -699,11 +744,6 @@
 | 
			
		||||
    "version": "5.2.1",
 | 
			
		||||
    "date": "2025-03-28T13:00:23Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "cockpit-project/cockpit",
 | 
			
		||||
    "version": "336.2",
 | 
			
		||||
    "date": "2025-03-28T10:16:47Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "gethomepage/homepage",
 | 
			
		||||
    "version": "v1.1.1",
 | 
			
		||||
@@ -719,11 +759,6 @@
 | 
			
		||||
    "version": "v1.34.0",
 | 
			
		||||
    "date": "2025-03-26T08:48:34Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "grafana/grafana",
 | 
			
		||||
    "version": "v11.6.0",
 | 
			
		||||
    "date": "2025-03-25T22:10:15Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "ipfs/kubo",
 | 
			
		||||
    "version": "v0.34.1",
 | 
			
		||||
@@ -744,11 +779,6 @@
 | 
			
		||||
    "version": "v1.6.8",
 | 
			
		||||
    "date": "2025-03-25T13:33:10Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "caddyserver/caddy",
 | 
			
		||||
    "version": "v2.9.1",
 | 
			
		||||
    "date": "2025-01-08T15:22:53Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "ZoeyVid/NPMplus",
 | 
			
		||||
    "version": "2025-03-24-r2",
 | 
			
		||||
@@ -789,11 +819,6 @@
 | 
			
		||||
    "version": "250321-57590c48b",
 | 
			
		||||
    "date": "2025-03-21T11:48:16Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "jhuckaby/Cronicle",
 | 
			
		||||
    "version": "v0.9.77",
 | 
			
		||||
    "date": "2025-03-21T02:25:42Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "seanmorley15/AdventureLog",
 | 
			
		||||
    "version": "v0.9.0",
 | 
			
		||||
@@ -929,11 +954,6 @@
 | 
			
		||||
    "version": "v1.5.7",
 | 
			
		||||
    "date": "2025-02-27T20:04:08Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "cross-seed/cross-seed",
 | 
			
		||||
    "version": "v6.11.2",
 | 
			
		||||
    "date": "2025-02-26T14:54:49Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "silverbulletmd/silverbullet",
 | 
			
		||||
    "version": "0.10.4",
 | 
			
		||||
@@ -944,11 +964,6 @@
 | 
			
		||||
    "version": "v0.25.1",
 | 
			
		||||
    "date": "2025-02-25T17:30:48Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "benzino77/tasmocompiler",
 | 
			
		||||
    "version": "v12.5.0",
 | 
			
		||||
    "date": "2025-02-25T14:55:50Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "schlagmichdoch/PairDrop",
 | 
			
		||||
    "version": "v1.11.2",
 | 
			
		||||
@@ -1059,11 +1074,6 @@
 | 
			
		||||
    "version": "v0.2.1",
 | 
			
		||||
    "date": "2025-01-19T22:40:40Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "donaldzou/WGDashboard",
 | 
			
		||||
    "version": "v4.1.4",
 | 
			
		||||
    "date": "2025-01-19T13:14:19Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "0xERR0R/blocky",
 | 
			
		||||
    "version": "v0.25",
 | 
			
		||||
@@ -1184,11 +1194,6 @@
 | 
			
		||||
    "version": "v3.3.21",
 | 
			
		||||
    "date": "2024-06-26T01:14:46Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Suwayomi/Suwayomi-Server",
 | 
			
		||||
    "version": "v1.1.1",
 | 
			
		||||
    "date": "2024-06-15T17:23:48Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Forceu/barcodebuddy",
 | 
			
		||||
    "version": "v1.8.1.8",
 | 
			
		||||
@@ -1224,11 +1229,6 @@
 | 
			
		||||
    "version": "1.0.0",
 | 
			
		||||
    "date": "2024-05-05T02:01:51Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "monicahq/monica",
 | 
			
		||||
    "version": "v4.1.2",
 | 
			
		||||
    "date": "2024-05-04T08:06:50Z"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "thelounge/thelounge-deb",
 | 
			
		||||
    "version": "v4.4.3",
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,8 @@ const generateSourceUrl = (slug: string, type: string) => {
 | 
			
		||||
      return `${baseUrl}/tools/pve/${slug}.sh`;
 | 
			
		||||
    case "addon":
 | 
			
		||||
      return `${baseUrl}/tools/addon/${slug}.sh`;
 | 
			
		||||
    case "turnkey":
 | 
			
		||||
      return `${baseUrl}/turnkey/${slug}.sh`;
 | 
			
		||||
    default:
 | 
			
		||||
      return `${baseUrl}/ct/${slug}.sh`; // fallback for "ct"
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ msg_ok "Installed Dependencies"
 | 
			
		||||
msg_info "Installing Additional Tools"
 | 
			
		||||
curl -fsSL "https://github.com/Y2Z/monolith/releases/latest/download/monolith-gnu-linux-x86_64" -o "/usr/bin/monolith"
 | 
			
		||||
chmod +x /usr/bin/monolith
 | 
			
		||||
curl -fsSL "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux" -o "/usr/bin/yt-dlp"
 | 
			
		||||
curl -fsSL "https://github.com/yt-dlp/yt-dlp-nightly-builds/releases/latest/download/yt-dlp_linux" -o "/usr/bin/yt-dlp"
 | 
			
		||||
chmod +x /usr/bin/yt-dlp
 | 
			
		||||
msg_ok "Installed Additional Tools"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										190
									
								
								install/reactive-resume-install.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								install/reactive-resume-install.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,190 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
# Copyright (c) 2021-2025 community-scripts ORG
 | 
			
		||||
# Author: vhsdream
 | 
			
		||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
 | 
			
		||||
# Source: https://rxresu.me
 | 
			
		||||
 | 
			
		||||
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 \
 | 
			
		||||
    gnupg \
 | 
			
		||||
    unzip \
 | 
			
		||||
    postgresql-common
 | 
			
		||||
msg_ok "Installed Dependencies"
 | 
			
		||||
 | 
			
		||||
msg_info "Installing Additional Dependencies"
 | 
			
		||||
mkdir -p /etc/apt/keyrings
 | 
			
		||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
 | 
			
		||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" >/etc/apt/sources.list.d/nodesource.list
 | 
			
		||||
echo "YES" | /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh &>/dev/null
 | 
			
		||||
$STD apt-get install -y postgresql-16 nodejs
 | 
			
		||||
cd /tmp
 | 
			
		||||
curl -fsSL https://dl.min.io/server/minio/release/linux-amd64/minio.deb -o minio.deb
 | 
			
		||||
$STD dpkg -i minio.deb
 | 
			
		||||
 | 
			
		||||
msg_info "Setting up Database"
 | 
			
		||||
DB_USER="rxresume"
 | 
			
		||||
DB_NAME="rxresume"
 | 
			
		||||
DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)
 | 
			
		||||
$STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH ENCRYPTED PASSWORD '$DB_PASS';"
 | 
			
		||||
$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;"
 | 
			
		||||
$STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME to $DB_USER;"
 | 
			
		||||
$STD sudo -u postgres psql -c "ALTER USER $DB_USER WITH SUPERUSER;"
 | 
			
		||||
msg_ok "Set up Database"
 | 
			
		||||
 | 
			
		||||
msg_info "Installing ${APPLICATION}"
 | 
			
		||||
MINIO_PASS=$(openssl rand -base64 48)
 | 
			
		||||
ACCESS_TOKEN=$(openssl rand -base64 48)
 | 
			
		||||
REFRESH_TOKEN=$(openssl rand -base64 48)
 | 
			
		||||
CHROME_TOKEN=$(openssl rand -hex 32)
 | 
			
		||||
LOCAL_IP=$(hostname -I | awk '{print $1}')
 | 
			
		||||
TAG=$(curl -fsSL https://api.github.com/repos/browserless/browserless/tags?per_page=1 | grep "name" | awk '{print substr($2, 3, length($2)-4) }')
 | 
			
		||||
RELEASE=$(curl -fsSL https://api.github.com/repos/AmruthPillai/Reactive-Resume/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }')
 | 
			
		||||
curl -fsSL "https://github.com/AmruthPillai/Reactive-Resume/archive/refs/tags/v${RELEASE}.zip" -o v${RELEASE}.zip
 | 
			
		||||
unzip -q v${RELEASE}.zip
 | 
			
		||||
mv ${APPLICATION}-${RELEASE}/ /opt/${APPLICATION}
 | 
			
		||||
cd /opt/${APPLICATION}
 | 
			
		||||
corepack enable
 | 
			
		||||
export CI="true"
 | 
			
		||||
export PUPPETEER_SKIP_DOWNLOAD="true"
 | 
			
		||||
export NODE_ENV="production"
 | 
			
		||||
export NEXT_TELEMETRY_DISABLED=1
 | 
			
		||||
$STD pnpm install --frozen-lockfile
 | 
			
		||||
$STD pnpm run build
 | 
			
		||||
$STD pnpm install --prod --frozen-lockfile
 | 
			
		||||
$STD pnpm run prisma:generate
 | 
			
		||||
msg_ok "Installed ${APPLICATION}"
 | 
			
		||||
 | 
			
		||||
msg_info "Installing Browserless (Patience)"
 | 
			
		||||
cd /tmp
 | 
			
		||||
curl -fsSL https://github.com/browserless/browserless/archive/refs/tags/v${TAG}.zip -o v${TAG}.zip
 | 
			
		||||
unzip -q v${TAG}.zip
 | 
			
		||||
mv browserless-${TAG} /opt/browserless
 | 
			
		||||
cd /opt/browserless
 | 
			
		||||
$STD npm install
 | 
			
		||||
rm -rf src/routes/{chrome,edge,firefox,webkit}
 | 
			
		||||
$STD node_modules/playwright-core/cli.js install --with-deps chromium
 | 
			
		||||
$STD npm run build
 | 
			
		||||
$STD npm run build:function
 | 
			
		||||
$STD npm prune production
 | 
			
		||||
msg_ok "Installed Browserless"
 | 
			
		||||
 | 
			
		||||
msg_info "Configuring applications"
 | 
			
		||||
mkdir -p /opt/minio
 | 
			
		||||
cat <<EOF >/opt/minio/.env
 | 
			
		||||
MINIO_ROOT_USER="storageadmin"
 | 
			
		||||
MINIO_ROOT_PASSWORD="${MINIO_PASS}"
 | 
			
		||||
MINIO_VOLUMES=/opt/minio
 | 
			
		||||
MINIO_OPTS="--address :9000 --console-address 127.0.0.1:9001"
 | 
			
		||||
EOF
 | 
			
		||||
cat <<EOF >/opt/${APPLICATION}/.env
 | 
			
		||||
NODE_ENV=production
 | 
			
		||||
PORT=3000
 | 
			
		||||
PUBLIC_URL=http://${LOCAL_IP}:3000
 | 
			
		||||
STORAGE_URL=http://${LOCAL_IP}:9000/rxresume
 | 
			
		||||
DATABASE_URL=postgresql://${DB_USER}:${DB_PASS}@localhost:5432/${DB_NAME}?schema=public
 | 
			
		||||
ACCESS_TOKEN_SECRET=${ACCESS_TOKEN}
 | 
			
		||||
REFRESH_TOKEN_SECRET=${REFRESH_TOKEN}
 | 
			
		||||
CHROME_PORT=8080
 | 
			
		||||
CHROME_TOKEN=${CHROME_TOKEN}
 | 
			
		||||
CHROME_URL=ws://localhost:8080
 | 
			
		||||
CHROME_IGNORE_HTTPS_ERRORS=true
 | 
			
		||||
MAIL_FROM=noreply@locahost
 | 
			
		||||
# SMTP_URL=smtp://username:password@smtp.server.mail:587 #
 | 
			
		||||
STORAGE_ENDPOINT=localhost
 | 
			
		||||
STORAGE_PORT=9000
 | 
			
		||||
STORAGE_REGION=us-east-1
 | 
			
		||||
STORAGE_BUCKET=rxresume
 | 
			
		||||
STORAGE_ACCESS_KEY=storageadmin
 | 
			
		||||
STORAGE_SECRET_KEY=${MINIO_PASS}
 | 
			
		||||
STORAGE_USE_SSL=false
 | 
			
		||||
STORAGE_SKIP_BUCKET_CHECK=false
 | 
			
		||||
 | 
			
		||||
# GitHub (OAuth, Optional)
 | 
			
		||||
# GITHUB_CLIENT_ID=
 | 
			
		||||
# GITHUB_CLIENT_SECRET=
 | 
			
		||||
# GITHUB_CALLBACK_URL=http://localhost:5173/api/auth/github/callback
 | 
			
		||||
 | 
			
		||||
# Google (OAuth, Optional)
 | 
			
		||||
# GOOGLE_CLIENT_ID=
 | 
			
		||||
# GOOGLE_CLIENT_SECRET=
 | 
			
		||||
# GOOGLE_CALLBACK_URL=http://localhost:5173/api/auth/google/callback
 | 
			
		||||
EOF
 | 
			
		||||
cat <<EOF >/opt/browserless/.env
 | 
			
		||||
DEBUG=browserless*,-**:verbose
 | 
			
		||||
HOST=localhost
 | 
			
		||||
PORT=8080
 | 
			
		||||
TOKEN=${CHROME_TOKEN}
 | 
			
		||||
EOF
 | 
			
		||||
echo "${RELEASE}" >/opt/${APPLICATION}_version.txt
 | 
			
		||||
{
 | 
			
		||||
    echo "${APPLICATION} Credentials"
 | 
			
		||||
    echo "Database User: $DB_USER"
 | 
			
		||||
    echo "Database Password: $DB_PASS"
 | 
			
		||||
    echo "Database Name: $DB_NAME"
 | 
			
		||||
    echo "Minio Root Password: ${MINIO_PASS}"
 | 
			
		||||
} >>~/${APPLICATION}.creds
 | 
			
		||||
msg_ok "Configured applications"
 | 
			
		||||
 | 
			
		||||
msg_info "Creating Services"
 | 
			
		||||
mkdir -p /etc/systemd/system/minio.service.d/
 | 
			
		||||
cat <<EOF >/etc/systemd/system/minio.service.d/override.conf
 | 
			
		||||
[Service]
 | 
			
		||||
User=root
 | 
			
		||||
Group=root
 | 
			
		||||
WorkingDirectory=/usr/local/bin
 | 
			
		||||
EnvironmentFile=/opt/minio/.env
 | 
			
		||||
EOF
 | 
			
		||||
 | 
			
		||||
cat <<EOF >/etc/systemd/system/${APPLICATION}.service
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=${APPLICATION} Service
 | 
			
		||||
After=network.target postgresql.service minio.service
 | 
			
		||||
Wants=postgresql.service minio.service
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
WorkingDirectory=/opt/${APPLICATION}
 | 
			
		||||
EnvironmentFile=/opt/${APPLICATION}/.env
 | 
			
		||||
ExecStart=/usr/bin/pnpm run start
 | 
			
		||||
Restart=always
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
WantedBy=multi-user.target
 | 
			
		||||
EOF
 | 
			
		||||
 | 
			
		||||
cat <<EOF >/etc/systemd/system/browserless.service
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=Browserless service
 | 
			
		||||
After=network.target ${APPLICATION}.service
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
WorkingDirectory=/opt/browserless
 | 
			
		||||
EnvironmentFile=/opt/browserless/.env
 | 
			
		||||
ExecStart=/usr/bin/npm run start
 | 
			
		||||
Restart=unless-stopped
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
WantedBy=multi-user.target
 | 
			
		||||
EOF
 | 
			
		||||
systemctl daemon-reload
 | 
			
		||||
systemctl enable -q --now minio.service ${APPLICATION}.service browserless.service
 | 
			
		||||
msg_ok "Created Services"
 | 
			
		||||
 | 
			
		||||
motd_ssh
 | 
			
		||||
customize
 | 
			
		||||
 | 
			
		||||
msg_info "Cleaning up"
 | 
			
		||||
rm -f /tmp/v${RELEASE}.zip
 | 
			
		||||
rm -f /tmp/v${TAG}.zip
 | 
			
		||||
rm -f /tmp/minio.deb
 | 
			
		||||
$STD apt-get -y autoremove
 | 
			
		||||
$STD apt-get -y autoclean
 | 
			
		||||
msg_ok "Cleaned"
 | 
			
		||||
@@ -16,7 +16,8 @@ update_os
 | 
			
		||||
msg_info "Installing Dependencies"
 | 
			
		||||
$STD apt-get install -y \
 | 
			
		||||
  make \
 | 
			
		||||
  gcc
 | 
			
		||||
  gcc \
 | 
			
		||||
  gpg
 | 
			
		||||
msg_ok "Installed Dependencies"
 | 
			
		||||
 | 
			
		||||
msg_info "Installing Golang"
 | 
			
		||||
@@ -43,8 +44,8 @@ msg_ok "Installed Node.js"
 | 
			
		||||
msg_info "Setting up seelf. Patience"
 | 
			
		||||
RELEASE=$(curl -fsSL https://api.github.com/repos/YuukanOO/seelf/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }')
 | 
			
		||||
curl -fsSL "https://github.com/YuukanOO/seelf/archive/refs/tags/v${RELEASE}.tar.gz" -o $(basename "https://github.com/YuukanOO/seelf/archive/refs/tags/v${RELEASE}.tar.gz")
 | 
			
		||||
tar -xzf v${RELEASE}.tar.gz
 | 
			
		||||
mv seelf-${RELEASE}/ /opt/seelf
 | 
			
		||||
tar -xzf v"${RELEASE}".tar.gz
 | 
			
		||||
mv seelf-"${RELEASE}"/ /opt/seelf
 | 
			
		||||
cd /opt/seelf
 | 
			
		||||
$STD make build
 | 
			
		||||
PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)
 | 
			
		||||
@@ -84,11 +85,11 @@ customize
 | 
			
		||||
 | 
			
		||||
# Cleanup
 | 
			
		||||
msg_info "Cleaning up"
 | 
			
		||||
rm -f ~/v${RELEASE}.tar.gz
 | 
			
		||||
rm -f $temp_file
 | 
			
		||||
rm -f ~/v"${RELEASE}".tar.gz
 | 
			
		||||
rm -f "$temp_file"
 | 
			
		||||
$STD apt-get -y autoremove
 | 
			
		||||
$STD apt-get -y autoclean
 | 
			
		||||
msg_ok "Cleaned"
 | 
			
		||||
 | 
			
		||||
motd_ssh
 | 
			
		||||
customize
 | 
			
		||||
customize
 | 
			
		||||
 
 | 
			
		||||
@@ -15,10 +15,10 @@ update_os
 | 
			
		||||
 | 
			
		||||
msg_info "Installing Dependencies"
 | 
			
		||||
$STD apt-get install -y \
 | 
			
		||||
    git \
 | 
			
		||||
    gnupg \
 | 
			
		||||
    apache2 \
 | 
			
		||||
    libapache2-mod-wsgi-py3
 | 
			
		||||
  git \
 | 
			
		||||
  gnupg \
 | 
			
		||||
  apache2 \
 | 
			
		||||
  libapache2-mod-wsgi-py3
 | 
			
		||||
msg_ok "Installed Dependencies"
 | 
			
		||||
 | 
			
		||||
msg_info "Installing Python"
 | 
			
		||||
 
 | 
			
		||||
@@ -53,8 +53,8 @@ msg_info "Installing Zipline (Patience)"
 | 
			
		||||
cd /opt
 | 
			
		||||
RELEASE=$(curl -fsSL https://api.github.com/repos/diced/zipline/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }')
 | 
			
		||||
curl -fsSL "https://github.com/diced/zipline/archive/refs/tags/v${RELEASE}.zip" -o $(basename "https://github.com/diced/zipline/archive/refs/tags/v${RELEASE}.zip")
 | 
			
		||||
unzip -q v${RELEASE}.zip
 | 
			
		||||
mv zipline-${RELEASE} /opt/zipline
 | 
			
		||||
unzip -q v"${RELEASE}".zip
 | 
			
		||||
mv zipline-"${RELEASE}" /opt/zipline
 | 
			
		||||
cd /opt/zipline
 | 
			
		||||
cat <<EOF >/opt/zipline/.env
 | 
			
		||||
DATABASE_URL=postgres://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME
 | 
			
		||||
@@ -62,7 +62,10 @@ CORE_SECRET=$SECRET_KEY
 | 
			
		||||
CORE_HOSTNAME=0.0.0.0
 | 
			
		||||
CORE_PORT=3000
 | 
			
		||||
CORE_RETURN_HTTPS=false
 | 
			
		||||
DATASOURCE_TYPE=local
 | 
			
		||||
DATASOURCE_LOCAL_DIRECTORY=/opt/zipline-upload
 | 
			
		||||
EOF
 | 
			
		||||
mkdir -p /opt/zipline-upload
 | 
			
		||||
$STD pnpm install
 | 
			
		||||
$STD pnpm build
 | 
			
		||||
echo "${RELEASE}" >"/opt/${APPLICATION}_version.txt"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										867
									
								
								misc/add-iptag.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										867
									
								
								misc/add-iptag.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,867 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
# Copyright (c) 2021-2025 community-scripts ORG
 | 
			
		||||
# Author: MickLesk (Canbiz) && Desert_Gamer
 | 
			
		||||
# License: MIT
 | 
			
		||||
# Source: https://github.com/gitsang/iptag
 | 
			
		||||
 | 
			
		||||
function header_info {
 | 
			
		||||
  clear
 | 
			
		||||
  cat <<"EOF"
 | 
			
		||||
 ___ ____     _____
 | 
			
		||||
|_ _|  _ \ _ |_   _|_ _  __ _
 | 
			
		||||
 | || |_) (_)  | |/ _` |/ _` |
 | 
			
		||||
 | ||  __/ _   | | (_| | (_| |
 | 
			
		||||
|___|_|   (_)  |_|\__,_|\__, |
 | 
			
		||||
                        |___/
 | 
			
		||||
EOF
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
clear
 | 
			
		||||
header_info
 | 
			
		||||
APP="IP-Tag"
 | 
			
		||||
hostname=$(hostname)
 | 
			
		||||
 | 
			
		||||
# Farbvariablen
 | 
			
		||||
YW=$(echo "\033[33m")
 | 
			
		||||
GN=$(echo "\033[1;92m")
 | 
			
		||||
RD=$(echo "\033[01;31m")
 | 
			
		||||
CL=$(echo "\033[m")
 | 
			
		||||
BFR="\\r\\033[K"
 | 
			
		||||
HOLD=" "
 | 
			
		||||
CM=" ✔️ ${CL}"
 | 
			
		||||
CROSS=" ✖️ ${CL}"
 | 
			
		||||
 | 
			
		||||
# This function enables error handling in the script by setting options and defining a trap for the ERR signal.
 | 
			
		||||
catch_errors() {
 | 
			
		||||
  set -Eeuo pipefail
 | 
			
		||||
  trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# This function is called when an error occurs. It receives the exit code, line number, and command that caused the error, and displays an error message.
 | 
			
		||||
error_handler() {
 | 
			
		||||
  if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then
 | 
			
		||||
    kill $SPINNER_PID >/dev/null
 | 
			
		||||
  fi
 | 
			
		||||
  printf "\e[?25h"
 | 
			
		||||
  local exit_code="$?"
 | 
			
		||||
  local line_number="$1"
 | 
			
		||||
  local command="$2"
 | 
			
		||||
  local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}"
 | 
			
		||||
  echo -e "\n$error_message\n"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# This function displays a spinner.
 | 
			
		||||
spinner() {
 | 
			
		||||
  local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
 | 
			
		||||
  local spin_i=0
 | 
			
		||||
  local interval=0.1
 | 
			
		||||
  printf "\e[?25l"
 | 
			
		||||
 | 
			
		||||
  local color="${YWB}"
 | 
			
		||||
 | 
			
		||||
  while true; do
 | 
			
		||||
    printf "\r ${color}%s${CL}" "${frames[spin_i]}"
 | 
			
		||||
    spin_i=$(((spin_i + 1) % ${#frames[@]}))
 | 
			
		||||
    sleep "$interval"
 | 
			
		||||
  done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# This function displays an informational message with a yellow color.
 | 
			
		||||
msg_info() {
 | 
			
		||||
  local msg="$1"
 | 
			
		||||
  echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}"
 | 
			
		||||
  spinner &
 | 
			
		||||
  SPINNER_PID=$!
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# This function displays a success message with a green color.
 | 
			
		||||
msg_ok() {
 | 
			
		||||
  if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then
 | 
			
		||||
    kill $SPINNER_PID >/dev/null
 | 
			
		||||
  fi
 | 
			
		||||
  printf "\e[?25h"
 | 
			
		||||
  local msg="$1"
 | 
			
		||||
  echo -e "${BFR}${CM}${GN}${msg}${CL}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# This function displays a error message with a red color.
 | 
			
		||||
msg_error() {
 | 
			
		||||
  if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then
 | 
			
		||||
    kill $SPINNER_PID >/dev/null
 | 
			
		||||
  fi
 | 
			
		||||
  printf "\e[?25h"
 | 
			
		||||
  local msg="$1"
 | 
			
		||||
  echo -e "${BFR}${CROSS}${RD}${msg}${CL}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Check if service exists
 | 
			
		||||
check_service_exists() {
 | 
			
		||||
  if systemctl is-active --quiet iptag.service; then
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Migrate configuration from old path to new
 | 
			
		||||
migrate_config() {
 | 
			
		||||
  local old_config="/opt/lxc-iptag"
 | 
			
		||||
  local new_config="/opt/iptag/iptag.conf"
 | 
			
		||||
 | 
			
		||||
  if [[ -f "$old_config" ]]; then
 | 
			
		||||
    msg_info "Migrating configuration from old path"
 | 
			
		||||
    if cp "$old_config" "$new_config" &>/dev/null; then
 | 
			
		||||
      rm -rf "$old_config" &>/dev/null
 | 
			
		||||
      msg_ok "Configuration migrated and old config removed"
 | 
			
		||||
    else
 | 
			
		||||
      msg_error "Failed to migrate configuration"
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Update existing installation
 | 
			
		||||
update_installation() {
 | 
			
		||||
  msg_info "Updating IP-Tag Scripts"
 | 
			
		||||
  systemctl stop iptag.service &>/dev/null
 | 
			
		||||
 | 
			
		||||
  # Create directory if it doesn't exist
 | 
			
		||||
  if [[ ! -d "/opt/iptag" ]]; then
 | 
			
		||||
    mkdir -p /opt/iptag
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Migrate config if needed
 | 
			
		||||
  migrate_config
 | 
			
		||||
 | 
			
		||||
  # Update main script
 | 
			
		||||
  cat <<'EOF' >/opt/iptag/iptag
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
# =============== CONFIGURATION =============== #
 | 
			
		||||
readonly CONFIG_FILE="/opt/iptag/iptag.conf"
 | 
			
		||||
readonly DEFAULT_TAG_FORMAT="full"
 | 
			
		||||
readonly DEFAULT_CHECK_INTERVAL=60
 | 
			
		||||
 | 
			
		||||
# Load the configuration file if it exists
 | 
			
		||||
if [ -f "$CONFIG_FILE" ]; then
 | 
			
		||||
    # shellcheck source=./iptag.conf
 | 
			
		||||
    source "$CONFIG_FILE"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Convert IP to integer for comparison
 | 
			
		||||
ip_to_int() {
 | 
			
		||||
    local ip="$1"
 | 
			
		||||
    local a b c d
 | 
			
		||||
    IFS=. read -r a b c d <<< "${ip}"
 | 
			
		||||
    echo "$((a << 24 | b << 16 | c << 8 | d))"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Check if IP is in CIDR
 | 
			
		||||
ip_in_cidr() {
 | 
			
		||||
    local ip="$1" cidr="$2"
 | 
			
		||||
    ipcalc -c "$ip" "$cidr" >/dev/null 2>&1 || return 1
 | 
			
		||||
 | 
			
		||||
    local network prefix ip_parts net_parts
 | 
			
		||||
    network=$(echo "$cidr" | cut -d/ -f1)
 | 
			
		||||
    prefix=$(echo "$cidr" | cut -d/ -f2)
 | 
			
		||||
    IFS=. read -r -a ip_parts <<< "$ip"
 | 
			
		||||
    IFS=. read -r -a net_parts <<< "$network"
 | 
			
		||||
 | 
			
		||||
    case $prefix in
 | 
			
		||||
        8)  [[ "${ip_parts[0]}" == "${net_parts[0]}" ]] ;;
 | 
			
		||||
        16) [[ "${ip_parts[0]}.${ip_parts[1]}" == "${net_parts[0]}.${net_parts[1]}" ]] ;;
 | 
			
		||||
        24) [[ "${ip_parts[0]}.${ip_parts[1]}.${ip_parts[2]}" == "${net_parts[0]}.${net_parts[1]}.${net_parts[2]}" ]] ;;
 | 
			
		||||
        32) [[ "$ip" == "$network" ]] ;;
 | 
			
		||||
        *)  return 1 ;;
 | 
			
		||||
    esac
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Format IP address according to the configuration
 | 
			
		||||
format_ip_tag() {
 | 
			
		||||
    local ip="$1"
 | 
			
		||||
    local format="${TAG_FORMAT:-$DEFAULT_TAG_FORMAT}"
 | 
			
		||||
 | 
			
		||||
    case "$format" in
 | 
			
		||||
        "last_octet")     echo "${ip##*.}" ;;
 | 
			
		||||
        "last_two_octets") echo "${ip#*.*.}" ;;
 | 
			
		||||
        *)               echo "$ip" ;;
 | 
			
		||||
    esac
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Check if IP is in any CIDRs
 | 
			
		||||
ip_in_cidrs() {
 | 
			
		||||
    local ip="$1" cidrs="$2"
 | 
			
		||||
    [[ -z "$cidrs" ]] && return 1
 | 
			
		||||
    local IFS=' '
 | 
			
		||||
    for cidr in $cidrs; do ip_in_cidr "$ip" "$cidr" && return 0; done
 | 
			
		||||
    return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Check if IP is valid
 | 
			
		||||
is_valid_ipv4() {
 | 
			
		||||
    local ip="$1"
 | 
			
		||||
    [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || return 1
 | 
			
		||||
    
 | 
			
		||||
    local IFS='.' parts
 | 
			
		||||
    read -ra parts <<< "$ip"
 | 
			
		||||
    for part in "${parts[@]}"; do
 | 
			
		||||
        (( part >= 0 && part <= 255 )) || return 1
 | 
			
		||||
    done
 | 
			
		||||
    return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
lxc_status_changed() {
 | 
			
		||||
  current_lxc_status=$(pct list 2>/dev/null)
 | 
			
		||||
  if [ "${last_lxc_status}" == "${current_lxc_status}" ]; then
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    last_lxc_status="${current_lxc_status}"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
vm_status_changed() {
 | 
			
		||||
  current_vm_status=$(qm list 2>/dev/null)
 | 
			
		||||
  if [ "${last_vm_status}" == "${current_vm_status}" ]; then
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    last_vm_status="${current_vm_status}"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fw_net_interface_changed() {
 | 
			
		||||
  current_net_interface=$(ifconfig | grep "^fw")
 | 
			
		||||
  if [ "${last_net_interface}" == "${current_net_interface}" ]; then
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    last_net_interface="${current_net_interface}"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Get VM IPs using MAC addresses and ARP table
 | 
			
		||||
get_vm_ips() {
 | 
			
		||||
    local vmid=$1 ips="" macs found_ip=false
 | 
			
		||||
    qm status "$vmid" 2>/dev/null | grep -q "status: running" || return
 | 
			
		||||
 | 
			
		||||
    macs=$(qm config "$vmid" 2>/dev/null | grep -E 'net[0-9]+' | grep -oE '[a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}')
 | 
			
		||||
    [[ -z "$macs" ]] && return
 | 
			
		||||
 | 
			
		||||
    for mac in $macs; do
 | 
			
		||||
        local ip
 | 
			
		||||
        ip=$(arp -an 2>/dev/null | grep -i "$mac" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}')
 | 
			
		||||
        [[ -n "$ip" ]] && { ips+="$ip "; found_ip=true; }
 | 
			
		||||
    done
 | 
			
		||||
 | 
			
		||||
    if ! $found_ip; then
 | 
			
		||||
        local agent_ip
 | 
			
		||||
        agent_ip=$(qm agent "$vmid" network-get-interfaces 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' || true)
 | 
			
		||||
        [[ -n "$agent_ip" ]] && ips+="$agent_ip "
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    echo "${ips% }"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Update tags
 | 
			
		||||
update_tags() {
 | 
			
		||||
    local type="$1" vmid="$2" config_cmd="pct"
 | 
			
		||||
    [[ "$type" == "vm" ]] && config_cmd="qm"
 | 
			
		||||
 | 
			
		||||
    local current_ips_full
 | 
			
		||||
    if [[ "$type" == "lxc" ]]; then
 | 
			
		||||
        current_ips_full=$(lxc-info -n "${vmid}" -i 2>/dev/null | grep -E "^IP:" | awk '{print $2}')
 | 
			
		||||
    else
 | 
			
		||||
        current_ips_full=$(get_vm_ips "${vmid}")
 | 
			
		||||
    fi
 | 
			
		||||
    [[ -z "$current_ips_full" ]] && return
 | 
			
		||||
 | 
			
		||||
    local current_tags=() next_tags=() current_ip_tags=()
 | 
			
		||||
    mapfile -t current_tags < <($config_cmd config "${vmid}" 2>/dev/null | grep tags | awk '{print $2}' | sed 's/;/\n/g')
 | 
			
		||||
 | 
			
		||||
    # Separate IP and non-IP tags
 | 
			
		||||
    for tag in "${current_tags[@]}"; do
 | 
			
		||||
        if is_valid_ipv4 "${tag}" || [[ "$tag" =~ ^[0-9]+(\.[0-9]+)*$ ]]; then
 | 
			
		||||
            current_ip_tags+=("${tag}")
 | 
			
		||||
        else
 | 
			
		||||
            next_tags+=("${tag}")
 | 
			
		||||
        fi
 | 
			
		||||
    done
 | 
			
		||||
 | 
			
		||||
    local formatted_ips=() needs_update=false added_ips=()
 | 
			
		||||
    for ip in ${current_ips_full}; do
 | 
			
		||||
        if is_valid_ipv4 "$ip" && ip_in_cidrs "$ip" "${CIDR_LIST[*]}"; then
 | 
			
		||||
            local formatted_ip=$(format_ip_tag "$ip")
 | 
			
		||||
            formatted_ips+=("$formatted_ip")
 | 
			
		||||
            if [[ ! " ${current_ip_tags[*]} " =~ " ${formatted_ip} " ]]; then
 | 
			
		||||
                needs_update=true
 | 
			
		||||
                added_ips+=("$formatted_ip")
 | 
			
		||||
                next_tags+=("$formatted_ip")
 | 
			
		||||
            fi
 | 
			
		||||
        fi
 | 
			
		||||
    done
 | 
			
		||||
 | 
			
		||||
    [[ ${#formatted_ips[@]} -eq 0 ]] && return
 | 
			
		||||
 | 
			
		||||
    # Add existing IP tags that are still valid
 | 
			
		||||
    for tag in "${current_ip_tags[@]}"; do
 | 
			
		||||
        if [[ " ${formatted_ips[*]} " =~ " ${tag} " ]]; then
 | 
			
		||||
            if [[ ! " ${next_tags[*]} " =~ " ${tag} " ]]; then
 | 
			
		||||
                next_tags+=("$tag")
 | 
			
		||||
            fi
 | 
			
		||||
        fi
 | 
			
		||||
    done
 | 
			
		||||
 | 
			
		||||
    if [[ "$needs_update" == true ]]; then
 | 
			
		||||
        echo "${type^} ${vmid}: adding IP tags: ${added_ips[*]}"
 | 
			
		||||
        $config_cmd set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" &>/dev/null
 | 
			
		||||
    elif [[ ${#current_ip_tags[@]} -gt 0 ]]; then
 | 
			
		||||
        echo "${type^} ${vmid}: IP tags already set: ${current_ip_tags[*]}"
 | 
			
		||||
    else
 | 
			
		||||
        echo "${type^} ${vmid}: setting initial IP tags: ${formatted_ips[*]}"
 | 
			
		||||
        $config_cmd set "${vmid}" -tags "$(IFS=';'; echo "${formatted_ips[*]}")" &>/dev/null
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Check if status changed
 | 
			
		||||
check_status() {
 | 
			
		||||
    local type="$1" current
 | 
			
		||||
    case "$type" in
 | 
			
		||||
        "lxc") current=$(pct list 2>/dev/null | grep -v VMID) ;;
 | 
			
		||||
        "vm")  current=$(qm list 2>/dev/null | grep -v VMID) ;;
 | 
			
		||||
        "fw")  current=$(ifconfig 2>/dev/null | grep "^fw") ;;
 | 
			
		||||
    esac
 | 
			
		||||
    local last_var="last_${type}_status"
 | 
			
		||||
    [[ "${!last_var}" == "$current" ]] && return 1
 | 
			
		||||
    eval "$last_var='$current'"
 | 
			
		||||
    return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Update all instances
 | 
			
		||||
update_all() {
 | 
			
		||||
    local type="$1" list_cmd="pct" vmids count=0
 | 
			
		||||
    [[ "$type" == "vm" ]] && list_cmd="qm"
 | 
			
		||||
    
 | 
			
		||||
    vmids=$($list_cmd list 2>/dev/null | grep -v VMID | awk '{print $1}')
 | 
			
		||||
    for vmid in $vmids; do ((count++)); done
 | 
			
		||||
    
 | 
			
		||||
    echo "Found ${count} running ${type}s"
 | 
			
		||||
    [[ $count -eq 0 ]] && return
 | 
			
		||||
 | 
			
		||||
    for vmid in $vmids; do 
 | 
			
		||||
        update_tags "$type" "$vmid"
 | 
			
		||||
    done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Main check function
 | 
			
		||||
check() {
 | 
			
		||||
    local current_time changes_detected=false
 | 
			
		||||
    current_time=$(date +%s)
 | 
			
		||||
 | 
			
		||||
    for type in "lxc" "vm"; do
 | 
			
		||||
        local interval_var="${type^^}_STATUS_CHECK_INTERVAL"
 | 
			
		||||
        local last_check_var="last_${type}_check_time"
 | 
			
		||||
        local last_update_var="last_update_${type}_time"
 | 
			
		||||
        
 | 
			
		||||
        if [[ "${!interval_var}" -gt 0 ]] && (( current_time - ${!last_check_var} >= ${!interval_var} )); then
 | 
			
		||||
            echo "Checking ${type^^} status..."
 | 
			
		||||
            eval "${last_check_var}=\$current_time"
 | 
			
		||||
            if check_status "$type"; then
 | 
			
		||||
                changes_detected=true
 | 
			
		||||
                update_all "$type"
 | 
			
		||||
                eval "${last_update_var}=\$current_time"
 | 
			
		||||
            fi
 | 
			
		||||
        fi
 | 
			
		||||
 | 
			
		||||
        if (( current_time - ${!last_update_var} >= FORCE_UPDATE_INTERVAL )); then
 | 
			
		||||
            echo "Force updating ${type} tags..."
 | 
			
		||||
            changes_detected=true
 | 
			
		||||
            update_all "$type"
 | 
			
		||||
            eval "${last_update_var}=\$current_time"
 | 
			
		||||
        fi
 | 
			
		||||
    done
 | 
			
		||||
 | 
			
		||||
    if [[ "${FW_NET_INTERFACE_CHECK_INTERVAL}" -gt 0 ]] && \
 | 
			
		||||
       (( current_time - last_fw_check_time >= FW_NET_INTERFACE_CHECK_INTERVAL )); then
 | 
			
		||||
        echo "Checking network interfaces..."
 | 
			
		||||
        last_fw_check_time=$current_time
 | 
			
		||||
        if check_status "fw"; then
 | 
			
		||||
            changes_detected=true
 | 
			
		||||
            update_all "lxc"
 | 
			
		||||
            update_all "vm"
 | 
			
		||||
            last_update_lxc_time=$current_time
 | 
			
		||||
            last_update_vm_time=$current_time
 | 
			
		||||
        fi
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    $changes_detected || echo "No changes detected in system status"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Initialize time variables
 | 
			
		||||
declare -g last_lxc_status="" last_vm_status="" last_fw_status=""
 | 
			
		||||
declare -g last_lxc_check_time=0 last_vm_check_time=0 last_fw_check_time=0
 | 
			
		||||
declare -g last_update_lxc_time=0 last_update_vm_time=0
 | 
			
		||||
 | 
			
		||||
# Main loop
 | 
			
		||||
main() {
 | 
			
		||||
    while true; do
 | 
			
		||||
        check
 | 
			
		||||
        sleep "${LOOP_INTERVAL:-$DEFAULT_CHECK_INTERVAL}"
 | 
			
		||||
    done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
main
 | 
			
		||||
EOF
 | 
			
		||||
  chmod +x /opt/iptag/iptag
 | 
			
		||||
 | 
			
		||||
  # Update service file
 | 
			
		||||
  cat <<EOF >/lib/systemd/system/iptag.service
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=IP-Tag service
 | 
			
		||||
After=network.target
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
Type=simple
 | 
			
		||||
ExecStart=/opt/iptag/iptag
 | 
			
		||||
Restart=always
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
WantedBy=multi-user.target
 | 
			
		||||
EOF
 | 
			
		||||
 | 
			
		||||
  systemctl daemon-reload &>/dev/null
 | 
			
		||||
  systemctl enable -q --now iptag.service &>/dev/null
 | 
			
		||||
  msg_ok "Updated IP-Tag Scripts"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Main installation process
 | 
			
		||||
if check_service_exists; then
 | 
			
		||||
  while true; do
 | 
			
		||||
    read -p "IP-Tag service is already installed. Do you want to update it? (y/n): " yn
 | 
			
		||||
    case $yn in
 | 
			
		||||
    [Yy]*)
 | 
			
		||||
      update_installation
 | 
			
		||||
      exit 0
 | 
			
		||||
      ;;
 | 
			
		||||
    [Nn]*)
 | 
			
		||||
      msg_error "Installation cancelled."
 | 
			
		||||
      exit 0
 | 
			
		||||
      ;;
 | 
			
		||||
    *)
 | 
			
		||||
      msg_error "Please answer yes or no."
 | 
			
		||||
      ;;
 | 
			
		||||
    esac
 | 
			
		||||
  done
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
while true; do
 | 
			
		||||
  read -p "This will install ${APP} on ${hostname}. Proceed? (y/n): " yn
 | 
			
		||||
  case $yn in
 | 
			
		||||
  [Yy]*)
 | 
			
		||||
    break
 | 
			
		||||
    ;;
 | 
			
		||||
  [Nn]*)
 | 
			
		||||
    msg_error "Installation cancelled."
 | 
			
		||||
    exit
 | 
			
		||||
    ;;
 | 
			
		||||
  *)
 | 
			
		||||
    msg_error "Please answer yes or no."
 | 
			
		||||
    ;;
 | 
			
		||||
  esac
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
if ! pveversion | grep -Eq "pve-manager/8\.[0-4](\.[0-9]+)*"; then
 | 
			
		||||
  msg_error "This version of Proxmox Virtual Environment is not supported"
 | 
			
		||||
  msg_error "⚠️ Requires Proxmox Virtual Environment Version 8.0 or later."
 | 
			
		||||
  msg_error "Exiting..."
 | 
			
		||||
  sleep 2
 | 
			
		||||
  exit
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
FILE_PATH="/usr/local/bin/iptag"
 | 
			
		||||
if [[ -f "$FILE_PATH" ]]; then
 | 
			
		||||
  msg_info "The file already exists: '$FILE_PATH'. Skipping installation."
 | 
			
		||||
  exit 0
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
msg_info "Installing Dependencies"
 | 
			
		||||
apt-get update &>/dev/null
 | 
			
		||||
apt-get install -y ipcalc net-tools &>/dev/null
 | 
			
		||||
msg_ok "Installed Dependencies"
 | 
			
		||||
 | 
			
		||||
msg_info "Setting up IP-Tag Scripts"
 | 
			
		||||
mkdir -p /opt/iptag
 | 
			
		||||
msg_ok "Setup IP-Tag Scripts"
 | 
			
		||||
 | 
			
		||||
# Migrate config if needed
 | 
			
		||||
migrate_config
 | 
			
		||||
 | 
			
		||||
msg_info "Setup Default Config"
 | 
			
		||||
if [[ ! -f /opt/iptag/iptag.conf ]]; then
 | 
			
		||||
  cat <<EOF >/opt/iptag/iptag.conf
 | 
			
		||||
# Configuration file for LXC IP tagging
 | 
			
		||||
 | 
			
		||||
# List of allowed CIDRs
 | 
			
		||||
CIDR_LIST=(
 | 
			
		||||
  192.168.0.0/16
 | 
			
		||||
  172.16.0.0/12
 | 
			
		||||
  10.0.0.0/8
 | 
			
		||||
  100.64.0.0/10
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# Tag format options:
 | 
			
		||||
# - "full": full IP address (e.g., 192.168.0.100)
 | 
			
		||||
# - "last_octet": only the last octet (e.g., 100)
 | 
			
		||||
# - "last_two_octets": last two octets (e.g., 0.100)
 | 
			
		||||
TAG_FORMAT="full"
 | 
			
		||||
 | 
			
		||||
# Interval settings (in seconds)
 | 
			
		||||
LOOP_INTERVAL=60
 | 
			
		||||
VM_STATUS_CHECK_INTERVAL=60
 | 
			
		||||
FW_NET_INTERFACE_CHECK_INTERVAL=60
 | 
			
		||||
LXC_STATUS_CHECK_INTERVAL=60
 | 
			
		||||
FORCE_UPDATE_INTERVAL=1800
 | 
			
		||||
EOF
 | 
			
		||||
  msg_ok "Setup default config"
 | 
			
		||||
else
 | 
			
		||||
  msg_ok "Default config already exists"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
msg_info "Setup Main Function"
 | 
			
		||||
if [[ ! -f /opt/iptag/iptag ]]; then
 | 
			
		||||
  cat <<'EOF' >/opt/iptag/iptag
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
# =============== CONFIGURATION =============== #
 | 
			
		||||
CONFIG_FILE="/opt/iptag/iptag.conf"
 | 
			
		||||
 | 
			
		||||
# Load the configuration file if it exists
 | 
			
		||||
if [ -f "$CONFIG_FILE" ]; then
 | 
			
		||||
  # shellcheck source=./iptag.conf
 | 
			
		||||
  source "$CONFIG_FILE"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Convert IP to integer for comparison
 | 
			
		||||
ip_to_int() {
 | 
			
		||||
  local ip="$1"
 | 
			
		||||
  local a b c d
 | 
			
		||||
  IFS=. read -r a b c d <<< "${ip}"
 | 
			
		||||
  echo "$((a << 24 | b << 16 | c << 8 | d))"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Check if IP is in CIDR
 | 
			
		||||
ip_in_cidr() {
 | 
			
		||||
  local ip="$1"
 | 
			
		||||
  local cidr="$2"
 | 
			
		||||
 | 
			
		||||
  # Use ipcalc with the -c option (check), which returns 0 if the IP is in the network
 | 
			
		||||
  if ipcalc -c "$ip" "$cidr" >/dev/null 2>&1; then
 | 
			
		||||
    # Get network address and mask from CIDR
 | 
			
		||||
    local network prefix
 | 
			
		||||
    network=$(echo "$cidr" | cut -d/ -f1)
 | 
			
		||||
    prefix=$(echo "$cidr" | cut -d/ -f2)
 | 
			
		||||
 | 
			
		||||
    # Check if IP is in the network
 | 
			
		||||
    local ip_a ip_b ip_c ip_d net_a net_b net_c net_d
 | 
			
		||||
    IFS=. read -r ip_a ip_b ip_c ip_d <<< "$ip"
 | 
			
		||||
    IFS=. read -r net_a net_b net_c net_d <<< "$network"
 | 
			
		||||
 | 
			
		||||
    # Check octets match based on prefix length
 | 
			
		||||
    local result=0
 | 
			
		||||
    if (( prefix >= 8 )); then
 | 
			
		||||
      [[ "$ip_a" != "$net_a" ]] && result=1
 | 
			
		||||
    fi
 | 
			
		||||
    if (( prefix >= 16 )); then
 | 
			
		||||
      [[ "$ip_b" != "$net_b" ]] && result=1
 | 
			
		||||
    fi
 | 
			
		||||
    if (( prefix >= 24 )); then
 | 
			
		||||
      [[ "$ip_c" != "$net_c" ]] && result=1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    return $result
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Format IP address according to the configuration
 | 
			
		||||
format_ip_tag() {
 | 
			
		||||
  local ip="$1"
 | 
			
		||||
  local format="${TAG_FORMAT:-full}"
 | 
			
		||||
 | 
			
		||||
  case "$format" in
 | 
			
		||||
    "last_octet")
 | 
			
		||||
      echo "${ip##*.}"
 | 
			
		||||
      ;;
 | 
			
		||||
    "last_two_octets")
 | 
			
		||||
      echo "${ip#*.*.}"
 | 
			
		||||
      ;;
 | 
			
		||||
    *)
 | 
			
		||||
      echo "$ip"
 | 
			
		||||
      ;;
 | 
			
		||||
  esac
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Check if IP is in any CIDRs
 | 
			
		||||
ip_in_cidrs() {
 | 
			
		||||
  local ip="$1"
 | 
			
		||||
  local cidrs="$2"
 | 
			
		||||
 | 
			
		||||
  # Check that cidrs is not empty
 | 
			
		||||
  [[ -z "$cidrs" ]] && return 1
 | 
			
		||||
 | 
			
		||||
  local IFS=' '
 | 
			
		||||
  for cidr in $cidrs; do
 | 
			
		||||
    ip_in_cidr "$ip" "$cidr" && return 0
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Check if IP is valid
 | 
			
		||||
is_valid_ipv4() {
 | 
			
		||||
  local ip="$1"
 | 
			
		||||
  [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || return 1
 | 
			
		||||
  local IFS='.'
 | 
			
		||||
  read -ra parts <<< "$ip"
 | 
			
		||||
  for part in "${parts[@]}"; do
 | 
			
		||||
    [[ "$part" =~ ^[0-9]+$ ]] && ((part >= 0 && part <= 255)) || return 1
 | 
			
		||||
  done
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
lxc_status_changed() {
 | 
			
		||||
  current_lxc_status=$(pct list 2>/dev/null)
 | 
			
		||||
  if [ "${last_lxc_status}" == "${current_lxc_status}" ]; then
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    last_lxc_status="${current_lxc_status}"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
vm_status_changed() {
 | 
			
		||||
  current_vm_status=$(qm list 2>/dev/null)
 | 
			
		||||
  if [ "${last_vm_status}" == "${current_vm_status}" ]; then
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    last_vm_status="${current_vm_status}"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fw_net_interface_changed() {
 | 
			
		||||
  current_net_interface=$(ifconfig | grep "^fw")
 | 
			
		||||
  if [ "${last_net_interface}" == "${current_net_interface}" ]; then
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    last_net_interface="${current_net_interface}"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Get VM IPs using MAC addresses and ARP table
 | 
			
		||||
get_vm_ips() {
 | 
			
		||||
  local vmid=$1
 | 
			
		||||
  local ips=""
 | 
			
		||||
 | 
			
		||||
  # Check if VM is running
 | 
			
		||||
  qm status "$vmid" 2>/dev/null | grep -q "status: running" || return
 | 
			
		||||
 | 
			
		||||
  # Get MAC addresses from VM configuration
 | 
			
		||||
  local macs
 | 
			
		||||
  macs=$(qm config "$vmid" 2>/dev/null | grep -E 'net[0-9]+' | grep -o -E '[a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}')
 | 
			
		||||
 | 
			
		||||
  # Look up IPs from ARP table using MAC addresses
 | 
			
		||||
  for mac in $macs; do
 | 
			
		||||
    local ip
 | 
			
		||||
    ip=$(arp -an 2>/dev/null | grep -i "$mac" | grep -o -E '([0-9]{1,3}\.){3}[0-9]{1,3}')
 | 
			
		||||
    if [ -n "$ip" ]; then
 | 
			
		||||
      ips+="$ip "
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  echo "$ips"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Update tags for container or VM
 | 
			
		||||
update_tags() {
 | 
			
		||||
  local type="$1"
 | 
			
		||||
  local vmid="$2"
 | 
			
		||||
  local config_cmd="pct"
 | 
			
		||||
  [[ "$type" == "vm" ]] && config_cmd="qm"
 | 
			
		||||
 | 
			
		||||
  # Get current IPs
 | 
			
		||||
  local current_ips_full
 | 
			
		||||
  if [[ "$type" == "lxc" ]]; then
 | 
			
		||||
    # Redirect error output to suppress AppArmor warnings
 | 
			
		||||
    current_ips_full=$(lxc-info -n "${vmid}" -i 2>/dev/null | grep -E "^IP:" | awk '{print $2}')
 | 
			
		||||
  else
 | 
			
		||||
    current_ips_full=$(get_vm_ips "${vmid}")
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Parse current tags and get valid IPs
 | 
			
		||||
  local current_tags=()
 | 
			
		||||
  local next_tags=()
 | 
			
		||||
  mapfile -t current_tags < <($config_cmd config "${vmid}" 2>/dev/null | grep tags | awk '{print $2}' | sed 's/;/\n/g')
 | 
			
		||||
 | 
			
		||||
  for tag in "${current_tags[@]}"; do
 | 
			
		||||
    # Skip tag if it looks like an IP (full or partial)
 | 
			
		||||
    if ! is_valid_ipv4 "${tag}" && ! [[ "$tag" =~ ^[0-9]+(\.[0-9]+)*$ ]]; then
 | 
			
		||||
      next_tags+=("${tag}")
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  # Add valid IPs to tags
 | 
			
		||||
  local added_ips=()
 | 
			
		||||
  local skipped_ips=()
 | 
			
		||||
 | 
			
		||||
  for ip in ${current_ips_full}; do
 | 
			
		||||
    if is_valid_ipv4 "${ip}"; then
 | 
			
		||||
      if ip_in_cidrs "${ip}" "${CIDR_LIST[*]}"; then
 | 
			
		||||
        local formatted_ip=$(format_ip_tag "$ip")
 | 
			
		||||
        next_tags+=("${formatted_ip}")
 | 
			
		||||
        added_ips+=("${formatted_ip}")
 | 
			
		||||
      else
 | 
			
		||||
        skipped_ips+=("${ip}")
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  # Log only if there are changes
 | 
			
		||||
  if [ ${#added_ips[@]} -gt 0 ]; then
 | 
			
		||||
    echo "${type^} ${vmid}: added IP tags: ${added_ips[*]}"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Update if changed
 | 
			
		||||
  if [[ "$(IFS=';'; echo "${current_tags[*]}")" != "$(IFS=';'; echo "${next_tags[*]}")" ]]; then
 | 
			
		||||
    $config_cmd set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" &>/dev/null
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Check if status changed
 | 
			
		||||
check_status_changed() {
 | 
			
		||||
  local type="$1"
 | 
			
		||||
  local current_status
 | 
			
		||||
 | 
			
		||||
  case "$type" in
 | 
			
		||||
    "lxc")
 | 
			
		||||
      current_status=$(pct list 2>/dev/null | grep -v VMID)
 | 
			
		||||
      [[ "${last_lxc_status}" == "${current_status}" ]] && return 1
 | 
			
		||||
      last_lxc_status="${current_status}"
 | 
			
		||||
      ;;
 | 
			
		||||
    "vm")
 | 
			
		||||
      current_status=$(qm list 2>/dev/null | grep -v VMID)
 | 
			
		||||
      [[ "${last_vm_status}" == "${current_status}" ]] && return 1
 | 
			
		||||
      last_vm_status="${current_status}"
 | 
			
		||||
      ;;
 | 
			
		||||
    "fw")
 | 
			
		||||
      current_status=$(ifconfig 2>/dev/null | grep "^fw")
 | 
			
		||||
      [[ "${last_net_interface}" == "${current_status}" ]] && return 1
 | 
			
		||||
      last_net_interface="${current_status}"
 | 
			
		||||
      ;;
 | 
			
		||||
  esac
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
check() {
 | 
			
		||||
  current_time=$(date +%s)
 | 
			
		||||
 | 
			
		||||
  # Check LXC status
 | 
			
		||||
  time_since_last_lxc_status_check=$((current_time - last_lxc_status_check_time))
 | 
			
		||||
  if [[ "${LXC_STATUS_CHECK_INTERVAL}" -gt 0 ]] \
 | 
			
		||||
    && [[ "${time_since_last_lxc_status_check}" -ge "${LXC_STATUS_CHECK_INTERVAL}" ]]; then
 | 
			
		||||
    echo "Checking LXC status..."
 | 
			
		||||
    last_lxc_status_check_time=${current_time}
 | 
			
		||||
    if check_status_changed "lxc"; then
 | 
			
		||||
      update_all_tags "lxc"
 | 
			
		||||
      last_update_lxc_time=${current_time}
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Check VM status
 | 
			
		||||
  time_since_last_vm_status_check=$((current_time - last_vm_status_check_time))
 | 
			
		||||
  if [[ "${VM_STATUS_CHECK_INTERVAL}" -gt 0 ]] \
 | 
			
		||||
    && [[ "${time_since_last_vm_status_check}" -ge "${VM_STATUS_CHECK_INTERVAL}" ]]; then
 | 
			
		||||
    echo "Checking VM status..."
 | 
			
		||||
    last_vm_status_check_time=${current_time}
 | 
			
		||||
    if check_status_changed "vm"; then
 | 
			
		||||
      update_all_tags "vm"
 | 
			
		||||
      last_update_vm_time=${current_time}
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Check network interface changes
 | 
			
		||||
  time_since_last_fw_net_interface_check=$((current_time - last_fw_net_interface_check_time))
 | 
			
		||||
  if [[ "${FW_NET_INTERFACE_CHECK_INTERVAL}" -gt 0 ]] \
 | 
			
		||||
    && [[ "${time_since_last_fw_net_interface_check}" -ge "${FW_NET_INTERFACE_CHECK_INTERVAL}" ]]; then
 | 
			
		||||
    echo "Checking network interfaces..."
 | 
			
		||||
    last_fw_net_interface_check_time=${current_time}
 | 
			
		||||
    if check_status_changed "fw"; then
 | 
			
		||||
      update_all_tags "lxc"
 | 
			
		||||
      update_all_tags "vm"
 | 
			
		||||
      last_update_lxc_time=${current_time}
 | 
			
		||||
      last_update_vm_time=${current_time}
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Force update if needed
 | 
			
		||||
  for type in "lxc" "vm"; do
 | 
			
		||||
    local last_update_var="last_update_${type}_time"
 | 
			
		||||
    local time_since_last_update=$((current_time - ${!last_update_var}))
 | 
			
		||||
    if [ ${time_since_last_update} -ge ${FORCE_UPDATE_INTERVAL} ]; then
 | 
			
		||||
      echo "Force updating ${type} tags..."
 | 
			
		||||
      update_all_tags "$type"
 | 
			
		||||
      eval "${last_update_var}=${current_time}"
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Initialize time variables
 | 
			
		||||
last_lxc_status_check_time=0
 | 
			
		||||
last_vm_status_check_time=0
 | 
			
		||||
last_fw_net_interface_check_time=0
 | 
			
		||||
last_update_lxc_time=0
 | 
			
		||||
last_update_vm_time=0
 | 
			
		||||
 | 
			
		||||
# main: Set the IP tags for all LXC containers and VMs
 | 
			
		||||
main() {
 | 
			
		||||
  while true; do
 | 
			
		||||
    check
 | 
			
		||||
    sleep "${LOOP_INTERVAL}"
 | 
			
		||||
  done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
main
 | 
			
		||||
EOF
 | 
			
		||||
  msg_ok "Setup Main Function"
 | 
			
		||||
else
 | 
			
		||||
  msg_ok "Main Function already exists"
 | 
			
		||||
fi
 | 
			
		||||
chmod +x /opt/iptag/iptag
 | 
			
		||||
 | 
			
		||||
msg_info "Creating Service"
 | 
			
		||||
if [[ ! -f /lib/systemd/system/iptag.service ]]; then
 | 
			
		||||
  cat <<EOF >/lib/systemd/system/iptag.service
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=IP-Tag service
 | 
			
		||||
After=network.target
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
Type=simple
 | 
			
		||||
ExecStart=/opt/iptag/iptag
 | 
			
		||||
Restart=always
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
WantedBy=multi-user.target
 | 
			
		||||
EOF
 | 
			
		||||
  msg_ok "Created Service"
 | 
			
		||||
else
 | 
			
		||||
  msg_ok "Service already exists."
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
msg_ok "Setup IP-Tag Scripts"
 | 
			
		||||
 | 
			
		||||
msg_info "Starting Service"
 | 
			
		||||
systemctl daemon-reload &>/dev/null
 | 
			
		||||
systemctl enable -q --now iptag.service &>/dev/null
 | 
			
		||||
msg_ok "Started Service"
 | 
			
		||||
SPINNER_PID=""
 | 
			
		||||
echo -e "\n${APP} installation completed successfully! ${CL}\n"
 | 
			
		||||
@@ -179,8 +179,20 @@ EOF
 | 
			
		||||
      msg_info "Disabling high availability"
 | 
			
		||||
      systemctl disable -q --now pve-ha-lrm
 | 
			
		||||
      systemctl disable -q --now pve-ha-crm
 | 
			
		||||
      systemctl disable -q --now corosync
 | 
			
		||||
      msg_ok "Disabled high availability"
 | 
			
		||||
      CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "COROSYNC" --menu "Disable Corosync for a Proxmox VE Cluster?" 10 58 2 \
 | 
			
		||||
        "yes" " " \
 | 
			
		||||
        "no" " " 3>&2 2>&1 1>&3)
 | 
			
		||||
      case $CHOICE in
 | 
			
		||||
      yes)
 | 
			
		||||
        msg_info "Disabling Corosync"
 | 
			
		||||
        systemctl disable -q --now corosync
 | 
			
		||||
        msg_ok "Disabled Corosync"
 | 
			
		||||
        ;;
 | 
			
		||||
      no)
 | 
			
		||||
        msg_error "Selected no to Disabling Corosync"
 | 
			
		||||
        ;;
 | 
			
		||||
      esac
 | 
			
		||||
      ;;
 | 
			
		||||
    no)
 | 
			
		||||
      msg_error "Selected no to Disabling high availability"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user