mirror of
				https://github.com/community-scripts/ProxmoxVE.git
				synced 2025-11-04 10:22:50 +00:00 
			
		
		
		
	Compare commits
	
		
			12 Commits
		
	
	
		
			2025-02-13
			...
			2025-02-14
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					347a23ad60 | ||
| 
						 | 
					29806c4525 | ||
| 
						 | 
					a045dc8012 | ||
| 
						 | 
					80e7e2f5b6 | ||
| 
						 | 
					1789d181aa | ||
| 
						 | 
					f0ca0c3379 | ||
| 
						 | 
					1711e44a4d | ||
| 
						 | 
					afe4af2ff6 | ||
| 
						 | 
					ec8c564e25 | ||
| 
						 | 
					fd9d64b342 | ||
| 
						 | 
					1d928f7ea8 | ||
| 
						 | 
					f3c9e8f013 | 
							
								
								
									
										2
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
name: "🐞 Script Issue Report"
 | 
			
		||||
description: Report a specific issue with a script. For other inquiries, please use the Discussions section.
 | 
			
		||||
 | 
			
		||||
labels: ["bug"]
 | 
			
		||||
body:
 | 
			
		||||
  - type: markdown
 | 
			
		||||
    attributes:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
								
							@@ -3,7 +3,7 @@ contact_links:
 | 
			
		||||
    - name: 🤔 Questions and Help
 | 
			
		||||
      url: https://github.com/community-scripts/ProxmoxVE/discussions
 | 
			
		||||
      about: For suggestions or questions, please use the Discussions section.
 | 
			
		||||
    - name: 🌟 Feature request
 | 
			
		||||
    - name: 🌟 new Script request
 | 
			
		||||
      url: https://github.com/community-scripts/ProxmoxVE/discussions/new?category=request-script
 | 
			
		||||
      about: For feature/script requests, please use the Discussions section.
 | 
			
		||||
    - name: 💻 Discord
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								.github/ISSUE_TEMPLATE/feature_request.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								.github/ISSUE_TEMPLATE/feature_request.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
name: "✨ Feature Request"
 | 
			
		||||
description: "Suggest a new feature or enhancement."
 | 
			
		||||
labels: ["enhancement"]
 | 
			
		||||
body:
 | 
			
		||||
  - type: markdown
 | 
			
		||||
    attributes:
 | 
			
		||||
      value: |
 | 
			
		||||
        # ✨ **Feature Request**
 | 
			
		||||
        Have an idea for a new feature? Share your thoughts below!
 | 
			
		||||
 | 
			
		||||
  - type: input
 | 
			
		||||
    id: feature_summary
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: "🌟 Briefly describe the feature"
 | 
			
		||||
      placeholder: "e.g., Add support for XYZ"
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    id: feature_description
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: "📝 Detailed description"
 | 
			
		||||
      placeholder: "Explain the feature in detail"
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    id: use_case
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: "💡 Why is this useful?"
 | 
			
		||||
      placeholder: "Describe the benefit of this feature"
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
							
								
								
									
										25
									
								
								.github/ISSUE_TEMPLATE/task.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								.github/ISSUE_TEMPLATE/task.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
name: "🛠️ Task / General Request"
 | 
			
		||||
description: "Request a general task, improvement, or refactor."
 | 
			
		||||
labels: ["task"]
 | 
			
		||||
body:
 | 
			
		||||
  - type: markdown
 | 
			
		||||
    attributes:
 | 
			
		||||
      value: |
 | 
			
		||||
        # 🛠️ **Task / General Request**
 | 
			
		||||
        Request a task that isn't a bug or feature request.
 | 
			
		||||
 | 
			
		||||
  - type: input
 | 
			
		||||
    id: task_summary
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: "📌 Task summary"
 | 
			
		||||
      placeholder: "e.g., Refactor XYZ"
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    id: task_details
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: "📋 Task details"
 | 
			
		||||
      placeholder: "Explain what needs to be done"
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
							
								
								
									
										10
									
								
								.github/workflows/script-test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/workflows/script-test.yml
									
									
									
									
										vendored
									
									
								
							@@ -89,10 +89,12 @@ jobs:
 | 
			
		||||
                chmod +x "$INSTALL_SCRIPT"
 | 
			
		||||
                RUNNING_FILE=$FILE
 | 
			
		||||
            fi
 | 
			
		||||
            git checkout origin/main .github/workflows/scripts/app-test/pr-build.func
 | 
			
		||||
            git checkout origin/main .github/workflows/scripts/app-test/pr-install.func
 | 
			
		||||
            git checkout origin/main .github/workflows/scripts/app-test/pr-alpine-install.func
 | 
			
		||||
            git checkout origin/main .github/workflows/scripts/app-test/pr-create-lxc.sh
 | 
			
		||||
            git remote add community-scripts https://github.com/community-scripts/ProxmoxVE.git
 | 
			
		||||
            git fetch community-scripts
 | 
			
		||||
            git checkout community-scripts/main -- .github/workflows/scripts/app-test/pr-build.func
 | 
			
		||||
            git checkout community-scripts/main -- .github/workflows/scripts/app-test/pr-install.func
 | 
			
		||||
            git checkout community-scripts/main -- .github/workflows/scripts/app-test/pr-alpine-install.func
 | 
			
		||||
            git checkout community-scripts/main -- .github/workflows/scripts/app-test/pr-create-lxc.sh
 | 
			
		||||
            chmod +x $RUNNING_FILE         
 | 
			
		||||
            chmod +x .github/workflows/scripts/app-test/pr-create-lxc.sh
 | 
			
		||||
            chmod +x .github/workflows/scripts/app-test/pr-install.func
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -17,6 +17,28 @@ All LXC instances created using this repository come pre-installed with Midnight
 | 
			
		||||
Do not break established syntax in this file, as it is automatically updated by a Github Workflow
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 2025-02-14
 | 
			
		||||
 | 
			
		||||
### Changes
 | 
			
		||||
 | 
			
		||||
### 🚀 Updated Scripts
 | 
			
		||||
 | 
			
		||||
- Fix homarr [@CrazyWolf13](https://github.com/CrazyWolf13) ([#2369](https://github.com/community-scripts/ProxmoxVE/pull/2369))
 | 
			
		||||
 | 
			
		||||
### 🌐 Website
 | 
			
		||||
 | 
			
		||||
- RustDesk Server - Added configuration guide to json [@tremor021](https://github.com/tremor021) ([#2389](https://github.com/community-scripts/ProxmoxVE/pull/2389))
 | 
			
		||||
 | 
			
		||||
### 🧰 Maintenance
 | 
			
		||||
 | 
			
		||||
- [gh] Update script-test.yml [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2399](https://github.com/community-scripts/ProxmoxVE/pull/2399))
 | 
			
		||||
- [gh] Introducing new Issue Github Template Feature (Bug, Feature, Task) [@MickLesk](https://github.com/MickLesk) ([#2394](https://github.com/community-scripts/ProxmoxVE/pull/2394))
 | 
			
		||||
 | 
			
		||||
### 📡 API
 | 
			
		||||
 | 
			
		||||
- [API]Add more enpoints to API [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2390](https://github.com/community-scripts/ProxmoxVE/pull/2390))
 | 
			
		||||
- [API] Update api.func: Remove unwanted file creation [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2378](https://github.com/community-scripts/ProxmoxVE/pull/2378))
 | 
			
		||||
 | 
			
		||||
## 2025-02-13
 | 
			
		||||
 | 
			
		||||
### Changes
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										283
									
								
								api/main.go
									
									
									
									
									
								
							
							
						
						
									
										283
									
								
								api/main.go
									
									
									
									
									
								
							@@ -11,6 +11,7 @@ import (
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/mux"
 | 
			
		||||
@@ -31,6 +32,7 @@ func loadEnv() {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DataModel represents a single document in MongoDB
 | 
			
		||||
type DataModel struct {
 | 
			
		||||
	ID         primitive.ObjectID `json:"id" bson:"_id,omitempty"`
 | 
			
		||||
	CT_TYPE    uint               `json:"ct_type" bson:"ct_type"`
 | 
			
		||||
@@ -56,6 +58,13 @@ type StatusModel struct {
 | 
			
		||||
	STATUS    string `json:"status" bson:"status"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type CountResponse struct {
 | 
			
		||||
	TotalEntries int64            `json:"total_entries"`
 | 
			
		||||
	StatusCount  map[string]int64 `json:"status_count"`
 | 
			
		||||
	NSAPPCount   map[string]int64 `json:"nsapp_count"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConnectDatabase initializes the MongoDB connection
 | 
			
		||||
func ConnectDatabase() {
 | 
			
		||||
	loadEnv()
 | 
			
		||||
 | 
			
		||||
@@ -78,6 +87,7 @@ func ConnectDatabase() {
 | 
			
		||||
	fmt.Println("Connected to MongoDB on 10.10.10.18")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UploadJSON handles API requests and stores data as a document in MongoDB
 | 
			
		||||
func UploadJSON(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	var input DataModel
 | 
			
		||||
 | 
			
		||||
@@ -98,6 +108,7 @@ func UploadJSON(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	json.NewEncoder(w).Encode(map[string]string{"message": "Data saved successfully"})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateStatus updates the status of a record based on RANDOM_ID
 | 
			
		||||
func UpdateStatus(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	var input StatusModel
 | 
			
		||||
 | 
			
		||||
@@ -120,6 +131,7 @@ func UpdateStatus(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	json.NewEncoder(w).Encode(map[string]string{"message": "Record updated successfully"})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetDataJSON fetches all data from MongoDB
 | 
			
		||||
func GetDataJSON(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	var records []DataModel
 | 
			
		||||
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 | 
			
		||||
@@ -144,6 +156,270 @@ func GetDataJSON(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	w.Header().Set("Content-Type", "application/json")
 | 
			
		||||
	json.NewEncoder(w).Encode(records)
 | 
			
		||||
}
 | 
			
		||||
func GetPaginatedData(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	page, _ := strconv.Atoi(r.URL.Query().Get("page"))
 | 
			
		||||
	limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
 | 
			
		||||
	if page < 1 {
 | 
			
		||||
		page = 1
 | 
			
		||||
	}
 | 
			
		||||
	if limit < 1 {
 | 
			
		||||
		limit = 10
 | 
			
		||||
	}
 | 
			
		||||
	skip := (page - 1) * limit
 | 
			
		||||
	var records []DataModel
 | 
			
		||||
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 | 
			
		||||
	defer cancel()
 | 
			
		||||
 | 
			
		||||
	options := options.Find().SetSkip(int64(skip)).SetLimit(int64(limit))
 | 
			
		||||
	cursor, err := collection.Find(ctx, bson.M{}, options)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer cursor.Close(ctx)
 | 
			
		||||
 | 
			
		||||
	for cursor.Next(ctx) {
 | 
			
		||||
		var record DataModel
 | 
			
		||||
		if err := cursor.Decode(&record); err != nil {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		records = append(records, record)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.Header().Set("Content-Type", "application/json")
 | 
			
		||||
	json.NewEncoder(w).Encode(records)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetSummary(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 | 
			
		||||
	defer cancel()
 | 
			
		||||
 | 
			
		||||
	totalCount, err := collection.CountDocuments(ctx, bson.M{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	statusCount := make(map[string]int64)
 | 
			
		||||
	nsappCount := make(map[string]int64)
 | 
			
		||||
 | 
			
		||||
	pipeline := []bson.M{
 | 
			
		||||
		{"$group": bson.M{"_id": "$status", "count": bson.M{"$sum": 1}}},
 | 
			
		||||
	}
 | 
			
		||||
	cursor, err := collection.Aggregate(ctx, pipeline)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		for cursor.Next(ctx) {
 | 
			
		||||
			var result struct {
 | 
			
		||||
				ID    string `bson:"_id"`
 | 
			
		||||
				Count int64  `bson:"count"`
 | 
			
		||||
			}
 | 
			
		||||
			if err := cursor.Decode(&result); err == nil {
 | 
			
		||||
				statusCount[result.ID] = result.Count
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pipeline = []bson.M{
 | 
			
		||||
		{"$group": bson.M{"_id": "$nsapp", "count": bson.M{"$sum": 1}}},
 | 
			
		||||
	}
 | 
			
		||||
	cursor, err = collection.Aggregate(ctx, pipeline)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		for cursor.Next(ctx) {
 | 
			
		||||
			var result struct {
 | 
			
		||||
				ID    string `bson:"_id"`
 | 
			
		||||
				Count int64  `bson:"count"`
 | 
			
		||||
			}
 | 
			
		||||
			if err := cursor.Decode(&result); err == nil {
 | 
			
		||||
				nsappCount[result.ID] = result.Count
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	response := CountResponse{
 | 
			
		||||
		TotalEntries: totalCount,
 | 
			
		||||
		StatusCount:  statusCount,
 | 
			
		||||
		NSAPPCount:   nsappCount,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.Header().Set("Content-Type", "application/json")
 | 
			
		||||
	json.NewEncoder(w).Encode(response)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetByNsapp(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	nsapp := r.URL.Query().Get("nsapp")
 | 
			
		||||
	var records []DataModel
 | 
			
		||||
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 | 
			
		||||
	defer cancel()
 | 
			
		||||
 | 
			
		||||
	cursor, err := collection.Find(ctx, bson.M{"nsapp": nsapp})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer cursor.Close(ctx)
 | 
			
		||||
 | 
			
		||||
	for cursor.Next(ctx) {
 | 
			
		||||
		var record DataModel
 | 
			
		||||
		if err := cursor.Decode(&record); err != nil {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		records = append(records, record)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.Header().Set("Content-Type", "application/json")
 | 
			
		||||
	json.NewEncoder(w).Encode(records)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetByDateRange(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
 | 
			
		||||
	startDate := r.URL.Query().Get("start_date")
 | 
			
		||||
	endDate := r.URL.Query().Get("end_date")
 | 
			
		||||
 | 
			
		||||
	if startDate == "" || endDate == "" {
 | 
			
		||||
		http.Error(w, "Both start_date and end_date are required", http.StatusBadRequest)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	start, err := time.Parse("2006-01-02T15:04:05.999999+00:00", startDate+"T00:00:00+00:00")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		http.Error(w, "Invalid start_date format", http.StatusBadRequest)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	end, err := time.Parse("2006-01-02T15:04:05.999999+00:00", endDate+"T23:59:59+00:00")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		http.Error(w, "Invalid end_date format", http.StatusBadRequest)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var records []DataModel
 | 
			
		||||
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 | 
			
		||||
	defer cancel()
 | 
			
		||||
 | 
			
		||||
	cursor, err := collection.Find(ctx, bson.M{
 | 
			
		||||
		"created_at": bson.M{
 | 
			
		||||
			"$gte": start,
 | 
			
		||||
			"$lte": end,
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer cursor.Close(ctx)
 | 
			
		||||
 | 
			
		||||
	for cursor.Next(ctx) {
 | 
			
		||||
		var record DataModel
 | 
			
		||||
		if err := cursor.Decode(&record); err != nil {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		records = append(records, record)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.Header().Set("Content-Type", "application/json")
 | 
			
		||||
	json.NewEncoder(w).Encode(records)
 | 
			
		||||
}
 | 
			
		||||
func GetByStatus(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	status := r.URL.Query().Get("status")
 | 
			
		||||
	var records []DataModel
 | 
			
		||||
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 | 
			
		||||
	defer cancel()
 | 
			
		||||
 | 
			
		||||
	cursor, err := collection.Find(ctx, bson.M{"status": status})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer cursor.Close(ctx)
 | 
			
		||||
 | 
			
		||||
	for cursor.Next(ctx) {
 | 
			
		||||
		var record DataModel
 | 
			
		||||
		if err := cursor.Decode(&record); err != nil {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		records = append(records, record)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.Header().Set("Content-Type", "application/json")
 | 
			
		||||
	json.NewEncoder(w).Encode(records)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetByOS(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	osType := r.URL.Query().Get("os_type")
 | 
			
		||||
	osVersion := r.URL.Query().Get("os_version")
 | 
			
		||||
	var records []DataModel
 | 
			
		||||
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 | 
			
		||||
	defer cancel()
 | 
			
		||||
 | 
			
		||||
	cursor, err := collection.Find(ctx, bson.M{"os_type": osType, "os_version": osVersion})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer cursor.Close(ctx)
 | 
			
		||||
 | 
			
		||||
	for cursor.Next(ctx) {
 | 
			
		||||
		var record DataModel
 | 
			
		||||
		if err := cursor.Decode(&record); err != nil {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		records = append(records, record)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.Header().Set("Content-Type", "application/json")
 | 
			
		||||
	json.NewEncoder(w).Encode(records)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetErrors(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	errorCount := make(map[string]int)
 | 
			
		||||
 | 
			
		||||
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 | 
			
		||||
	defer cancel()
 | 
			
		||||
 | 
			
		||||
	cursor, err := collection.Find(ctx, bson.M{"error": bson.M{"$ne": ""}})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer cursor.Close(ctx)
 | 
			
		||||
 | 
			
		||||
	for cursor.Next(ctx) {
 | 
			
		||||
		var record DataModel
 | 
			
		||||
		if err := cursor.Decode(&record); err != nil {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if record.ERROR != "" {
 | 
			
		||||
			errorCount[record.ERROR]++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type ErrorCountResponse struct {
 | 
			
		||||
		Error string `json:"error"`
 | 
			
		||||
		Count int    `json:"count"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var errorCounts []ErrorCountResponse
 | 
			
		||||
	for err, count := range errorCount {
 | 
			
		||||
		errorCounts = append(errorCounts, ErrorCountResponse{
 | 
			
		||||
			Error: err,
 | 
			
		||||
			Count: count,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.Header().Set("Content-Type", "application/json")
 | 
			
		||||
	json.NewEncoder(w).Encode(struct {
 | 
			
		||||
		ErrorCounts []ErrorCountResponse `json:"error_counts"`
 | 
			
		||||
	}{
 | 
			
		||||
		ErrorCounts: errorCounts,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	ConnectDatabase()
 | 
			
		||||
@@ -152,6 +428,13 @@ func main() {
 | 
			
		||||
	router.HandleFunc("/upload", UploadJSON).Methods("POST")
 | 
			
		||||
	router.HandleFunc("/upload/updatestatus", UpdateStatus).Methods("POST")
 | 
			
		||||
	router.HandleFunc("/data/json", GetDataJSON).Methods("GET")
 | 
			
		||||
	router.HandleFunc("/data/paginated", GetPaginatedData).Methods("GET")
 | 
			
		||||
	router.HandleFunc("/data/summary", GetSummary).Methods("GET")
 | 
			
		||||
	router.HandleFunc("/data/nsapp", GetByNsapp).Methods("GET")
 | 
			
		||||
	router.HandleFunc("/data/date", GetByDateRange).Methods("GET")
 | 
			
		||||
	router.HandleFunc("/data/status", GetByStatus).Methods("GET")
 | 
			
		||||
	router.HandleFunc("/data/os", GetByOS).Methods("GET")
 | 
			
		||||
	router.HandleFunc("/data/errors", GetErrors).Methods("GET")
 | 
			
		||||
 | 
			
		||||
	c := cors.New(cors.Options{
 | 
			
		||||
		AllowedOrigins:   []string{"*"},
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								ct/homarr.sh
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								ct/homarr.sh
									
									
									
									
									
								
							@@ -1,7 +1,7 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
 | 
			
		||||
# Copyright (c) 2021-2025 tteck
 | 
			
		||||
# Author: tteck (tteckster) | Co-Author: MickLesk (Canbiz)
 | 
			
		||||
# Author: tteck (tteckster) | Co-Author: MickLesk (Canbiz) | Co-Author: CrazyWolf13
 | 
			
		||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
 | 
			
		||||
# Source: https://homarr.dev/
 | 
			
		||||
 | 
			
		||||
@@ -15,6 +15,7 @@ var_version="12"
 | 
			
		||||
var_unprivileged="1"
 | 
			
		||||
 | 
			
		||||
header_info "$APP"
 | 
			
		||||
 | 
			
		||||
variables
 | 
			
		||||
color
 | 
			
		||||
catch_errors
 | 
			
		||||
@@ -37,7 +38,7 @@ fi
 | 
			
		||||
  RELEASE=$(curl -s https://api.github.com/repos/homarr-labs/homarr/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }')
 | 
			
		||||
  if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then
 | 
			
		||||
 | 
			
		||||
    msg_info "Stopping Services"
 | 
			
		||||
    msg_info "Stopping Services (Patience)"
 | 
			
		||||
    systemctl stop homarr
 | 
			
		||||
    msg_ok "Services Stopped"
 | 
			
		||||
 | 
			
		||||
@@ -55,9 +56,23 @@ fi
 | 
			
		||||
    mv /opt/homarr-data-backup/.env /opt/homarr/.env
 | 
			
		||||
    cd /opt/homarr
 | 
			
		||||
    pnpm install &>/dev/null
 | 
			
		||||
    pnpm run db:migration:sqlite:run &>/dev/null
 | 
			
		||||
    pnpm build &>/dev/null
 | 
			
		||||
    mkdir build
 | 
			
		||||
    cp /opt/homarr/apps/nextjs/next.config.ts .
 | 
			
		||||
    cp /opt/homarr/apps/nextjs/package.json .
 | 
			
		||||
    cp -r /opt/homarr/packages/db/migrations /opt/homarr_db/migrations
 | 
			
		||||
    cp -r /opt/homarr/apps/nextjs/.next/standalone/* /opt/homarr
 | 
			
		||||
    mkdir -p /appdata/redis
 | 
			
		||||
    cp /opt/homarr/packages/redis/redis.conf /opt/homarr/redis.conf
 | 
			
		||||
    rm /etc/nginx/nginx.conf
 | 
			
		||||
    mkdir -p /etc/nginx/templates
 | 
			
		||||
    cp /opt/homarr/nginx.conf /etc/nginx/templates/nginx.conf
 | 
			
		||||
 | 
			
		||||
    mkdir -p /opt/homarr/apps/cli
 | 
			
		||||
    cp /opt/homarr/packages/cli/cli.cjs /opt/homarr/apps/cli/cli.cjs
 | 
			
		||||
    echo $'#!/bin/bash\ncd /opt/homarr/apps/cli && node ./cli.cjs "$@"' > /usr/bin/homarr
 | 
			
		||||
    chmod +x /usr/bin/homarr
 | 
			
		||||
    
 | 
			
		||||
    mkdir /opt/homarr/build
 | 
			
		||||
    cp ./node_modules/better-sqlite3/build/Release/better_sqlite3.node ./build/better_sqlite3.node
 | 
			
		||||
    echo "${RELEASE}" >/opt/${APP}_version.txt
 | 
			
		||||
    msg_ok "Updated ${APP}"
 | 
			
		||||
@@ -79,4 +94,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}:7575${CL}"
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
# Copyright (c) 2021-2025 community-scripts ORG
 | 
			
		||||
# Author: MickLesk (Canbiz)
 | 
			
		||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
 | 
			
		||||
# Source: https://github.com/ajnart/homarr
 | 
			
		||||
# Source: https://github.com/homarr-labs/homarr
 | 
			
		||||
 | 
			
		||||
source /dev/stdin <<< "$FUNCTIONS_FILE_PATH"
 | 
			
		||||
color
 | 
			
		||||
@@ -20,10 +20,13 @@ $STD apt-get install -y \
 | 
			
		||||
  curl \
 | 
			
		||||
  redis-server \
 | 
			
		||||
  ca-certificates \
 | 
			
		||||
  gnupg \
 | 
			
		||||
  gpg \
 | 
			
		||||
  make \
 | 
			
		||||
  g++ \
 | 
			
		||||
  build-essential
 | 
			
		||||
  build-essential \
 | 
			
		||||
  nginx \
 | 
			
		||||
  gettext \
 | 
			
		||||
  openssl
 | 
			
		||||
msg_ok "Installed Dependencies"
 | 
			
		||||
 | 
			
		||||
msg_info "Setting up Node.js Repository"
 | 
			
		||||
@@ -46,27 +49,56 @@ unzip -q v${RELEASE}.zip
 | 
			
		||||
mv homarr-${RELEASE} /opt/homarr
 | 
			
		||||
mkdir -p /opt/homarr_db
 | 
			
		||||
touch /opt/homarr_db/db.sqlite
 | 
			
		||||
AUTH_SECRET="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)"
 | 
			
		||||
SECRET_ENCRYPTION_KEY="$(openssl rand -hex 32)"
 | 
			
		||||
 | 
			
		||||
cd /opt/homarr
 | 
			
		||||
cat <<EOF >/opt/homarr/.env
 | 
			
		||||
AUTH_SECRET='${AUTH_SECRET}'
 | 
			
		||||
DB_DRIVER='better-sqlite3'
 | 
			
		||||
DB_DIALECT='sqlite'
 | 
			
		||||
SECRET_ENCRYPTION_KEY='${SECRET_ENCRYPTION_KEY}'
 | 
			
		||||
DB_URL='/opt/homarr_db/db.sqlite'
 | 
			
		||||
TURBO_TELEMETRY_DISABLED=1
 | 
			
		||||
AUTH_PROVIDERS='credentials'
 | 
			
		||||
NODE_ENV='production'
 | 
			
		||||
EOF
 | 
			
		||||
 | 
			
		||||
cd /opt/homarr
 | 
			
		||||
$STD pnpm install
 | 
			
		||||
$STD pnpm run db:migration:sqlite:run
 | 
			
		||||
$STD pnpm build
 | 
			
		||||
mkdir build
 | 
			
		||||
cp ./node_modules/better-sqlite3/build/Release/better_sqlite3.node ./build/better_sqlite3.node
 | 
			
		||||
echo "${RELEASE}" >"/opt/${APPLICATION}_version.txt"
 | 
			
		||||
msg_ok "Installed Homarr"
 | 
			
		||||
 | 
			
		||||
msg_info "Creating Service"
 | 
			
		||||
msg_info "Copying build and config files"
 | 
			
		||||
cp /opt/homarr/apps/nextjs/next.config.ts .
 | 
			
		||||
cp /opt/homarr/apps/nextjs/package.json .
 | 
			
		||||
cp -r /opt/homarr/packages/db/migrations /opt/homarr_db/migrations
 | 
			
		||||
cp -r /opt/homarr/apps/nextjs/.next/standalone/* /opt/homarr
 | 
			
		||||
mkdir -p /appdata/redis
 | 
			
		||||
cp /opt/homarr/packages/redis/redis.conf /opt/homarr/redis.conf
 | 
			
		||||
mkdir -p /etc/nginx/templates
 | 
			
		||||
rm /etc/nginx/nginx.conf
 | 
			
		||||
cp /opt/homarr/nginx.conf /etc/nginx/templates/nginx.conf
 | 
			
		||||
mkdir -p /opt/homarr/apps/cli
 | 
			
		||||
cp /opt/homarr/packages/cli/cli.cjs /opt/homarr/apps/cli/cli.cjs
 | 
			
		||||
echo $'#!/bin/bash\ncd /opt/homarr/apps/cli && node ./cli.cjs "$@"' > /usr/bin/homarr
 | 
			
		||||
chmod +x /usr/bin/homarr
 | 
			
		||||
mkdir /opt/homarr/build
 | 
			
		||||
cp ./node_modules/better-sqlite3/build/Release/better_sqlite3.node ./build/better_sqlite3.node
 | 
			
		||||
echo "${RELEASE}" >"/opt/${APPLICATION}_version.txt"
 | 
			
		||||
msg_ok "Finished copying"
 | 
			
		||||
 | 
			
		||||
msg_info "Creating Services"
 | 
			
		||||
cat <<'EOF' >/opt/run_homarr.sh
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
export DB_DIALECT='sqlite'
 | 
			
		||||
export AUTH_SECRET=$(openssl rand -base64 32)
 | 
			
		||||
node /opt/homarr_db/migrations/$DB_DIALECT/migrate.cjs /opt/homarr_db/migrations/$DB_DIALECT
 | 
			
		||||
export HOSTNAME=$(ip route get 1.1.1.1 | grep -oP 'src \K[^ ]+')
 | 
			
		||||
envsubst '${HOSTNAME}' < /etc/nginx/templates/nginx.conf > /etc/nginx/nginx.conf
 | 
			
		||||
nginx -g 'daemon off;' &
 | 
			
		||||
redis-server /opt/homarr/packages/redis/redis.conf &
 | 
			
		||||
node apps/tasks/tasks.cjs &
 | 
			
		||||
node apps/websocket/wssServer.cjs &
 | 
			
		||||
node apps/nextjs/server.js & PID=$!
 | 
			
		||||
wait $PID
 | 
			
		||||
EOF
 | 
			
		||||
chmod +x /opt/run_homarr.sh
 | 
			
		||||
cat <<EOF >/etc/systemd/system/homarr.service
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=Homarr Service
 | 
			
		||||
@@ -76,7 +108,7 @@ After=network.target
 | 
			
		||||
Type=exec
 | 
			
		||||
WorkingDirectory=/opt/homarr
 | 
			
		||||
EnvironmentFile=-/opt/homarr/.env
 | 
			
		||||
ExecStart=/usr/bin/pnpm start
 | 
			
		||||
ExecStart=/opt/run_homarr.sh
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
WantedBy=multi-user.target
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
    "type": "ct",
 | 
			
		||||
    "updateable": true,
 | 
			
		||||
    "privileged": false,
 | 
			
		||||
    "interface_port": 3000,
 | 
			
		||||
    "interface_port": 7575,
 | 
			
		||||
    "documentation": null,
 | 
			
		||||
    "website": "https://homarr.dev/",
 | 
			
		||||
    "logo": "https://raw.githubusercontent.com/loganmarchione/homelab-svg-assets/main/assets/homarr.svg",
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@
 | 
			
		||||
    },
 | 
			
		||||
    "notes": [
 | 
			
		||||
        {
 | 
			
		||||
            "text": "Check documentation on how to configure RustDesk Server. `https://rustdesk.com/docs/en/`",
 | 
			
		||||
            "text": "Check our configuration guide for help: `https://github.com/community-scripts/ProxmoxVE/discussions/2388`",
 | 
			
		||||
            "type": "info"
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@ post_to_api() {
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
  RESPONSE=$(curl -s -o response.txt -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \
 | 
			
		||||
  RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \
 | 
			
		||||
    -H "Content-Type: application/json" \
 | 
			
		||||
    -d "$JSON_PAYLOAD") || true
 | 
			
		||||
}
 | 
			
		||||
@@ -87,7 +87,7 @@ post_to_api_vm() {
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
  RESPONSE=$(curl -s -o response.txt -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \
 | 
			
		||||
  RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \
 | 
			
		||||
    -H "Content-Type: application/json" \
 | 
			
		||||
    -d "$JSON_PAYLOAD") || true
 | 
			
		||||
}
 | 
			
		||||
@@ -115,7 +115,7 @@ post_update_to_api() {
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
   
 | 
			
		||||
  RESPONSE=$(curl -s -o response.txt -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \
 | 
			
		||||
  RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \
 | 
			
		||||
    -H "Content-Type: application/json" \
 | 
			
		||||
    -d "$JSON_PAYLOAD") || true
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user