Replace Go API with PocketBase; update docs

Remove the old Go/Mongo API (api/main.go, go.mod, go.sum, .env.example) and switch telemetry backend to PocketBase (http://db.community-scripts.org). Update documentation and flowcharts to reflect the PocketBase collection (_dev_telemetry_data), new REST endpoints (POST/PATCH/GET), field schema, and revised api.func integration (LXC/VM reporting and status updates). Misc scripts and helpers were adjusted (misc/api.func, misc/build.func, misc/error_handler.func) and a new misc/ingest.go was added. This consolidates telemetry to a hosted PocketBase instance and updates docs and integration points accordingly.
This commit is contained in:
CanbiZ (MickLesk) 2026-02-09 15:34:17 +01:00
parent 1eb0cc55ff
commit 820d4551a1
14 changed files with 1137 additions and 2557 deletions

View File

@ -1,5 +0,0 @@
MONGO_USER=
MONGO_PASSWORD=
MONGO_IP=
MONGO_PORT=
MONGO_DATABASE=

View File

@ -1,23 +0,0 @@
module proxmox-api
go 1.24.0
require (
github.com/gorilla/mux v1.8.1
github.com/joho/godotenv v1.5.1
github.com/rs/cors v1.11.1
go.mongodb.org/mongo-driver v1.17.2
)
require (
github.com/golang/snappy v0.0.4 // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
golang.org/x/crypto v0.45.0 // indirect
golang.org/x/sync v0.18.0 // indirect
golang.org/x/text v0.31.0 // indirect
)

View File

@ -1,56 +0,0 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver v1.17.2 h1:gvZyk8352qSfzyZ2UMWcpDpMSGEr1eqE4T793SqyhzM=
go.mongodb.org/mongo-driver v1.17.2/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -1,450 +0,0 @@
// Copyright (c) 2021-2025 community-scripts ORG
// Author: Michel Roegl-Brunner (michelroegl-brunner)
// License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"strconv"
"time"
"github.com/gorilla/mux"
"github.com/joho/godotenv"
"github.com/rs/cors"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var client *mongo.Client
var collection *mongo.Collection
func loadEnv() {
if err := godotenv.Load(); err != nil {
log.Fatal("Error loading .env file")
}
}
// 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"`
DISK_SIZE float32 `json:"disk_size" bson:"disk_size"`
CORE_COUNT uint `json:"core_count" bson:"core_count"`
RAM_SIZE uint `json:"ram_size" bson:"ram_size"`
OS_TYPE string `json:"os_type" bson:"os_type"`
OS_VERSION string `json:"os_version" bson:"os_version"`
DISABLEIP6 string `json:"disableip6" bson:"disableip6"`
NSAPP string `json:"nsapp" bson:"nsapp"`
METHOD string `json:"method" bson:"method"`
CreatedAt time.Time `json:"created_at" bson:"created_at"`
PVEVERSION string `json:"pve_version" bson:"pve_version"`
STATUS string `json:"status" bson:"status"`
RANDOM_ID string `json:"random_id" bson:"random_id"`
TYPE string `json:"type" bson:"type"`
ERROR string `json:"error" bson:"error"`
}
type StatusModel struct {
RANDOM_ID string `json:"random_id" bson:"random_id"`
ERROR string `json:"error" bson:"error"`
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()
mongoURI := fmt.Sprintf("mongodb://%s:%s@%s:%s",
os.Getenv("MONGO_USER"),
os.Getenv("MONGO_PASSWORD"),
os.Getenv("MONGO_IP"),
os.Getenv("MONGO_PORT"))
database := os.Getenv("MONGO_DATABASE")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
var err error
client, err = mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
if err != nil {
log.Fatal("Failed to connect to MongoDB!", err)
}
collection = client.Database(database).Collection("data_models")
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
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
input.CreatedAt = time.Now()
_, err := collection.InsertOne(context.Background(), input)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
log.Println("Received data:", input)
w.WriteHeader(http.StatusCreated)
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
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
filter := bson.M{"random_id": input.RANDOM_ID}
update := bson.M{"$set": bson.M{"status": input.STATUS, "error": input.ERROR}}
_, err := collection.UpdateOne(context.Background(), filter, update)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
log.Println("Updated data:", input)
w.WriteHeader(http.StatusOK)
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)
defer cancel()
cursor, err := collection.Find(ctx, bson.M{})
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 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()
router := mux.NewRouter()
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{"*"},
AllowedMethods: []string{"GET", "POST"},
AllowedHeaders: []string{"Content-Type", "Authorization"},
AllowCredentials: true,
})
handler := c.Handler(router)
fmt.Println("Server running on port 8080")
log.Fatal(http.ListenAndServe(":8080", handler))
}

View File

@ -1,38 +1,30 @@
# API Integration Documentation (/api) # API Integration Documentation
This directory contains comprehensive documentation for API integration and the `/api` directory.
## Overview ## Overview
The `/api` directory contains the Proxmox Community Scripts API backend for diagnostic reporting, telemetry, and analytics integration. The telemetry and diagnostics API uses **PocketBase** as backend, hosted at `http://db.community-scripts.org`. All telemetry data is stored in the `_dev_telemetry_data` collection.
The Go/MongoDB API server (`/api` directory) has been replaced entirely by PocketBase.
## Key Components ## Key Components
### Main API Service ### PocketBase Backend
Located in `/api/main.go`: - **URL**: `http://db.community-scripts.org`
- **Collection**: `_dev_telemetry_data`
- **Admin UI**: `http://db.community-scripts.org/_/#/collections`
- RESTful API for receiving telemetry data - RESTful API for receiving telemetry data
- Installation statistics tracking - Installation statistics tracking
- Error reporting and analytics - Error reporting and analytics
- Performance monitoring
### Integration with Scripts ### Integration with Scripts
The API is integrated into all installation scripts via `api.func`: The API is integrated into all installation scripts via `api.func`:
- Sends installation start/completion events - Sends installation start/completion events
- Reports errors and exit codes - Reports errors and exit codes with numeric values
- Collects anonymous usage statistics - Collects anonymous usage statistics
- Enables project analytics - Enables project analytics
## Documentation Structure ## Documentation Structure
API documentation covers:
- API endpoint specifications
- Integration methods
- Data formats and schemas
- Error handling
- Privacy and data handling
## Key Resources
- **[misc/api.func/](../misc/api.func/)** - API function library documentation - **[misc/api.func/](../misc/api.func/)** - API function library documentation
- **[misc/api.func/README.md](../misc/api.func/README.md)** - Quick reference - **[misc/api.func/README.md](../misc/api.func/README.md)** - Quick reference
- **[misc/api.func/API_FUNCTIONS_REFERENCE.md](../misc/api.func/API_FUNCTIONS_REFERENCE.md)** - Complete function reference - **[misc/api.func/API_FUNCTIONS_REFERENCE.md](../misc/api.func/API_FUNCTIONS_REFERENCE.md)** - Complete function reference
@ -42,48 +34,92 @@ API documentation covers:
The `api.func` library provides: The `api.func` library provides:
### `post_to_api()` ### `post_to_api()`
Send container installation data to API. Send LXC container installation data to PocketBase.
**Usage**: Creates a new record in `_dev_telemetry_data` with status `installing`.
```bash
post_to_api CTID STATUS APP_NAME
```
### `post_update_to_api()` ### `post_to_api_vm()`
Report application update status. Send VM installation data to PocketBase.
**Usage**: Creates a new record with `type=vm` and `ct_type=2`.
```bash
post_update_to_api CTID APP_NAME VERSION
```
### `get_error_description()` ### `post_update_to_api(status, exit_code)`
Update installation status via PocketBase PATCH.
Maps status values:
- `"done"` → PocketBase status `"sucess"`
- `"failed"` → PocketBase status `"failed"`
### `explain_exit_code(code)`
Get human-readable error description from exit code. Get human-readable error description from exit code.
**Usage**: **Usage**:
```bash ```bash
ERROR_DESC=$(get_error_description EXIT_CODE) ERROR_DESC=$(explain_exit_code 137)
# → "Killed (SIGKILL / Out of memory?)"
``` ```
## PocketBase Collection Schema
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | text (auto) | yes | PocketBase record ID |
| `random_id` | text | yes | Session UUID (unique) |
| `type` | select | yes | `lxc`, `vm`, `addon`, `pve` |
| `ct_type` | number | yes | 1=LXC, 2=VM |
| `nsapp` | text | yes | Application name |
| `status` | select | yes | `installing`, `sucess`, `failed`, `unknown` |
| `disk_size` | number | no | Disk size in GB |
| `core_count` | number | no | CPU cores |
| `ram_size` | number | no | RAM in MB |
| `os_type` | text | no | OS type (debian, ubuntu, etc.) |
| `os_version` | text | no | OS version |
| `pve_version` | text | no | Proxmox VE version |
| `method` | text | no | Installation method |
| `error` | text | no | Error description |
| `exit_code` | number | no | Numeric exit code |
| `created` | autodate | auto | Record creation timestamp |
| `updated` | autodate | auto | Last update timestamp |
## API Endpoints (PocketBase REST)
**Base URL**: `http://db.community-scripts.org`
| Method | Endpoint | Description |
|--------|----------|-------------|
| `POST` | `/api/collections/_dev_telemetry_data/records` | Create telemetry record |
| `PATCH` | `/api/collections/_dev_telemetry_data/records/{id}` | Update record status |
| `GET` | `/api/collections/_dev_telemetry_data/records` | List/search records |
### Query Parameters (GET)
- `filter` PocketBase filter syntax, e.g. `(nsapp='debian' && status='failed')`
- `sort` Sort fields, e.g. `-created,nsapp`
- `page` / `perPage` Pagination
- `fields` Limit returned fields
## API Integration Points ## API Integration Points
### In Container Creation (`ct/AppName.sh`) ### In Container Creation (`ct/AppName.sh`)
- Called by build.func to report container creation - Called by `build.func` to report container creation via `post_to_api`
- Sends initial container setup data - Sends initial container setup data with status `installing`
- Reports success or failure - Reports success or failure via `post_update_to_api`
### In Installation Scripts (`install/appname-install.sh`) ### In VM Creation (`vm/AppName.sh`)
- Called at start of installation - Calls `post_to_api_vm` after VM creation
- Called on installation completion - Status updates via `post_update_to_api`
- Called on error conditions
### Data Collected ### Data Flow
- Container/VM ID ```
- Application name and version Installation Scripts
- Installation duration
- Success/failure status ├─ Call: api.func functions
- Error codes (if failure)
- Anonymous usage metrics ├─ POST → PocketBase (create record, status=installing)
│ └─ Returns record ID (stored in PB_RECORD_ID)
└─ PATCH → PocketBase (update record with final status)
└─ status=sucess/failed + exit_code + error
```
## Privacy ## Privacy
@ -92,55 +128,18 @@ All API data:
- ✅ Aggregated for statistics - ✅ Aggregated for statistics
- ✅ Used only for project improvement - ✅ Used only for project improvement
- ✅ No tracking of user identities - ✅ No tracking of user identities
- ✅ Can be disabled if desired - ✅ Can be disabled via diagnostics settings
## API Architecture
```
Installation Scripts
├─ Call: api.func functions
└─ POST to: https://api.community-scripts.org
├─ Receives data
├─ Validates format
├─ Stores metrics
└─ Aggregates statistics
└─ Used for:
├─ Download tracking
├─ Error trending
├─ Feature usage stats
└─ Project health monitoring
```
## Common API Tasks
- **Enable API reporting** → Built-in by default, no configuration needed
- **Disable API** → Set `api_disable="yes"` before running
- **View API data** → Visit https://community-scripts.org/stats
- **Report API errors** → [GitHub Issues](https://github.com/community-scripts/ProxmoxVED/issues)
## Debugging API Issues ## Debugging API Issues
If API calls fail: If API calls fail:
1. Check internet connectivity 1. Check internet connectivity
2. Verify API endpoint availability 2. Verify PocketBase endpoint: `curl -s http://db.community-scripts.org/api/health`
3. Review error codes in [EXIT_CODES.md](../EXIT_CODES.md) 3. Review error codes in [EXIT_CODES.md](../EXIT_CODES.md)
4. Check API function logs 4. Check that `DIAGNOSTICS=yes` in `/usr/local/community-scripts/diagnostics`
5. Report issues on GitHub 5. Report issues on [GitHub](https://git.community-scripts.org/community-scripts/ProxmoxVED/issues)
## API Endpoint
**Base URL**: `https://api.community-scripts.org`
**Endpoints**:
- `POST /install` - Report container installation
- `POST /update` - Report application update
- `GET /stats` - Public statistics
--- ---
**Last Updated**: December 2025 **Last Updated**: February 2026
**Maintainers**: community-scripts team **Maintainers**: community-scripts team

View File

@ -1,5 +1,9 @@
# api.func Execution Flowchart # api.func Execution Flowchart
## Overview
This document illustrates the execution flow of `api.func` functions. The backend is **PocketBase** at `http://db.community-scripts.org`, collection `_dev_telemetry_data`.
## Main API Communication Flow ## Main API Communication Flow
``` ```
@ -10,333 +14,321 @@
┌─────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────┐
│ Prerequisites Check │ │ Prerequisites Check │
│ │ │ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ Prerequisites Validation │ │ │ │ Prerequisites Validation │ │
│ │ │ │ │ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────────┐ │ │
│ │ │ Check curl │ │ Check │ │ Check │ │ │ │ │ │ Check curl │ │ Check │ │ Check │ │ │
│ │ │ Availability │ │ Diagnostics │ │ Random UUID │ │ │ │ │ │ Availability │ │ DIAGNOSTICS │ │ RANDOM_UUID │ │ │
│ │ │ │ │ Setting │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ │ • command -v │ │ • DIAGNOSTICS │ │ • RANDOM_UUID │ │ │ │ │ • command -v │ │ • Must be "yes" │ │ • Must not be │ │ │
│ │ │ curl │ │ = "yes" │ │ not empty │ │ │ │ │ curl │ │ • Return if │ │ empty │ │ │
│ │ │ • Return if │ │ • Return if │ │ • Return if │ │ │ │ │ • Return if │ │ "no" or unset │ │ • Return if │ │ │
│ │ │ not found │ │ disabled │ │ not set │ │ │ │ │ not found │ │ │ │ not set │ │ │
│ │ │ │ │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └──────────────────┘ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────┐
│ Data Collection │ │ Data Collection │
│ │ │ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ System Information Gathering │ │ │ │ System Information Gathering │ │
│ │ │ │ │ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────────┐ │ │
│ │ │ Get PVE │ │ Collect │ │ Prepare JSON │ │ │ │ │ │ Get PVE │ │ Collect Env │ │ Build JSON │ │ │
│ │ │ Version │ │ Environment │ │ Payload │ │ │ │ │ Version │ │ Variables │ │ Payload │ │ │
│ │ │ │ │ Variables │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ │ • pveversion │ │ • CT_TYPE │ │ • Create JSON │ │ │ │ │ • pveversion │ │ • CT_TYPE │ │ • Heredoc JSON │ │ │
│ │ │ command │ │ • DISK_SIZE │ │ structure │ │ │ │ │ command │ │ • DISK_SIZE │ │ • Include all │ │ │
│ │ │ • Parse version │ │ • CORE_COUNT │ │ • Include all │ │ │ │ │ • Parse version │ │ • CORE_COUNT │ │ fields │ │ │
│ │ │ • Extract │ │ • RAM_SIZE │ │ variables │ │ │ │ │ • Fallback: │ │ • RAM_SIZE │ │ • status = │ │ │
│ │ │ major.minor │ │ • var_os │ │ • Format for API │ │ │ │ │ "not found" │ │ • var_os │ │ "installing" │ │ │
│ │ │ │ │ • var_version │ │ │ │ │ │ │ │ │ • NSAPP, METHOD │ │ │ │ │
│ │ │ │ │ • NSAPP │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └──────────────────┘ │ │
│ │ │ │ │ • METHOD │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────┐
│ API Request Execution PocketBase API Request │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ ┌─────────────────────────────────────────────────────────────────────────┐
│ │ HTTP Request Processing │ │ HTTP Request Processing
│ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────────┐
│ │ │ Prepare │ │ Execute │ │ Handle │ │ │ │ │ Prepare │ │ Execute │ │ Handle │ │
│ │ │ Request │ │ HTTP Request │ │ Response │ │ │ │ Request │ │ HTTP POST │ │ Response │ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ │ • Set API URL │ │ • curl -s -w │ │ • Capture HTTP │ │ │ │ • URL: │ │ • curl -s -w │ │ • Check HTTP │ │
│ │ │ • Set headers │ │ "%{http_code}" │ │ status code │ │ │ │ PB_API_URL │ │ "%{http_code}"│ │ 200/201 │ │
│ │ │ • Set payload │ │ • POST request │ │ • Store response │ │ │ │ • Method: POST │ │ • -X POST │ │ • Extract "id" │ │
│ │ │ • Content-Type │ │ • JSON data │ │ • Handle errors │ │ │ │ • Content-Type: │ │ • -L (follow │ │ from response │ │
│ │ │ application/ │ │ • Follow │ │ gracefully │ │ │ │ application/ │ │ redirects) │ │ • Store in │ │
│ │ │ json │ │ redirects │ │ │ │ │ │ json │ │ • JSON body │ │ PB_RECORD_ID │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ │ └─────────────────┘ └─────────────────┘ └──────────────────┘
│ └─────────────────────────────────────────────────────────────────────────────┘ │ │ └─────────────────────────────────────────────────────────────────────────┘
└─────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────┘
``` ```
## LXC API Reporting Flow ## LXC API Reporting Flow`post_to_api()`
``` ```
┌─────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────┐
POST_TO_API() Flow post_to_api() Flow
Send LXC container installation data to API POST → Create LXC telemetry record in PocketBase
└─────────────────────┬───────────────────────────────────────────────────────────┘ └─────────────────────┬───────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────┐
│ LXC Data Preparation │ │ Prerequisites: curl? ──► DIAGNOSTICS="yes"? ──► RANDOM_UUID set? │
│ │ │ (return silently on any failure) │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ LXC-Specific Data Collection │ │
│ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │
│ │ │ Set LXC │ │ Include LXC │ │ Set Status │ │ │
│ │ │ Type │ │ Variables │ │ Information │ │
│ │ │ │ │ │ │ │ │
│ │ │ • ct_type: 1 │ │ • DISK_SIZE │ │ • status: │ │
│ │ │ • type: "lxc" │ │ • CORE_COUNT │ │ "installing" │ │
│ │ │ • Include all │ │ • RAM_SIZE │ │ • Include all │ │
│ │ │ LXC data │ │ • var_os │ │ tracking data │ │
│ │ │ │ │ • var_version │ │ │ │
│ │ │ │ │ • DISABLEIP6 │ │ │ │
│ │ │ │ │ • NSAPP │ │ │ │
│ │ │ │ │ • METHOD │ │ │ │
│ │ │ │ │ • pve_version │ │ │ │
│ │ │ │ │ • random_id │ │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐
│ JSON Payload Creation │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ JSON Structure Generation │ │
│ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │
│ │ │ Create JSON │ │ Validate │ │ Format for │ │ │
│ │ │ Structure │ │ Data │ │ API Request │ │
│ │ │ │ │ │ │ │ │
│ │ │ • Use heredoc │ │ • Check all │ │ • Ensure proper │ │
│ │ │ syntax │ │ variables │ │ JSON format │ │
│ │ │ • Include all │ │ are set │ │ • Escape special │ │
│ │ │ required │ │ • Validate │ │ characters │ │
│ │ │ fields │ │ data types │ │ • Set content │ │
│ │ │ • Format │ │ • Handle │ │ type │ │
│ │ │ properly │ │ missing │ │ │ │
│ │ │ │ │ values │ │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────┘
```
## VM API Reporting Flow
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ POST_TO_API_VM() Flow │
│ Send VM installation data to API │
└─────────────────────┬───────────────────────────────────────────────────────────┘ └─────────────────────┬───────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────┐
│ VM Data Preparation │ │ LXC Data Preparation │
│ │ │ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ ┌─────────────────┐ ┌─────────────────────┐ ┌───────────────────┐ │
│ │ VM-Specific Data Collection │ │ │ │ Set LXC type │ │ Collect variables │ │ Set initial │ │
│ │ │ │ │ │ │ │ │ │ status │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ • ct_type: 1 │ │ • DISK_SIZE │ │ │ │
│ │ │ Check │ │ Set VM │ │ Process Disk │ │ │ │ │ • type: "lxc" │ │ • CORE_COUNT │ │ • status: │ │
│ │ │ Diagnostics │ │ Type │ │ Size │ │ │ │ │ │ • RAM_SIZE │ │ "installing" │ │
│ │ │ File │ │ │ │ │ │ │ │ │ │ • var_os, var_version│ │ • random_id: │ │
│ │ │ │ │ • ct_type: 2 │ │ • Remove 'G' │ │ │ │ │ │ • NSAPP, METHOD │ │ RANDOM_UUID │ │
│ │ │ • Check file │ │ • type: "vm" │ │ suffix │ │ │ │ │ │ • pve_version │ │ │ │
│ │ │ existence │ │ • Include all │ │ • Convert to │ │ │ └─────────────────┘ └─────────────────────┘ └───────────────────┘ │
│ │ │ • Read │ │ VM data │ │ numeric value │ │
│ │ │ DIAGNOSTICS │ │ │ │ • Store in │ │
│ │ │ setting │ │ │ │ DISK_SIZE_API │ │
│ │ │ • Parse value │ │ │ │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐
│ VM JSON Payload Creation │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ VM-Specific JSON Structure │ │
│ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │
│ │ │ Include VM │ │ Set VM │ │ Format VM │ │ │
│ │ │ Variables │ │ Status │ │ Data for API │ │
│ │ │ │ │ │ │ │ │
│ │ │ • DISK_SIZE_API │ │ • status: │ │ • Ensure proper │ │
│ │ │ • CORE_COUNT │ │ "installing" │ │ JSON format │ │
│ │ │ • RAM_SIZE │ │ • Include all │ │ • Handle VM- │ │
│ │ │ • var_os │ │ tracking │ │ specific data │ │
│ │ │ • var_version │ │ information │ │ • Set appropriate │ │
│ │ │ • NSAPP │ │ │ │ content type │ │
│ │ │ • METHOD │ │ │ │ │ │
│ │ │ • pve_version │ │ │ │ │ │
│ │ │ • random_id │ │ │ │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────┘
```
## Status Update Flow
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ POST_UPDATE_TO_API() Flow │
│ Send installation completion status to API │
└─────────────────────┬───────────────────────────────────────────────────────────┘ └─────────────────────┬───────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────┐
│ Update Prevention Check │ │ POST → PB_API_URL │
│ │ │ http://db.community-scripts.org/api/collections/_dev_telemetry_data/records │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │
│ │ Duplicate Update Prevention │ │ │ Response (HTTP 200/201): │
│ │ │ │ │ { "id": "abc123def456789", ... } │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │
│ │ │ Check │ │ Set Flag │ │ Return Early │ │ │ │ └──► PB_RECORD_ID = "abc123def456789" │
│ │ │ POST_UPDATE_ │ │ if First │ │ if Already │ │
│ │ │ DONE │ │ Update │ │ Updated │ │
│ │ │ │ │ │ │ │ │
│ │ │ • Check if │ │ • Set │ │ • Return 0 │ │
│ │ │ already │ │ POST_UPDATE_ │ │ • Skip API call │ │
│ │ │ updated │ │ DONE=true │ │ • Prevent │ │
│ │ │ • Prevent │ │ • Continue │ │ duplicate │ │
│ │ │ duplicate │ │ with update │ │ requests │ │
│ │ │ requests │ │ │ │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Status and Error Processing │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ Status Determination │ │
│ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │
│ │ │ Determine │ │ Get Error │ │ Prepare Status │ │ │
│ │ │ Status │ │ Description │ │ Data │ │
│ │ │ │ │ │ │ │ │
│ │ │ • status: │ │ • Call │ │ • Include status │ │
│ │ │ "success" or │ │ get_error_ │ │ • Include error │ │
│ │ │ "failed" │ │ description() │ │ description │ │
│ │ │ • Set exit │ │ • Get human- │ │ • Include random │ │
│ │ │ code based │ │ readable │ │ ID for tracking │ │
│ │ │ on status │ │ error message │ │ │ │
│ │ │ • Default to │ │ • Handle │ │ │ │
│ │ │ error if │ │ unknown │ │ │ │
│ │ │ not set │ │ errors │ │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Status Update API Request │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ Status Update Payload Creation │ │
│ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │
│ │ │ Create │ │ Send Status │ │ Mark Update │ │ │
│ │ │ Status JSON │ │ Update │ │ Complete │ │
│ │ │ │ │ │ │ │ │
│ │ │ • Include │ │ • POST to │ │ • Set │ │
│ │ │ status │ │ updatestatus │ │ POST_UPDATE_ │ │
│ │ │ • Include │ │ endpoint │ │ DONE=true │ │
│ │ │ error │ │ • Include JSON │ │ • Prevent further │ │
│ │ │ description │ │ payload │ │ updates │ │
│ │ │ • Include │ │ • Handle │ │ • Complete │ │
│ │ │ random_id │ │ response │ │ process │ │
│ │ │ │ │ gracefully │ │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────┘
``` ```
## Error Description Flow ## VM API Reporting Flow — `post_to_api_vm()`
``` ```
┌─────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────┐
GET_ERROR_DESCRIPTION() Flow │ post_to_api_vm() Flow │
Convert numeric exit codes to human-readable explanations │ POST → Create VM telemetry record in PocketBase │
└─────────────────────┬───────────────────────────────────────────────────────────┘ └─────────────────────┬───────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────┐
│ Error Code Classification │ │ Read /usr/local/community-scripts/diagnostics │
│ │ │ Extract DIAGNOSTICS=yes/no from file │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ └─────────────────────┬───────────────────────────────────────────────────────────┘
│ │ Error Code Categories │ │
│ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │
│ │ │ General │ │ Network │ │ LXC-Specific │ │ │
│ │ │ System │ │ Errors │ │ Errors │ │
│ │ │ Errors │ │ │ │ │ │
│ │ │ │ │ • 18: Connection│ │ • 100-101: LXC │ │
│ │ │ • 0-9: Basic │ │ failed │ │ install errors │ │
│ │ │ errors │ │ • 22: Invalid │ │ • 200-209: LXC │ │
│ │ │ • 126-128: │ │ argument │ │ creation errors │ │
│ │ │ Command │ │ • 28: No space │ │ │ │
│ │ │ errors │ │ • 35: Timeout │ │ │ │
│ │ │ • 129-143: │ │ • 56: TLS error │ │ │ │
│ │ │ Signal │ │ • 60: SSL cert │ │ │ │
│ │ │ errors │ │ error │ │ │ │
│ │ │ • 152: Resource │ │ │ │ │ │
│ │ │ limit │ │ │ │ │ │
│ │ │ • 255: Unknown │ │ │ │ │ │
│ │ │ critical │ │ │ │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────┐
│ Error Message Return │ │ Prerequisites: curl? ──► DIAGNOSTICS="yes"? ──► RANDOM_UUID set? │
│ │ │ (return silently on any failure) │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ └─────────────────────┬───────────────────────────────────────────────────────────┘
│ │ Error Message Formatting │ │
│ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ ┌─────────────────────────────────────────────────────────────────────────────────┐
│ │ │ Match Error │ │ Return │ │ Default Case │ │ │ │ VM Data Preparation │
│ │ │ Code │ │ Description │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────────┐ ┌───────────────────┐ │
│ │ │ • Use case │ │ • Return │ │ • Return "Unknown │ │ │ │ Set VM type │ │ Process disk size │ │ Set initial │ │
│ │ │ statement │ │ human- │ │ error code │ │ │ │ │ │ │ │ status │ │
│ │ │ • Match │ │ readable │ │ (exit_code)" │ │ │ │ • ct_type: 2 │ │ • Strip 'G' suffix │ │ │ │
│ │ │ specific │ │ message │ │ • Handle │ │ │ │ • type: "vm" │ │ "20G" → 20 │ │ • status: │ │
│ │ │ codes │ │ • Include │ │ unrecognized │ │ │ │ │ │ • Store in │ │ "installing" │ │
│ │ │ • Handle │ │ context │ │ codes │ │ │ │ │ │ DISK_SIZE_API │ │ • random_id: │ │
│ │ │ ranges │ │ information │ │ • Provide fallback │ │ │ │ │ │ │ │ RANDOM_UUID │ │
│ │ │ │ │ │ │ message │ │ │ └─────────────────┘ └─────────────────────┘ └───────────────────┘ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ └─────────────────────┬───────────────────────────────────────────────────────────┘
│ └─────────────────────────────────────────────────────────────────────────────┘ │
┌─────────────────────────────────────────────────────────────────────────────────┐
│ POST → PB_API_URL │
│ http://db.community-scripts.org/api/collections/_dev_telemetry_data/records │
│ │
│ Response (HTTP 200/201): │
│ { "id": "xyz789abc012345", ... } │
│ │ │
│ └──► PB_RECORD_ID = "xyz789abc012345" │
└─────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────┘
``` ```
## Status Update Flow — `post_update_to_api()`
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ post_update_to_api(status, exit_code) Flow │
│ PATCH → Update existing PocketBase record with final status │
└─────────────────────┬───────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Duplicate Prevention Check │
│ │
│ ┌─────────────────┐ ┌──────────────────────────────────────────────┐ │
│ │ Check │ │ POST_UPDATE_DONE == "true"? │ │
│ │ POST_UPDATE_ │───►│ │ │
│ │ DONE flag │ │ YES → return 0 (skip PATCH) │ │
│ │ │ │ NO → continue │ │
│ └─────────────────┘ └──────────────────────────────────────────────┘ │
└─────────────────────┬───────────────────────────────────────────────────────────┘
│ (first call only)
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Prerequisites: curl? ──► DIAGNOSTICS="yes"? ──► RANDOM_UUID set? │
└─────────────────────┬───────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Status Mapping │
│ │
│ Input $1 │ PocketBase status │ exit_code │ error │
│ ─────────────────┼─────────────────────┼──────────────┼────────────────────── │
│ "done"/"success" │ "sucess" │ 0 │ "" │
│ "failed" │ "failed" │ from $2 │ explain_exit_code() │
│ anything else │ "unknown" │ from $2 │ explain_exit_code() │
│ │
│ Note: PocketBase schema spells it "sucess" intentionally │
└─────────────────────┬───────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Record ID Resolution │
│ │
│ ┌──────────────────────────┐ ┌──────────────────────────────────────┐ │
│ │ PB_RECORD_ID set? │ │ Fallback: GET lookup │ │
│ │ │ │ │ │
│ │ YES → use PB_RECORD_ID │ │ GET PB_API_URL │ │
│ │ │ │ ?filter=(random_id='UUID') │ │
│ │ NO → try GET lookup ───┼───►│ &fields=id │ │
│ │ │ │ &perPage=1 │ │
│ │ │ │ │ │
│ │ │ │ Extract "id" from response │ │
│ │ │ │ If not found → set flag, return │ │
│ └──────────────────────────┘ └──────────────────────────────────────┘ │
└─────────────────────┬───────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐
│ PATCH Request │
│ │
│ PATCH → PB_API_URL/{record_id} │
│ http://db.community-scripts.org/api/collections/_dev_telemetry_data/ │
│ records/{record_id} │
│ │
│ Payload: │
│ { │
│ "status": "sucess" | "failed" | "unknown", │
│ "error": "..." | "", │
│ "exit_code": 0 | <numeric>
│ } │
│ │
│ ──► POST_UPDATE_DONE = true (prevents future calls) │
└─────────────────────────────────────────────────────────────────────────────────┘
```
## Error Description Flow — `explain_exit_code()`
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ explain_exit_code(code) Flow │
│ Convert numeric exit codes to human-readable descriptions │
│ Canonical function — used by api.func AND error_handler.func │
└─────────────────────┬───────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Exit Code Classification (non-overlapping ranges) │
│ │
│ ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ Generic/Shell │ │ curl/wget │ │ APT/DPKG │ │
│ │ 12 │ │ 6, 7, 22, 28, 35│ │ 100102, 255 │ │
│ └─────────────────┘ └──────────────────┘ └──────────────────────────┘ │
│ │
│ ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ System/Signals │ │ Systemd/Service │ │ Python/pip/uv │ │
│ │ 124143 │ │ 150154 │ │ 160162 │ │
│ └─────────────────┘ └──────────────────┘ └──────────────────────────┘ │
│ │
│ ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ PostgreSQL │ │ MySQL/MariaDB │ │ MongoDB │ │
│ │ 170173 │ │ 180183 │ │ 190193 │ │
│ └─────────────────┘ └──────────────────┘ └──────────────────────────┘ │
│ │
│ ┌─────────────────┐ ┌──────────────────┐ │
│ │ Proxmox │ │ Node.js/npm │ │
│ │ 200231 │ │ 243249 │ │
│ └─────────────────┘ └──────────────────┘ │
└─────────────────────┬───────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐
│ case "$code" in │
<matched>) echo "<description>" ;; │
│ *) echo "Unknown error" ;; │
│ esac │
└─────────────────────────────────────────────────────────────────────────────────┘
```
## Complete Installation Lifecycle
```
┌──────────────────────────────────────────────────────────────┐
│ Installation Script (e.g. build.func / vm-core.func) │
└────────┬─────────────────────────────────────────────────────┘
│ 1. source api.func
│ 2. Set DIAGNOSTICS, RANDOM_UUID, NSAPP, etc.
┌──────────────────────────────────────────────────────────────┐
│ post_to_api() / post_to_api_vm() │
│ │
│ POST → PB_API_URL │
│ Body: { ..., "status": "installing", "random_id": "..." } │
│ │
│ Response → PB_RECORD_ID = "abc123def456789" │
└────────┬─────────────────────────────────────────────────────┘
│ 3. Installation proceeds...
│ (container/VM creation, package install, etc.)
┌──────────────────────────────────────────────────────────────┐
│ post_update_to_api("done", 0) │
│ or │
│ post_update_to_api("failed", $exit_code) │
│ │
│ PATCH → PB_API_URL/{PB_RECORD_ID} │
│ Body: { "status": "sucess", "error": "", "exit_code": 0 } │
│ or { "status": "failed", "error": "...", "exit_code": N }│
│ │
│ POST_UPDATE_DONE = true │
└──────────────────────────────────────────────────────────────┘
```
## Integration Points ## Integration Points
### With Installation Scripts ### With Installation Scripts
- **build.func**: Sends LXC installation data - **build.func**: Calls `post_to_api()` for LXC creation, then `post_update_to_api()` on completion
- **vm-core.func**: Sends VM installation data - **vm-core.func**: Calls `post_to_api_vm()` for VM creation, then `post_update_to_api()` on completion
- **install.func**: Reports installation status - **install.func / alpine-install.func**: Reports installation status via `post_update_to_api()`
- **alpine-install.func**: Reports Alpine installation data
### With Error Handling ### With Error Handling
- **error_handler.func**: Provides error explanations - **error_handler.func**: Uses `explain_exit_code()` for human-readable error messages
- **core.func**: Uses error descriptions in silent execution - **Diagnostic reporting**: PocketBase records track error patterns anonymously
- **Diagnostic reporting**: Tracks error patterns
### External Dependencies ### External Dependencies
- **curl**: HTTP client for API communication - **curl**: HTTP client for PocketBase API communication
- **Community Scripts API**: External API endpoint - **PocketBase**: Backend at `http://db.community-scripts.org`
- **Network connectivity**: Required for API communication - **Network connectivity**: Required for API communication (failures are silently ignored)

View File

@ -2,63 +2,88 @@
## Overview ## Overview
This document provides a comprehensive alphabetical reference of all functions in `api.func`, including parameters, dependencies, usage examples, and error handling. This document provides a comprehensive reference of all functions in `api.func`, including parameters, dependencies, usage examples, and error handling. The backend is **PocketBase** hosted at `http://db.community-scripts.org`.
## Configuration Variables
| Variable | Value | Description |
|----------|-------|-------------|
| `PB_URL` | `http://db.community-scripts.org` | PocketBase server URL |
| `PB_COLLECTION` | `_dev_telemetry_data` | PocketBase collection name |
| `PB_API_URL` | `${PB_URL}/api/collections/${PB_COLLECTION}/records` | Full API endpoint |
| `PB_RECORD_ID` | *(runtime)* | Stores the PocketBase record ID returned by POST for later PATCH calls |
## Function Categories ## Function Categories
### Error Description Functions ### Error Description Functions
#### `get_error_description()` #### `explain_exit_code()`
**Purpose**: Convert numeric exit codes to human-readable explanations **Purpose**: Convert numeric exit codes to human-readable explanations
**Parameters**: **Parameters**:
- `$1` - Exit code to explain - `$1` Exit code to explain
**Returns**: Human-readable error explanation string **Returns**: Human-readable error explanation string
**Side Effects**: None **Side Effects**: None
**Dependencies**: None **Dependencies**: None
**Environment Variables Used**: None **Environment Variables Used**: None
**Supported Exit Codes**: > **Note**: `explain_exit_code()` is the **canonical** function for exit-code mapping. It is used by both `api.func` (telemetry) and `error_handler.func` (error display).
- **General System**: 0-9, 18, 22, 28, 35, 56, 60, 125-128, 129-143, 152, 255
- **LXC-Specific**: 100-101, 200-209 **Supported Exit Code Ranges** (non-overlapping):
- **Docker**: 125
| Range | Category |
|-------|----------|
| 12 | Generic / Shell |
| 635 | curl / wget |
| 100102 | APT / Package manager |
| 124143 | System / Signals |
| 150154 | Systemd / Service |
| 160162 | Python / pip / uv |
| 170173 | PostgreSQL |
| 180183 | MySQL / MariaDB |
| 190193 | MongoDB |
| 200231 | Proxmox custom codes |
| 243249 | Node.js / npm |
| 255 | DPKG fatal |
**Usage Example**: **Usage Example**:
```bash ```bash
error_msg=$(get_error_description 127) error_msg=$(explain_exit_code 127)
echo "Error 127: $error_msg" echo "Error 127: $error_msg"
# Output: Error 127: Command not found: Incorrect path or missing dependency. # Output: Error 127: Command not found
``` ```
**Error Code Examples**: **Error Code Examples**:
```bash ```bash
get_error_description 0 # " " (space) explain_exit_code 1 # "General error / Operation not permitted"
get_error_description 1 # "General error: An unspecified error occurred." explain_exit_code 22 # "curl: HTTP error returned (404, 429, 500+)"
get_error_description 127 # "Command not found: Incorrect path or missing dependency." explain_exit_code 127 # "Command not found"
get_error_description 200 # "LXC creation failed." explain_exit_code 200 # "Proxmox: Failed to create lock file"
get_error_description 255 # "Unknown critical error, often due to missing permissions or broken scripts." explain_exit_code 255 # "DPKG: Fatal internal error"
explain_exit_code 999 # "Unknown error"
``` ```
### API Communication Functions ### API Communication Functions
#### `post_to_api()` #### `post_to_api()`
**Purpose**: Send LXC container installation data to community-scripts.org API
**Purpose**: Create an LXC container telemetry record in PocketBase
**Parameters**: None (uses environment variables) **Parameters**: None (uses environment variables)
**Returns**: None **Returns**: None
**Side Effects**: **Side Effects**:
- Sends HTTP POST request to API - Sends HTTP **POST** to `PB_API_URL`
- Stores response in RESPONSE variable - Stores the returned PocketBase record `id` in `PB_RECORD_ID` for later PATCH updates
- Requires curl command and network connectivity
**Dependencies**: `curl` command **Dependencies**: `curl` command
**Environment Variables Used**: `DIAGNOSTICS`, `RANDOM_UUID`, `CT_TYPE`, `DISK_SIZE`, `CORE_COUNT`, `RAM_SIZE`, `var_os`, `var_version`, `DISABLEIP6`, `NSAPP`, `METHOD` **Environment Variables Used**: `DIAGNOSTICS`, `RANDOM_UUID`, `CT_TYPE`, `DISK_SIZE`, `CORE_COUNT`, `RAM_SIZE`, `var_os`, `var_version`, `NSAPP`, `METHOD`
**Prerequisites**: **Prerequisites**:
- `curl` command must be available - `curl` must be available
- `DIAGNOSTICS` must be set to "yes" - `DIAGNOSTICS` must be `"yes"`
- `RANDOM_UUID` must be set and not empty - `RANDOM_UUID` must be set and not empty
**API Endpoint**: `http://api.community-scripts.org/dev/upload` **API Endpoint**: `POST http://db.community-scripts.org/api/collections/_dev_telemetry_data/records`
**JSON Payload Structure**: **JSON Payload**:
```json ```json
{ {
"ct_type": 1, "ct_type": 1,
@ -68,7 +93,6 @@ get_error_description 255 # "Unknown critical error, often due to missing perm
"ram_size": 2048, "ram_size": 2048,
"os_type": "debian", "os_type": "debian",
"os_version": "12", "os_version": "12",
"disableip6": "true",
"nsapp": "plex", "nsapp": "plex",
"method": "install", "method": "install",
"pve_version": "8.0", "pve_version": "8.0",
@ -77,6 +101,10 @@ get_error_description 255 # "Unknown critical error, often due to missing perm
} }
``` ```
**Response Handling**:
- On HTTP 200/201, `PB_RECORD_ID` is extracted from the response JSON (`"id"` field)
- On failure, the function returns silently without blocking the installation
**Usage Example**: **Usage Example**:
```bash ```bash
export DIAGNOSTICS="yes" export DIAGNOSTICS="yes"
@ -91,39 +119,39 @@ export NSAPP="plex"
export METHOD="install" export METHOD="install"
post_to_api post_to_api
# PB_RECORD_ID is now set (e.g. "abc123def456789")
``` ```
#### `post_to_api_vm()` #### `post_to_api_vm()`
**Purpose**: Send VM installation data to community-scripts.org API
**Purpose**: Create a VM telemetry record in PocketBase
**Parameters**: None (uses environment variables) **Parameters**: None (uses environment variables)
**Returns**: None **Returns**: None
**Side Effects**: **Side Effects**:
- Sends HTTP POST request to API - Sends HTTP **POST** to `PB_API_URL`
- Stores response in RESPONSE variable - Stores the returned PocketBase record `id` in `PB_RECORD_ID`
- Requires curl command and network connectivity
**Dependencies**: `curl` command, diagnostics file **Dependencies**: `curl` command, diagnostics file
**Environment Variables Used**: `DIAGNOSTICS`, `RANDOM_UUID`, `DISK_SIZE`, `CORE_COUNT`, `RAM_SIZE`, `var_os`, `var_version`, `NSAPP`, `METHOD` **Environment Variables Used**: `RANDOM_UUID`, `DISK_SIZE`, `CORE_COUNT`, `RAM_SIZE`, `var_os`, `var_version`, `NSAPP`, `METHOD`
**Prerequisites**: **Prerequisites**:
- `/usr/local/community-scripts/diagnostics` file must exist - `/usr/local/community-scripts/diagnostics` file must exist
- `DIAGNOSTICS` must be set to "yes" in diagnostics file - `DIAGNOSTICS` must be `"yes"` in that file (read at runtime)
- `curl` command must be available - `curl` must be available
- `RANDOM_UUID` must be set and not empty - `RANDOM_UUID` must be set and not empty
**API Endpoint**: `http://api.community-scripts.org/dev/upload` **API Endpoint**: `POST http://db.community-scripts.org/api/collections/_dev_telemetry_data/records`
**JSON Payload Structure**: **JSON Payload**:
```json ```json
{ {
"ct_type": 2, "ct_type": 2,
"type": "vm", "type": "vm",
"disk_size": 8, "disk_size": 20,
"core_count": 2, "core_count": 4,
"ram_size": 2048, "ram_size": 4096,
"os_type": "debian", "os_type": "ubuntu",
"os_version": "12", "os_version": "22.04",
"disableip6": "", "nsapp": "nextcloud",
"nsapp": "plex",
"method": "install", "method": "install",
"pve_version": "8.0", "pve_version": "8.0",
"status": "installing", "status": "installing",
@ -131,50 +159,81 @@ post_to_api
} }
``` ```
> **Note**: `DISK_SIZE` is stripped of its `G` suffix before sending (e.g. `"20G"``20`).
**Usage Example**: **Usage Example**:
```bash ```bash
# Create diagnostics file # Create diagnostics file
mkdir -p /usr/local/community-scripts
echo "DIAGNOSTICS=yes" > /usr/local/community-scripts/diagnostics echo "DIAGNOSTICS=yes" > /usr/local/community-scripts/diagnostics
export RANDOM_UUID="$(uuidgen)" export RANDOM_UUID="$(uuidgen)"
export DISK_SIZE="8G" export DISK_SIZE="20G"
export CORE_COUNT=2 export CORE_COUNT=4
export RAM_SIZE=2048 export RAM_SIZE=4096
export var_os="debian" export var_os="ubuntu"
export var_version="12" export var_version="22.04"
export NSAPP="plex" export NSAPP="nextcloud"
export METHOD="install" export METHOD="install"
post_to_api_vm post_to_api_vm
# PB_RECORD_ID is now set
``` ```
#### `post_update_to_api()` #### `post_update_to_api()`
**Purpose**: Send installation completion status to community-scripts.org API
**Purpose**: Update an existing PocketBase record with installation completion status via PATCH
**Parameters**: **Parameters**:
- `$1` - Status ("success" or "failed", default: "failed") - `$1` — Status (`"done"`, `"success"`, or `"failed"`; default: `"failed"`)
- `$2` - Exit code (default: 1) - `$2` — Exit code (numeric, default: `1`)
**Returns**: None **Returns**: None
**Side Effects**: **Side Effects**:
- Sends HTTP POST request to API - Sends HTTP **PATCH** to `PB_API_URL/{record_id}`
- Sets POST_UPDATE_DONE=true to prevent duplicates - Sets `POST_UPDATE_DONE=true` to prevent duplicate calls
- Stores response in RESPONSE variable **Dependencies**: `curl`, `explain_exit_code()`
**Dependencies**: `curl` command, `get_error_description()` **Environment Variables Used**: `DIAGNOSTICS`, `RANDOM_UUID`, `PB_RECORD_ID`
**Environment Variables Used**: `DIAGNOSTICS`, `RANDOM_UUID`
**Prerequisites**: **Prerequisites**:
- `curl` command must be available - `curl` must be available
- `DIAGNOSTICS` must be set to "yes" - `DIAGNOSTICS` must be `"yes"`
- `RANDOM_UUID` must be set and not empty - `RANDOM_UUID` must be set and not empty
- POST_UPDATE_DONE must be false (prevents duplicates) - `POST_UPDATE_DONE` must not be `"true"` (prevents duplicate updates)
**API Endpoint**: `http://api.community-scripts.org/dev/upload/updatestatus` **Record Lookup**:
1. If `PB_RECORD_ID` is already set (from a prior `post_to_api` / `post_to_api_vm` call), it is used directly.
2. Otherwise, the function performs a **GET** lookup:
```
GET PB_API_URL?filter=(random_id='<RANDOM_UUID>')&fields=id&perPage=1
```
3. If no record is found, the function sets `POST_UPDATE_DONE=true` and returns.
**JSON Payload Structure**: **Status Mapping** (PocketBase select field values: `installing`, `sucess`, `failed`, `unknown`):
| Input Status | PocketBase `status` | `exit_code` | `error` |
|---|---|---|---|
| `"done"` / `"success"` / `"sucess"` | `"sucess"` | `0` | `""` |
| `"failed"` | `"failed"` | *from $2* | *from `explain_exit_code()`* |
| anything else | `"unknown"` | *from $2* | *from `explain_exit_code()`* |
> **Note**: The PocketBase schema intentionally spells success as `"sucess"`.
**API Endpoint**: `PATCH http://db.community-scripts.org/api/collections/_dev_telemetry_data/records/{record_id}`
**JSON Payload**:
```json ```json
{ {
"status": "success", "status": "sucess",
"error": "Error description from get_error_description()", "error": "",
"random_id": "uuid-string" "exit_code": 0
}
```
or for failures:
```json
{
"status": "failed",
"error": "Command not found",
"exit_code": 127
} }
``` ```
@ -183,10 +242,10 @@ post_to_api_vm
export DIAGNOSTICS="yes" export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)" export RANDOM_UUID="$(uuidgen)"
# Report successful installation # After a successful installation
post_update_to_api "success" 0 post_update_to_api "done" 0
# Report failed installation # After a failed installation
post_update_to_api "failed" 127 post_update_to_api "failed" 127
``` ```
@ -196,198 +255,250 @@ post_update_to_api "failed" 127
``` ```
post_to_api() post_to_api()
├── Check curl availability ├── Check curl availability
├── Check DIAGNOSTICS setting ├── Check DIAGNOSTICS == "yes"
├── Check RANDOM_UUID ├── Check RANDOM_UUID is set
├── Get PVE version ├── Get PVE version
├── Create JSON payload ├── Create JSON payload (ct_type=1, type="lxc", status="installing")
└── Send HTTP POST request ├── POST to PB_API_URL
└── Extract PB_RECORD_ID from response
post_to_api_vm() post_to_api_vm()
├── Check diagnostics file ├── Read DIAGNOSTICS from /usr/local/community-scripts/diagnostics
├── Check curl availability ├── Check curl availability
├── Check DIAGNOSTICS setting ├── Check DIAGNOSTICS == "yes"
├── Check RANDOM_UUID ├── Check RANDOM_UUID is set
├── Process disk size ├── Strip 'G' suffix from DISK_SIZE
├── Get PVE version ├── Get PVE version
├── Create JSON payload ├── Create JSON payload (ct_type=2, type="vm", status="installing")
└── Send HTTP POST request ├── POST to PB_API_URL
└── Extract PB_RECORD_ID from response
post_update_to_api() post_update_to_api(status, exit_code)
├── Check POST_UPDATE_DONE flag
├── Check curl availability ├── Check curl availability
├── Check DIAGNOSTICS setting ├── Check POST_UPDATE_DONE flag
├── Check RANDOM_UUID ├── Check DIAGNOSTICS == "yes"
├── Determine status and exit code ├── Check RANDOM_UUID is set
├── Get error description ├── Map status → pb_status ("done"→"sucess", "failed"→"failed", *→"unknown")
├── Create JSON payload ├── For failed/unknown: call explain_exit_code(exit_code)
├── Send HTTP POST request ├── Resolve record_id (PB_RECORD_ID or GET lookup by random_id)
├── PATCH to PB_API_URL/{record_id}
└── Set POST_UPDATE_DONE=true └── Set POST_UPDATE_DONE=true
``` ```
### Error Description Flow ### Error Description Flow
``` ```
get_error_description() explain_exit_code(code)
├── Match exit code ├── Match code against case statement (non-overlapping ranges)
├── Return appropriate description ├── Return description string
└── Handle unknown codes └── Default: "Unknown error"
``` ```
## Error Code Reference ## Error Code Reference
### General System Errors ### Generic / Shell (12)
| Code | Description | | Code | Description |
|------|-------------| |------|-------------|
| 0 | (space) | | 1 | General error / Operation not permitted |
| 1 | General error: An unspecified error occurred. | | 2 | Misuse of shell builtins (e.g. syntax error) |
| 2 | Incorrect shell usage or invalid command arguments. |
| 3 | Unexecuted function or invalid shell condition. |
| 4 | Error opening a file or invalid path. |
| 5 | I/O error: An input/output failure occurred. |
| 6 | No such device or address. |
| 7 | Insufficient memory or resource exhaustion. |
| 8 | Non-executable file or invalid file format. |
| 9 | Failed child process execution. |
| 18 | Connection to a remote server failed. |
| 22 | Invalid argument or faulty network connection. |
| 28 | No space left on device. |
| 35 | Timeout while establishing a connection. |
| 56 | Faulty TLS connection. |
| 60 | SSL certificate error. |
### Command Execution Errors ### curl / wget (635)
| Code | Description | | Code | Description |
|------|-------------| |------|-------------|
| 125 | Docker error: Container could not start. | | 6 | curl: DNS resolution failed (could not resolve host) |
| 126 | Command not executable: Incorrect permissions or missing dependencies. | | 7 | curl: Failed to connect (network unreachable / host down) |
| 127 | Command not found: Incorrect path or missing dependency. | | 22 | curl: HTTP error returned (404, 429, 500+) |
| 128 | Invalid exit signal, e.g., incorrect Git command. | | 28 | curl: Operation timeout (network slow or server not responding) |
| 35 | curl: SSL/TLS handshake failed (certificate error) |
### Signal Errors ### APT / Package Manager (100102)
| Code | Description | | Code | Description |
|------|-------------| |------|-------------|
| 129 | Signal 1 (SIGHUP): Process terminated due to hangup. | | 100 | APT: Package manager error (broken packages / dependency problems) |
| 130 | Signal 2 (SIGINT): Manual termination via Ctrl+C. | | 101 | APT: Configuration error (bad sources.list, malformed config) |
| 132 | Signal 4 (SIGILL): Illegal machine instruction. | | 102 | APT: Lock held by another process (dpkg/apt still running) |
| 133 | Signal 5 (SIGTRAP): Debugging error or invalid breakpoint signal. |
| 134 | Signal 6 (SIGABRT): Program aborted itself. |
| 135 | Signal 7 (SIGBUS): Memory error, invalid memory address. |
| 137 | Signal 9 (SIGKILL): Process forcibly terminated (OOM-killer or 'kill -9'). |
| 139 | Signal 11 (SIGSEGV): Segmentation fault, possibly due to invalid pointer access. |
| 141 | Signal 13 (SIGPIPE): Pipe closed unexpectedly. |
| 143 | Signal 15 (SIGTERM): Process terminated normally. |
| 152 | Signal 24 (SIGXCPU): CPU time limit exceeded. |
### LXC-Specific Errors ### System / Signals (124143)
| Code | Description | | Code | Description |
|------|-------------| |------|-------------|
| 100 | LXC install error: Unexpected error in create_lxc.sh. | | 124 | Command timed out (timeout command) |
| 101 | LXC install error: No network connection detected. | | 126 | Command invoked cannot execute (permission problem?) |
| 200 | LXC creation failed. | | 127 | Command not found |
| 201 | LXC error: Invalid Storage class. | | 128 | Invalid argument to exit |
| 202 | User aborted menu in create_lxc.sh. | | 130 | Terminated by Ctrl+C (SIGINT) |
| 203 | CTID not set in create_lxc.sh. | | 134 | Process aborted (SIGABRT — possibly Node.js heap overflow) |
| 204 | PCT_OSTYPE not set in create_lxc.sh. | | 137 | Killed (SIGKILL / Out of memory?) |
| 205 | CTID cannot be less than 100 in create_lxc.sh. | | 139 | Segmentation fault (core dumped) |
| 206 | CTID already in use in create_lxc.sh. | | 141 | Broken pipe (SIGPIPE — output closed prematurely) |
| 207 | Template not found in create_lxc.sh. | | 143 | Terminated (SIGTERM) |
| 208 | Error downloading template in create_lxc.sh. |
| 209 | Container creation failed, but template is intact in create_lxc.sh. |
### Other Errors ### Systemd / Service (150154)
| Code | Description | | Code | Description |
|------|-------------| |------|-------------|
| 255 | Unknown critical error, often due to missing permissions or broken scripts. | | 150 | Systemd: Service failed to start |
| * | Unknown error code (exit_code). | | 151 | Systemd: Service unit not found |
| 152 | Permission denied (EACCES) |
| 153 | Build/compile failed (make/gcc/cmake) |
| 154 | Node.js: Native addon build failed (node-gyp) |
### Python / pip / uv (160162)
| Code | Description |
|------|-------------|
| 160 | Python: Virtualenv / uv environment missing or broken |
| 161 | Python: Dependency resolution failed |
| 162 | Python: Installation aborted (permissions or EXTERNALLY-MANAGED) |
### PostgreSQL (170173)
| Code | Description |
|------|-------------|
| 170 | PostgreSQL: Connection failed (server not running / wrong socket) |
| 171 | PostgreSQL: Authentication failed (bad user/password) |
| 172 | PostgreSQL: Database does not exist |
| 173 | PostgreSQL: Fatal error in query / syntax |
### MySQL / MariaDB (180183)
| Code | Description |
|------|-------------|
| 180 | MySQL/MariaDB: Connection failed (server not running / wrong socket) |
| 181 | MySQL/MariaDB: Authentication failed (bad user/password) |
| 182 | MySQL/MariaDB: Database does not exist |
| 183 | MySQL/MariaDB: Fatal error in query / syntax |
### MongoDB (190193)
| Code | Description |
|------|-------------|
| 190 | MongoDB: Connection failed (server not running) |
| 191 | MongoDB: Authentication failed (bad user/password) |
| 192 | MongoDB: Database not found |
| 193 | MongoDB: Fatal query error |
### Proxmox Custom Codes (200231)
| Code | Description |
|------|-------------|
| 200 | Proxmox: Failed to create lock file |
| 203 | Proxmox: Missing CTID variable |
| 204 | Proxmox: Missing PCT_OSTYPE variable |
| 205 | Proxmox: Invalid CTID (<100) |
| 206 | Proxmox: CTID already in use |
| 207 | Proxmox: Password contains unescaped special characters |
| 208 | Proxmox: Invalid configuration (DNS/MAC/Network format) |
| 209 | Proxmox: Container creation failed |
| 210 | Proxmox: Cluster not quorate |
| 211 | Proxmox: Timeout waiting for template lock |
| 212 | Proxmox: Storage type 'iscsidirect' does not support containers (VMs only) |
| 213 | Proxmox: Storage type does not support 'rootdir' content |
| 214 | Proxmox: Not enough storage space |
| 215 | Proxmox: Container created but not listed (ghost state) |
| 216 | Proxmox: RootFS entry missing in config |
| 217 | Proxmox: Storage not accessible |
| 218 | Proxmox: Template file corrupted or incomplete |
| 219 | Proxmox: CephFS does not support containers — use RBD |
| 220 | Proxmox: Unable to resolve template path |
| 221 | Proxmox: Template file not readable |
| 222 | Proxmox: Template download failed |
| 223 | Proxmox: Template not available after download |
| 224 | Proxmox: PBS storage is for backups only |
| 225 | Proxmox: No template available for OS/Version |
| 231 | Proxmox: LXC stack upgrade failed |
### Node.js / npm (243249)
| Code | Description |
|------|-------------|
| 243 | Node.js: Out of memory (JavaScript heap out of memory) |
| 245 | Node.js: Invalid command-line option |
| 246 | Node.js: Internal JavaScript Parse Error |
| 247 | Node.js: Fatal internal error |
| 248 | Node.js: Invalid C++ addon / N-API failure |
| 249 | npm/pnpm/yarn: Unknown fatal error |
### DPKG (255)
| Code | Description |
|------|-------------|
| 255 | DPKG: Fatal internal error |
### Default
| Code | Description |
|------|-------------|
| * | Unknown error |
## Environment Variable Dependencies ## Environment Variable Dependencies
### Required Variables ### Required Variables
- **`DIAGNOSTICS`**: Enable/disable diagnostic reporting ("yes"/"no") - **`DIAGNOSTICS`**: Enable/disable diagnostic reporting (`"yes"` / `"no"`)
- **`RANDOM_UUID`**: Unique identifier for tracking - **`RANDOM_UUID`**: Unique identifier for session tracking
### Optional Variables ### Container / VM Variables
- **`CT_TYPE`**: Container type (1 for LXC, 2 for VM) - **`CT_TYPE`**: Container type (`1` for LXC, `2` for VM)
- **`DISK_SIZE`**: Disk size in GB (or GB with 'G' suffix for VM) - **`DISK_SIZE`**: Disk size in GB (VMs may include `G` suffix)
- **`CORE_COUNT`**: Number of CPU cores - **`CORE_COUNT`**: Number of CPU cores
- **`RAM_SIZE`**: RAM size in MB - **`RAM_SIZE`**: RAM size in MB
- **`var_os`**: Operating system type - **`var_os`**: Operating system type
- **`var_version`**: OS version - **`var_version`**: OS version
- **`DISABLEIP6`**: IPv6 disable setting - **`NSAPP`**: Application name
- **`NSAPP`**: Namespace application name
- **`METHOD`**: Installation method - **`METHOD`**: Installation method
### Internal Variables ### Internal Variables
- **`POST_UPDATE_DONE`**: Prevents duplicate status updates - **`PB_URL`**: PocketBase server URL
- **`API_URL`**: Community scripts API endpoint - **`PB_COLLECTION`**: PocketBase collection name
- **`JSON_PAYLOAD`**: API request payload - **`PB_API_URL`**: Full PocketBase API endpoint
- **`RESPONSE`**: API response - **`PB_RECORD_ID`**: PocketBase record ID (set after POST, used for PATCH)
- **`DISK_SIZE_API`**: Processed disk size for VM API - **`POST_UPDATE_DONE`**: Flag to prevent duplicate status updates
- **`JSON_PAYLOAD`**: API request payload (local to each function)
- **`RESPONSE`**: API response (local to each function)
## Error Handling Patterns ## Error Handling Patterns
### API Communication Errors ### API Communication Errors
- All API functions handle curl failures gracefully - All API functions return silently on failure — network errors never block installation
- Network errors don't block installation process - Missing prerequisites (no curl, diagnostics disabled, no UUID) cause early return
- Missing prerequisites cause early return - `POST_UPDATE_DONE` flag prevents duplicate PATCH updates
- Duplicate updates are prevented - PocketBase record lookup falls back to `GET ?filter=(random_id='...')` if `PB_RECORD_ID` is unset
### Error Description Errors ### Error Description Errors
- Unknown error codes return generic message - Unknown error codes return `"Unknown error"`
- All error codes are handled with case statement - All recognized codes are handled via a `case` statement with non-overlapping ranges
- Fallback message includes the actual error code - The fallback message is generic (no error code is embedded)
### Prerequisites Validation
- Check curl availability before API calls
- Validate DIAGNOSTICS setting
- Ensure RANDOM_UUID is set
- Check for duplicate updates
## Integration Examples ## Integration Examples
### With build.func ### With build.func (LXC)
```bash ```bash
#!/usr/bin/env bash #!/usr/bin/env bash
source core.func source core.func
source api.func source api.func
source build.func source build.func
# Set up API reporting
export DIAGNOSTICS="yes" export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)" export RANDOM_UUID="$(uuidgen)"
# Report installation start # Report LXC installation start → POST creates record
post_to_api post_to_api
# Container creation... # ... container creation via build.func ...
# ... build.func code ...
# Report completion # Report completion → PATCH updates record
if [[ $? -eq 0 ]]; then if [[ $? -eq 0 ]]; then
post_update_to_api "success" 0 post_update_to_api "done" 0
else else
post_update_to_api "failed" $? post_update_to_api "failed" $?
fi fi
``` ```
### With vm-core.func ### With vm-core.func (VM)
```bash ```bash
#!/usr/bin/env bash #!/usr/bin/env bash
source core.func source core.func
source api.func source api.func
source vm-core.func source vm-core.func
# Set up API reporting
export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)" export RANDOM_UUID="$(uuidgen)"
# Report VM installation start # Report VM installation start → POST creates record
post_to_api_vm post_to_api_vm
# VM creation... # ... VM creation via vm-core.func ...
# ... vm-core.func code ...
# Report completion # Report completion → PATCH updates record
post_update_to_api "success" 0 post_update_to_api "done" 0
``` ```
### With error_handler.func ### With error_handler.func
@ -397,37 +508,30 @@ source core.func
source error_handler.func source error_handler.func
source api.func source api.func
# Use error descriptions
error_code=127 error_code=127
error_msg=$(get_error_description $error_code) error_msg=$(explain_exit_code $error_code)
echo "Error $error_code: $error_msg" echo "Error $error_code: $error_msg"
# Report error to API # Report error to PocketBase
post_update_to_api "failed" $error_code post_update_to_api "failed" $error_code
``` ```
## Best Practices ## Best Practices
### API Usage ### API Usage
1. Always check prerequisites before API calls 1. Always check prerequisites before API calls (handled internally by each function)
2. Use unique identifiers for tracking 2. Call `post_to_api` / `post_to_api_vm` **once** at installation start to get a `PB_RECORD_ID`
3. Handle API failures gracefully 3. Call `post_update_to_api` **once** at the end to finalize the record via PATCH
4. Don't block installation on API failures 4. Never block the installation on API failures
### Error Reporting ### Error Reporting
1. Use appropriate error codes 1. Use `explain_exit_code()` for human-readable error messages
2. Provide meaningful error descriptions 2. Pass the actual numeric exit code to `post_update_to_api`
3. Report both success and failure cases 3. Report both success (`"done"`) and failure (`"failed"`) cases
4. Prevent duplicate status updates 4. The `POST_UPDATE_DONE` flag automatically prevents duplicate updates
### Diagnostic Reporting ### Diagnostic Reporting
1. Respect user privacy settings 1. Respect user privacy — only send data when `DIAGNOSTICS="yes"`
2. Only send data when diagnostics enabled 2. Use anonymous random UUIDs for session tracking (no personal data)
3. Use anonymous tracking identifiers 3. Include relevant system information (PVE version, OS, app name)
4. Include relevant system information 4. The diagnostics file at `/usr/local/community-scripts/diagnostics` controls VM reporting
### Error Handling
1. Handle unknown error codes gracefully
2. Provide fallback error messages
3. Include error code in unknown error messages
4. Use consistent error message format

View File

@ -2,26 +2,42 @@
## Overview ## Overview
This document describes how `api.func` integrates with other components in the Proxmox Community Scripts project, including dependencies, data flow, and API surface. This document describes how `api.func` integrates with other components in the Proxmox Community Scripts project. The telemetry backend is **PocketBase** at `http://db.community-scripts.org`, using the `_dev_telemetry_data` collection.
## Architecture
```
Installation Scripts ──► api.func ──► PocketBase (db.community-scripts.org)
├─ POST → create record (status: "installing")
├─ PATCH → update record (status: "sucess"/"failed")
└─ GET → lookup record by random_id (fallback)
```
### Key Design Points
- **POST** creates a new telemetry record and returns a PocketBase `id`
- **PATCH** updates the existing record using that `id` (or a GET lookup by `random_id`)
- All communication is fire-and-forget — failures never block the installation
- `explain_exit_code()` is the canonical function for exit-code-to-description mapping
## Dependencies ## Dependencies
### External Dependencies ### External Dependencies
#### Required Commands #### Required Commands
- **`curl`**: HTTP client for API communication - **`curl`**: HTTP client for PocketBase API communication
- **`uuidgen`**: Generate unique identifiers (optional, can use other methods)
#### Optional Commands #### Optional Commands
- **None**: No other external command dependencies - **`uuidgen`**: Generate unique identifiers (any UUID source works)
- **`pveversion`**: Retrieve Proxmox VE version (gracefully skipped if missing)
### Internal Dependencies ### Internal Dependencies
#### Environment Variables from Other Scripts #### Environment Variables from Other Scripts
- **build.func**: Provides container creation variables - **build.func**: Provides container creation variables (`CT_TYPE`, `DISK_SIZE`, etc.)
- **vm-core.func**: Provides VM creation variables - **vm-core.func**: Provides VM creation variables
- **core.func**: Provides system information variables - **core.func**: Provides system information
- **Installation scripts**: Provide application-specific variables - **Installation scripts**: Provide application-specific variables (`NSAPP`, `METHOD`)
## Integration Points ## Integration Points
@ -29,48 +45,41 @@ This document describes how `api.func` integrates with other components in the P
#### LXC Container Reporting #### LXC Container Reporting
```bash ```bash
# build.func uses api.func for container reporting
source core.func source core.func
source api.func source api.func
source build.func source build.func
# Set up API reporting
export DIAGNOSTICS="yes" export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)" export RANDOM_UUID="$(uuidgen)"
# Container creation with API reporting # Set container parameters
create_container() { export CT_TYPE=1
# Set container parameters export DISK_SIZE="$var_disk"
export CT_TYPE=1 export CORE_COUNT="$var_cpu"
export DISK_SIZE="$var_disk" export RAM_SIZE="$var_ram"
export CORE_COUNT="$var_cpu" export var_os="$var_os"
export RAM_SIZE="$var_ram" export var_version="$var_version"
export var_os="$var_os" export NSAPP="$APP"
export var_version="$var_version" export METHOD="install"
export NSAPP="$APP"
export METHOD="install"
# Report installation start # POST → creates record in PocketBase, saves PB_RECORD_ID
post_to_api post_to_api
# Container creation using build.func # ... container creation via build.func ...
# ... build.func container creation logic ...
# Report completion # PATCH → updates the record with final status
if [[ $? -eq 0 ]]; then if [[ $? -eq 0 ]]; then
post_update_to_api "success" 0 post_update_to_api "done" 0
else else
post_update_to_api "failed" $? post_update_to_api "failed" $?
fi fi
}
``` ```
#### Error Reporting Integration #### Error Reporting Integration
```bash ```bash
# build.func uses api.func for error reporting
handle_container_error() { handle_container_error() {
local exit_code=$1 local exit_code=$1
local error_msg=$(get_error_description $exit_code) local error_msg=$(explain_exit_code $exit_code)
echo "Container creation failed: $error_msg" echo "Container creation failed: $error_msg"
post_update_to_api "failed" $exit_code post_update_to_api "failed" $exit_code
@ -81,93 +90,54 @@ handle_container_error() {
#### VM Installation Reporting #### VM Installation Reporting
```bash ```bash
# vm-core.func uses api.func for VM reporting
source core.func source core.func
source api.func source api.func
source vm-core.func source vm-core.func
# Set up VM API reporting # VM reads DIAGNOSTICS from file
mkdir -p /usr/local/community-scripts mkdir -p /usr/local/community-scripts
echo "DIAGNOSTICS=yes" > /usr/local/community-scripts/diagnostics echo "DIAGNOSTICS=yes" > /usr/local/community-scripts/diagnostics
export RANDOM_UUID="$(uuidgen)" export RANDOM_UUID="$(uuidgen)"
# VM creation with API reporting # Set VM parameters
create_vm() { export DISK_SIZE="${var_disk}G"
# Set VM parameters export CORE_COUNT="$var_cpu"
export DISK_SIZE="${var_disk}G" export RAM_SIZE="$var_ram"
export CORE_COUNT="$var_cpu" export var_os="$var_os"
export RAM_SIZE="$var_ram" export var_version="$var_version"
export var_os="$var_os" export NSAPP="$APP"
export var_version="$var_version" export METHOD="install"
export NSAPP="$APP"
export METHOD="install"
# Report VM installation start # POST → creates record in PocketBase (ct_type=2, type="vm")
post_to_api_vm post_to_api_vm
# VM creation using vm-core.func # ... VM creation via vm-core.func ...
# ... vm-core.func VM creation logic ...
# Report completion # PATCH → finalizes record
post_update_to_api "success" 0 post_update_to_api "done" 0
}
```
### With core.func
#### System Information Integration
```bash
# core.func provides system information for api.func
source core.func
source api.func
# Get system information for API reporting
get_system_info_for_api() {
# Get PVE version using core.func utilities
local pve_version=$(pveversion | awk -F'[/ ]' '{print $2}')
# Set API parameters
export var_os="$var_os"
export var_version="$var_version"
# Use core.func error handling with api.func reporting
if silent apt-get update; then
post_update_to_api "success" 0
else
post_update_to_api "failed" $?
fi
}
``` ```
### With error_handler.func ### With error_handler.func
#### Error Description Integration #### Error Description Integration
```bash ```bash
# error_handler.func uses api.func for error descriptions
source core.func source core.func
source error_handler.func source error_handler.func
source api.func source api.func
# Enhanced error handler with API reporting
enhanced_error_handler() { enhanced_error_handler() {
local exit_code=${1:-$?} local exit_code=${1:-$?}
local command=${2:-${BASH_COMMAND:-unknown}} local command=${2:-${BASH_COMMAND:-unknown}}
# Get error description from api.func # explain_exit_code() is the canonical error description function
local error_msg=$(get_error_description $exit_code) local error_msg=$(explain_exit_code $exit_code)
# Display error information
echo "Error $exit_code: $error_msg" echo "Error $exit_code: $error_msg"
echo "Command: $command" echo "Command: $command"
# Report error to API # PATCH the telemetry record with failure details
export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)"
post_update_to_api "failed" $exit_code post_update_to_api "failed" $exit_code
# Use standard error handler
error_handler $exit_code $command
} }
``` ```
@ -175,32 +145,28 @@ enhanced_error_handler() {
#### Installation Process Reporting #### Installation Process Reporting
```bash ```bash
# install.func uses api.func for installation reporting
source core.func source core.func
source api.func source api.func
source install.func source install.func
# Installation with API reporting
install_package_with_reporting() { install_package_with_reporting() {
local package="$1" local package="$1"
# Set up API reporting
export DIAGNOSTICS="yes" export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)" export RANDOM_UUID="$(uuidgen)"
export NSAPP="$package" export NSAPP="$package"
export METHOD="install" export METHOD="install"
# Report installation start # POST → create telemetry record
post_to_api post_to_api
# Package installation using install.func
if install_package "$package"; then if install_package "$package"; then
echo "$package installed successfully" echo "$package installed successfully"
post_update_to_api "success" 0 post_update_to_api "done" 0
return 0 return 0
else else
local exit_code=$? local exit_code=$?
local error_msg=$(get_error_description $exit_code) local error_msg=$(explain_exit_code $exit_code)
echo "$package installation failed: $error_msg" echo "$package installation failed: $error_msg"
post_update_to_api "failed" $exit_code post_update_to_api "failed" $exit_code
return $exit_code return $exit_code
@ -208,270 +174,105 @@ install_package_with_reporting() {
} }
``` ```
### With alpine-install.func
#### Alpine Installation Reporting
```bash
# alpine-install.func uses api.func for Alpine reporting
source core.func
source api.func
source alpine-install.func
# Alpine installation with API reporting
install_alpine_with_reporting() {
local app="$1"
# Set up API reporting
export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)"
export NSAPP="$app"
export METHOD="install"
export var_os="alpine"
# Report Alpine installation start
post_to_api
# Alpine installation using alpine-install.func
if install_alpine_app "$app"; then
echo "Alpine $app installed successfully"
post_update_to_api "success" 0
return 0
else
local exit_code=$?
local error_msg=$(get_error_description $exit_code)
echo "Alpine $app installation failed: $error_msg"
post_update_to_api "failed" $exit_code
return $exit_code
fi
}
```
### With alpine-tools.func
#### Alpine Tools Reporting
```bash
# alpine-tools.func uses api.func for Alpine tools reporting
source core.func
source api.func
source alpine-tools.func
# Alpine tools with API reporting
run_alpine_tool_with_reporting() {
local tool="$1"
# Set up API reporting
export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)"
export NSAPP="alpine-tools"
export METHOD="tool"
# Report tool execution start
post_to_api
# Run Alpine tool using alpine-tools.func
if run_alpine_tool "$tool"; then
echo "Alpine tool $tool executed successfully"
post_update_to_api "success" 0
return 0
else
local exit_code=$?
local error_msg=$(get_error_description $exit_code)
echo "Alpine tool $tool failed: $error_msg"
post_update_to_api "failed" $exit_code
return $exit_code
fi
}
```
### With passthrough.func
#### Hardware Passthrough Reporting
```bash
# passthrough.func uses api.func for hardware reporting
source core.func
source api.func
source passthrough.func
# Hardware passthrough with API reporting
configure_passthrough_with_reporting() {
local hardware_type="$1"
# Set up API reporting
export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)"
export NSAPP="passthrough"
export METHOD="hardware"
# Report passthrough configuration start
post_to_api
# Configure passthrough using passthrough.func
if configure_passthrough "$hardware_type"; then
echo "Hardware passthrough configured successfully"
post_update_to_api "success" 0
return 0
else
local exit_code=$?
local error_msg=$(get_error_description $exit_code)
echo "Hardware passthrough failed: $error_msg"
post_update_to_api "failed" $exit_code
return $exit_code
fi
}
```
### With tools.func
#### Maintenance Operations Reporting
```bash
# tools.func uses api.func for maintenance reporting
source core.func
source api.func
source tools.func
# Maintenance operations with API reporting
run_maintenance_with_reporting() {
local operation="$1"
# Set up API reporting
export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)"
export NSAPP="maintenance"
export METHOD="tool"
# Report maintenance start
post_to_api
# Run maintenance using tools.func
if run_maintenance_operation "$operation"; then
echo "Maintenance operation $operation completed successfully"
post_update_to_api "success" 0
return 0
else
local exit_code=$?
local error_msg=$(get_error_description $exit_code)
echo "Maintenance operation $operation failed: $error_msg"
post_update_to_api "failed" $exit_code
return $exit_code
fi
}
```
## Data Flow ## Data Flow
### Input Data ### Input Data
#### Environment Variables from Other Scripts #### Environment Variables
- **`CT_TYPE`**: Container type (1 for LXC, 2 for VM) | Variable | Source | Description |
- **`DISK_SIZE`**: Disk size in GB |----------|--------|-------------|
- **`CORE_COUNT`**: Number of CPU cores | `CT_TYPE` | build.func | Container type (1=LXC, 2=VM) |
- **`RAM_SIZE`**: RAM size in MB | `DISK_SIZE` | build.func / vm-core.func | Disk size in GB (VMs may have `G` suffix) |
- **`var_os`**: Operating system type | `CORE_COUNT` | build.func / vm-core.func | CPU core count |
- **`var_version`**: OS version | `RAM_SIZE` | build.func / vm-core.func | RAM in MB |
- **`DISABLEIP6`**: IPv6 disable setting | `var_os` | core.func | Operating system type |
- **`NSAPP`**: Namespace application name | `var_version` | core.func | OS version |
- **`METHOD`**: Installation method | `NSAPP` | Installation scripts | Application name |
- **`DIAGNOSTICS`**: Enable/disable diagnostic reporting | `METHOD` | Installation scripts | Installation method |
- **`RANDOM_UUID`**: Unique identifier for tracking | `DIAGNOSTICS` | User config / diagnostics file | Enable/disable telemetry |
| `RANDOM_UUID` | Caller | Session tracking UUID |
#### Function Parameters #### Function Parameters
- **Exit codes**: Passed to `get_error_description()` and `post_update_to_api()` - **Exit codes**: Passed to `explain_exit_code()` and `post_update_to_api()`
- **Status information**: Passed to `post_update_to_api()` - **Status strings**: Passed to `post_update_to_api()` (`"done"`, `"failed"`)
- **API endpoints**: Hardcoded in functions
#### System Information #### System Information
- **PVE version**: Retrieved from `pveversion` command - **PVE version**: Retrieved from `pveversion` command at runtime
- **Disk size processing**: Processed for VM API (removes 'G' suffix) - **Disk size**: VM disk size is stripped of `G` suffix before sending
- **Error codes**: Retrieved from command exit codes
### Processing Data ### Processing
#### API Request Preparation #### Record Creation (POST)
- **JSON payload creation**: Format data for API consumption 1. Validate prerequisites (curl, DIAGNOSTICS, RANDOM_UUID)
- **Data validation**: Ensure required fields are present 2. Gather PVE version
- **Error handling**: Handle missing or invalid data 3. Build JSON payload with all telemetry fields
- **Content type setting**: Set appropriate HTTP headers 4. `POST` to `PB_API_URL`
5. Extract `PB_RECORD_ID` from PocketBase response (HTTP 200/201)
#### Error Processing #### Record Update (PATCH)
- **Error code mapping**: Map numeric codes to descriptions 1. Validate prerequisites + check `POST_UPDATE_DONE` flag
- **Error message formatting**: Format error descriptions 2. Map status string → PocketBase select value (`"done"``"sucess"`)
- **Unknown error handling**: Handle unrecognized error codes 3. For failures: call `explain_exit_code()` to get error description
- **Fallback messages**: Provide default error messages 4. Resolve record ID: use `PB_RECORD_ID` or fall back to GET lookup
5. `PATCH` to `PB_API_URL/{record_id}` with status, error, exit_code
#### API Communication 6. Set `POST_UPDATE_DONE=true`
- **HTTP request preparation**: Prepare curl commands
- **Response handling**: Capture HTTP response codes
- **Error handling**: Handle network and API errors
- **Duplicate prevention**: Prevent duplicate status updates
### Output Data ### Output Data
#### API Communication #### PocketBase Records
- **HTTP requests**: Sent to community-scripts.org API - **POST response**: Returns record with `id` field → stored in `PB_RECORD_ID`
- **Response codes**: Captured from API responses - **PATCH response**: Updates record fields (status, error, exit_code)
- **Error information**: Reported to API - **GET response**: Used for record ID lookup by `random_id` filter
- **Status updates**: Sent to API
#### Error Information #### Internal State
- **Error descriptions**: Human-readable error messages | Variable | Description |
- **Error codes**: Mapped to descriptions |----------|-------------|
- **Context information**: Error context and details | `PB_RECORD_ID` | PocketBase record ID for PATCH calls |
- **Fallback messages**: Default error messages | `POST_UPDATE_DONE` | Flag preventing duplicate updates |
#### System State
- **POST_UPDATE_DONE**: Prevents duplicate updates
- **RESPONSE**: Stores API response
- **JSON_PAYLOAD**: Stores formatted API data
- **API_URL**: Stores API endpoint
## API Surface ## API Surface
### Public Functions ### Public Functions
#### Error Description | Function | Purpose | HTTP Method |
- **`get_error_description()`**: Convert exit codes to explanations |----------|---------|-------------|
- **Parameters**: Exit code to explain | `explain_exit_code(code)` | Map exit code to description | — |
- **Returns**: Human-readable explanation string | `post_to_api()` | Create LXC telemetry record | POST |
- **Usage**: Called by other functions and scripts | `post_to_api_vm()` | Create VM telemetry record | POST |
| `post_update_to_api(status, exit_code)` | Update record with final status | PATCH |
#### API Communication ### PocketBase Collection Schema
- **`post_to_api()`**: Send LXC installation data
- **`post_to_api_vm()`**: Send VM installation data
- **`post_update_to_api()`**: Send status updates
- **Parameters**: Status and exit code (for updates)
- **Returns**: None
- **Usage**: Called by installation scripts
### Internal Functions Collection: `_dev_telemetry_data`
#### None | Field | Type | Required | Description |
- All functions in api.func are public |-------|------|----------|-------------|
- No internal helper functions | `id` | text (auto) | yes | PocketBase record ID (15 chars) |
- Direct implementation of all functionality | `random_id` | text | yes | Session UUID (min 8 chars, unique) |
| `type` | select | yes | `"lxc"`, `"vm"`, `"addon"`, `"pve"` |
| `ct_type` | number | yes | 1 (LXC) or 2 (VM) |
| `nsapp` | text | yes | Application name |
| `status` | select | yes | `"installing"`, `"sucess"`, `"failed"`, `"unknown"` |
| `disk_size` | number | no | Disk size in GB |
| `core_count` | number | no | CPU cores |
| `ram_size` | number | no | RAM in MB |
| `os_type` | text | no | OS type |
| `os_version` | text | no | OS version |
| `pve_version` | text | no | Proxmox VE version |
| `method` | text | no | Installation method |
| `error` | text | no | Error description |
| `exit_code` | number | no | Numeric exit code |
| `created` | autodate | auto | Record creation timestamp |
| `updated` | autodate | auto | Last update timestamp |
### Global Variables > **Note**: The `status` field intentionally uses the spelling `"sucess"` (not `"success"`).
#### Configuration Variables ### Configuration Variables
- **`DIAGNOSTICS`**: Diagnostic reporting setting | Variable | Value |
- **`RANDOM_UUID`**: Unique tracking identifier |----------|-------|
- **`POST_UPDATE_DONE`**: Duplicate update prevention | `PB_URL` | `http://db.community-scripts.org` |
| `PB_COLLECTION` | `_dev_telemetry_data` |
#### Data Variables | `PB_API_URL` | `${PB_URL}/api/collections/${PB_COLLECTION}/records` |
- **`CT_TYPE`**: Container type
- **`DISK_SIZE`**: Disk size
- **`CORE_COUNT`**: CPU core count
- **`RAM_SIZE`**: RAM size
- **`var_os`**: Operating system
- **`var_version`**: OS version
- **`DISABLEIP6`**: IPv6 setting
- **`NSAPP`**: Application namespace
- **`METHOD`**: Installation method
#### Internal Variables
- **`API_URL`**: API endpoint URL
- **`JSON_PAYLOAD`**: API request payload
- **`RESPONSE`**: API response
- **`DISK_SIZE_API`**: Processed disk size for VM API
## Integration Patterns ## Integration Patterns
@ -479,45 +280,39 @@ run_maintenance_with_reporting() {
```bash ```bash
#!/usr/bin/env bash #!/usr/bin/env bash
# Standard integration pattern
# 1. Source core.func first # 1. Source dependencies
source core.func source core.func
# 2. Source api.func
source api.func source api.func
# 3. Set up API reporting # 2. Enable telemetry
export DIAGNOSTICS="yes" export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)" export RANDOM_UUID="$(uuidgen)"
# 4. Set application parameters # 3. Set application parameters
export NSAPP="$APP" export NSAPP="$APP"
export METHOD="install" export METHOD="install"
# 5. Report installation start # 4. POST → create telemetry record in PocketBase
post_to_api post_to_api
# 6. Perform installation # 5. Perform installation
# ... installation logic ... # ... installation logic ...
# 7. Report completion # 6. PATCH → update record with final status
post_update_to_api "success" 0 post_update_to_api "done" 0
``` ```
### Minimal Integration Pattern ### Minimal Integration Pattern
```bash ```bash
#!/usr/bin/env bash #!/usr/bin/env bash
# Minimal integration pattern
source api.func source api.func
# Basic error reporting
export DIAGNOSTICS="yes" export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)" export RANDOM_UUID="$(uuidgen)"
# Report failure # Report failure (PATCH via record lookup)
post_update_to_api "failed" 127 post_update_to_api "failed" 127
``` ```
@ -525,13 +320,10 @@ post_update_to_api "failed" 127
```bash ```bash
#!/usr/bin/env bash #!/usr/bin/env bash
# Advanced integration pattern
source core.func source core.func
source api.func source api.func
source error_handler.func source error_handler.func
# Set up comprehensive API reporting
export DIAGNOSTICS="yes" export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)" export RANDOM_UUID="$(uuidgen)"
export CT_TYPE=1 export CT_TYPE=1
@ -542,12 +334,12 @@ export var_os="debian"
export var_version="12" export var_version="12"
export METHOD="install" export METHOD="install"
# Enhanced error handling with API reporting # Enhanced error handler with PocketBase reporting
enhanced_error_handler() { enhanced_error_handler() {
local exit_code=${1:-$?} local exit_code=${1:-$?}
local command=${2:-${BASH_COMMAND:-unknown}} local command=${2:-${BASH_COMMAND:-unknown}}
local error_msg=$(get_error_description $exit_code) local error_msg=$(explain_exit_code $exit_code)
echo "Error $exit_code: $error_msg" echo "Error $exit_code: $error_msg"
post_update_to_api "failed" $exit_code post_update_to_api "failed" $exit_code
@ -556,88 +348,39 @@ enhanced_error_handler() {
trap 'enhanced_error_handler' ERR trap 'enhanced_error_handler' ERR
# Advanced operations with API reporting # POST → create record
post_to_api post_to_api
# ... operations ... # ... operations ...
post_update_to_api "success" 0
# PATCH → finalize
post_update_to_api "done" 0
``` ```
## Error Handling Integration ## Error Handling Integration
### Automatic Error Reporting ### Automatic Error Reporting
- **Error Descriptions**: Provides human-readable error messages - **Error Descriptions**: `explain_exit_code()` provides human-readable messages for all recognized exit codes
- **API Integration**: Reports errors to community-scripts.org API - **PocketBase Integration**: Errors are recorded via PATCH with `status`, `error`, and `exit_code` fields
- **Error Tracking**: Tracks error patterns for project improvement - **Error Tracking**: Anonymous telemetry helps track common failure patterns
- **Diagnostic Data**: Contributes to anonymous usage analytics - **Diagnostic Data**: Contributes to project-wide analytics without PII
### Manual Error Reporting
- **Custom Error Codes**: Use appropriate error codes for different scenarios
- **Error Context**: Provide context information for errors
- **Status Updates**: Report both success and failure cases
- **Error Analysis**: Analyze error patterns and trends
### API Communication Errors ### API Communication Errors
- **Network Failures**: Handle API communication failures gracefully - **Network Failures**: All API calls use `|| true` — failures are swallowed silently
- **Missing Prerequisites**: Check prerequisites before API calls - **Missing Prerequisites**: Functions return early if curl, DIAGNOSTICS, or UUID are missing
- **Duplicate Prevention**: Prevent duplicate status updates - **Duplicate Prevention**: `POST_UPDATE_DONE` flag ensures only one PATCH per session
- **Error Recovery**: Handle API errors without blocking installation - **Record Lookup Fallback**: If `PB_RECORD_ID` is unset, a GET filter query resolves the record
## Performance Considerations ## Performance Considerations
### API Communication Overhead ### API Communication Overhead
- **Minimal Impact**: API calls add minimal overhead - **Minimal Impact**: Only 2 HTTP calls per installation (1 POST + 1 PATCH)
- **Asynchronous**: API calls don't block installation process - **Non-blocking**: API failures never block the installation process
- **Error Handling**: API failures don't affect installation - **Fire-and-forget**: curl stderr is suppressed (`2>/dev/null`)
- **Optional**: API reporting is optional and can be disabled - **Optional**: Telemetry is entirely opt-in via `DIAGNOSTICS` setting
### Memory Usage ### Security Considerations
- **Minimal Footprint**: API functions use minimal memory - **Anonymous**: No personal data is transmitted — only system specs and app names
- **Variable Reuse**: Global variables reused across functions - **No Auth Required**: PocketBase collection rules allow anonymous create/update
- **No Memory Leaks**: Proper cleanup prevents memory leaks - **User Control**: Users can disable telemetry by setting `DIAGNOSTICS=no`
- **Efficient Processing**: Efficient JSON payload creation - **HTTP**: API uses HTTP (not HTTPS) for compatibility with minimal containers
### Execution Speed
- **Fast API Calls**: Quick API communication
- **Efficient Error Processing**: Fast error code processing
- **Minimal Delay**: Minimal delay in API operations
- **Non-blocking**: API calls don't block installation
## Security Considerations
### Data Privacy
- **Anonymous Reporting**: Only anonymous data is sent
- **No Sensitive Data**: No sensitive information is transmitted
- **User Control**: Users can disable diagnostic reporting
- **Data Minimization**: Only necessary data is sent
### API Security
- **HTTPS**: API communication uses secure protocols
- **Data Validation**: API data is validated before sending
- **Error Handling**: API errors are handled securely
- **No Credentials**: No authentication credentials are sent
### Network Security
- **Secure Communication**: Uses secure HTTP protocols
- **Error Handling**: Network errors are handled gracefully
- **No Data Leakage**: No sensitive data is leaked
- **Secure Endpoints**: Uses trusted API endpoints
## Future Integration Considerations
### Extensibility
- **New API Endpoints**: Easy to add new API endpoints
- **Additional Data**: Easy to add new data fields
- **Error Codes**: Easy to add new error code descriptions
- **API Versions**: Easy to support new API versions
### Compatibility
- **API Versioning**: Compatible with different API versions
- **Data Format**: Compatible with different data formats
- **Error Codes**: Compatible with different error code systems
- **Network Protocols**: Compatible with different network protocols
### Performance
- **Optimization**: API communication can be optimized
- **Caching**: API responses can be cached
- **Batch Operations**: Multiple operations can be batched
- **Async Processing**: API calls can be made asynchronous

View File

@ -1,794 +0,0 @@
# api.func Usage Examples
## Overview
This document provides practical usage examples for `api.func` functions, covering common scenarios, integration patterns, and best practices.
## Basic API Setup
### Standard API Initialization
```bash
#!/usr/bin/env bash
# Standard API setup for LXC containers
source api.func
# Set up diagnostic reporting
export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)"
# Set container parameters
export CT_TYPE=1
export DISK_SIZE=8
export CORE_COUNT=2
export RAM_SIZE=2048
export var_os="debian"
export var_version="12"
export NSAPP="plex"
export METHOD="install"
# Report installation start
post_to_api
# Your installation code here
# ... installation logic ...
# Report completion
if [[ $? -eq 0 ]]; then
post_update_to_api "success" 0
else
post_update_to_api "failed" $?
fi
```
### VM API Setup
```bash
#!/usr/bin/env bash
# API setup for VMs
source api.func
# Create diagnostics file for VM
mkdir -p /usr/local/community-scripts
echo "DIAGNOSTICS=yes" > /usr/local/community-scripts/diagnostics
# Set up VM parameters
export RANDOM_UUID="$(uuidgen)"
export DISK_SIZE="20G"
export CORE_COUNT=4
export RAM_SIZE=4096
export var_os="ubuntu"
export var_version="22.04"
export NSAPP="nextcloud"
export METHOD="install"
# Report VM installation start
post_to_api_vm
# Your VM installation code here
# ... VM creation logic ...
# Report completion
post_update_to_api "success" 0
```
## Error Description Examples
### Basic Error Explanation
```bash
#!/usr/bin/env bash
source api.func
# Explain common error codes
echo "Error 0: '$(get_error_description 0)'"
echo "Error 1: $(get_error_description 1)"
echo "Error 127: $(get_error_description 127)"
echo "Error 200: $(get_error_description 200)"
echo "Error 255: $(get_error_description 255)"
```
### Error Code Testing
```bash
#!/usr/bin/env bash
source api.func
# Test all error codes
test_error_codes() {
local codes=(0 1 2 127 128 130 137 139 143 200 203 205 255)
for code in "${codes[@]}"; do
echo "Code $code: $(get_error_description $code)"
done
}
test_error_codes
```
### Error Handling with Descriptions
```bash
#!/usr/bin/env bash
source api.func
# Function with error handling
run_command_with_error_handling() {
local command="$1"
local description="$2"
echo "Running: $description"
if $command; then
echo "Success: $description"
return 0
else
local exit_code=$?
local error_msg=$(get_error_description $exit_code)
echo "Error $exit_code: $error_msg"
return $exit_code
fi
}
# Usage
run_command_with_error_handling "apt-get update" "Package list update"
run_command_with_error_handling "nonexistent_command" "Test command"
```
## API Communication Examples
### LXC Installation Reporting
```bash
#!/usr/bin/env bash
source api.func
# Complete LXC installation with API reporting
install_lxc_with_reporting() {
local app="$1"
local ctid="$2"
# Set up API reporting
export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)"
export CT_TYPE=1
export DISK_SIZE=10
export CORE_COUNT=2
export RAM_SIZE=2048
export var_os="debian"
export var_version="12"
export NSAPP="$app"
export METHOD="install"
# Report installation start
post_to_api
# Installation process
echo "Installing $app container (ID: $ctid)..."
# Simulate installation
sleep 2
# Check if installation succeeded
if [[ $? -eq 0 ]]; then
echo "Installation completed successfully"
post_update_to_api "success" 0
return 0
else
echo "Installation failed"
post_update_to_api "failed" $?
return 1
fi
}
# Install multiple containers
install_lxc_with_reporting "plex" "100"
install_lxc_with_reporting "nextcloud" "101"
install_lxc_with_reporting "nginx" "102"
```
### VM Installation Reporting
```bash
#!/usr/bin/env bash
source api.func
# Complete VM installation with API reporting
install_vm_with_reporting() {
local app="$1"
local vmid="$2"
# Create diagnostics file
mkdir -p /usr/local/community-scripts
echo "DIAGNOSTICS=yes" > /usr/local/community-scripts/diagnostics
# Set up API reporting
export RANDOM_UUID="$(uuidgen)"
export DISK_SIZE="20G"
export CORE_COUNT=4
export RAM_SIZE=4096
export var_os="ubuntu"
export var_version="22.04"
export NSAPP="$app"
export METHOD="install"
# Report VM installation start
post_to_api_vm
# VM installation process
echo "Installing $app VM (ID: $vmid)..."
# Simulate VM creation
sleep 3
# Check if VM creation succeeded
if [[ $? -eq 0 ]]; then
echo "VM installation completed successfully"
post_update_to_api "success" 0
return 0
else
echo "VM installation failed"
post_update_to_api "failed" $?
return 1
fi
}
# Install multiple VMs
install_vm_with_reporting "nextcloud" "200"
install_vm_with_reporting "wordpress" "201"
```
## Status Update Examples
### Success Reporting
```bash
#!/usr/bin/env bash
source api.func
# Report successful installation
report_success() {
local operation="$1"
export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)"
echo "Reporting successful $operation"
post_update_to_api "success" 0
}
# Usage
report_success "container installation"
report_success "package installation"
report_success "service configuration"
```
### Failure Reporting
```bash
#!/usr/bin/env bash
source api.func
# Report failed installation
report_failure() {
local operation="$1"
local exit_code="$2"
export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)"
local error_msg=$(get_error_description $exit_code)
echo "Reporting failed $operation: $error_msg"
post_update_to_api "failed" $exit_code
}
# Usage
report_failure "container creation" 200
report_failure "package installation" 127
report_failure "service start" 1
```
### Conditional Status Reporting
```bash
#!/usr/bin/env bash
source api.func
# Conditional status reporting
report_installation_status() {
local operation="$1"
local exit_code="$2"
export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)"
if [[ $exit_code -eq 0 ]]; then
echo "Reporting successful $operation"
post_update_to_api "success" 0
else
local error_msg=$(get_error_description $exit_code)
echo "Reporting failed $operation: $error_msg"
post_update_to_api "failed" $exit_code
fi
}
# Usage
report_installation_status "container creation" 0
report_installation_status "package installation" 127
```
## Advanced Usage Examples
### Batch Installation with API Reporting
```bash
#!/usr/bin/env bash
source api.func
# Batch installation with comprehensive API reporting
batch_install_with_reporting() {
local apps=("plex" "nextcloud" "nginx" "mysql")
local ctids=(100 101 102 103)
# Set up API reporting
export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)"
export CT_TYPE=1
export DISK_SIZE=8
export CORE_COUNT=2
export RAM_SIZE=2048
export var_os="debian"
export var_version="12"
export METHOD="install"
local success_count=0
local failure_count=0
for i in "${!apps[@]}"; do
local app="${apps[$i]}"
local ctid="${ctids[$i]}"
echo "Installing $app (ID: $ctid)..."
# Set app-specific parameters
export NSAPP="$app"
# Report installation start
post_to_api
# Simulate installation
if install_app "$app" "$ctid"; then
echo "$app installed successfully"
post_update_to_api "success" 0
((success_count++))
else
echo "$app installation failed"
post_update_to_api "failed" $?
((failure_count++))
fi
echo "---"
done
echo "Batch installation completed: $success_count successful, $failure_count failed"
}
# Mock installation function
install_app() {
local app="$1"
local ctid="$2"
# Simulate installation
sleep 1
# Simulate occasional failures
if [[ $((RANDOM % 10)) -eq 0 ]]; then
return 1
fi
return 0
}
batch_install_with_reporting
```
### Error Analysis and Reporting
```bash
#!/usr/bin/env bash
source api.func
# Analyze and report errors
analyze_and_report_errors() {
local log_file="$1"
export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)"
if [[ ! -f "$log_file" ]]; then
echo "Log file not found: $log_file"
return 1
fi
# Extract error codes from log
local error_codes=$(grep -o 'exit code [0-9]\+' "$log_file" | grep -o '[0-9]\+' | sort -u)
if [[ -z "$error_codes" ]]; then
echo "No errors found in log"
post_update_to_api "success" 0
return 0
fi
echo "Found error codes: $error_codes"
# Report each unique error
for code in $error_codes; do
local error_msg=$(get_error_description $code)
echo "Error $code: $error_msg"
post_update_to_api "failed" $code
done
}
# Usage
analyze_and_report_errors "/var/log/installation.log"
```
### API Health Check
```bash
#!/usr/bin/env bash
source api.func
# Check API connectivity and functionality
check_api_health() {
echo "Checking API health..."
# Test prerequisites
if ! command -v curl >/dev/null 2>&1; then
echo "ERROR: curl not available"
return 1
fi
# Test error description function
local test_error=$(get_error_description 127)
if [[ -z "$test_error" ]]; then
echo "ERROR: Error description function not working"
return 1
fi
echo "Error description test: $test_error"
# Test API connectivity (without sending data)
local api_url="http://api.community-scripts.org/dev/upload"
if curl -s --head "$api_url" >/dev/null 2>&1; then
echo "API endpoint is reachable"
else
echo "WARNING: API endpoint not reachable"
fi
echo "API health check completed"
}
check_api_health
```
## Integration Examples
### With build.func
```bash
#!/usr/bin/env bash
# Integration with build.func
source core.func
source api.func
source build.func
# Set up API reporting
export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)"
# Container creation with API reporting
create_container_with_reporting() {
local app="$1"
local ctid="$2"
# Set container parameters
export APP="$app"
export CTID="$ctid"
export var_hostname="${app}-server"
export var_os="debian"
export var_version="12"
export var_cpu="2"
export var_ram="2048"
export var_disk="10"
export var_net="vmbr0"
export var_gateway="192.168.1.1"
export var_ip="192.168.1.$ctid"
export var_template_storage="local"
export var_container_storage="local"
# Report installation start
post_to_api
# Create container using build.func
if source build.func; then
echo "Container $app created successfully"
post_update_to_api "success" 0
return 0
else
echo "Container $app creation failed"
post_update_to_api "failed" $?
return 1
fi
}
# Create containers
create_container_with_reporting "plex" "100"
create_container_with_reporting "nextcloud" "101"
```
### With vm-core.func
```bash
#!/usr/bin/env bash
# Integration with vm-core.func
source core.func
source api.func
source vm-core.func
# Set up VM API reporting
mkdir -p /usr/local/community-scripts
echo "DIAGNOSTICS=yes" > /usr/local/community-scripts/diagnostics
export RANDOM_UUID="$(uuidgen)"
# VM creation with API reporting
create_vm_with_reporting() {
local app="$1"
local vmid="$2"
# Set VM parameters
export APP="$app"
export VMID="$vmid"
export var_hostname="${app}-vm"
export var_os="ubuntu"
export var_version="22.04"
export var_cpu="4"
export var_ram="4096"
export var_disk="20"
# Report VM installation start
post_to_api_vm
# Create VM using vm-core.func
if source vm-core.func; then
echo "VM $app created successfully"
post_update_to_api "success" 0
return 0
else
echo "VM $app creation failed"
post_update_to_api "failed" $?
return 1
fi
}
# Create VMs
create_vm_with_reporting "nextcloud" "200"
create_vm_with_reporting "wordpress" "201"
```
### With error_handler.func
```bash
#!/usr/bin/env bash
# Integration with error_handler.func
source core.func
source error_handler.func
source api.func
# Enhanced error handling with API reporting
enhanced_error_handler() {
local exit_code=${1:-$?}
local command=${2:-${BASH_COMMAND:-unknown}}
# Get error description from api.func
local error_msg=$(get_error_description $exit_code)
# Display error information
echo "Error $exit_code: $error_msg"
echo "Command: $command"
# Report error to API
export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)"
post_update_to_api "failed" $exit_code
# Use standard error handler
error_handler $exit_code $command
}
# Set up enhanced error handling
trap 'enhanced_error_handler' ERR
# Test enhanced error handling
nonexistent_command
```
## Best Practices Examples
### Comprehensive API Integration
```bash
#!/usr/bin/env bash
# Comprehensive API integration example
source core.func
source api.func
# Set up comprehensive API reporting
setup_api_reporting() {
# Enable diagnostics
export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)"
# Set common parameters
export CT_TYPE=1
export DISK_SIZE=8
export CORE_COUNT=2
export RAM_SIZE=2048
export var_os="debian"
export var_version="12"
export METHOD="install"
echo "API reporting configured"
}
# Installation with comprehensive reporting
install_with_comprehensive_reporting() {
local app="$1"
local ctid="$2"
# Set up API reporting
setup_api_reporting
export NSAPP="$app"
# Report installation start
post_to_api
# Installation process
echo "Installing $app..."
# Simulate installation steps
local steps=("Downloading" "Installing" "Configuring" "Starting")
for step in "${steps[@]}"; do
echo "$step $app..."
sleep 1
done
# Check installation result
if [[ $? -eq 0 ]]; then
echo "$app installation completed successfully"
post_update_to_api "success" 0
return 0
else
echo "$app installation failed"
post_update_to_api "failed" $?
return 1
fi
}
# Install multiple applications
apps=("plex" "nextcloud" "nginx" "mysql")
ctids=(100 101 102 103)
for i in "${!apps[@]}"; do
install_with_comprehensive_reporting "${apps[$i]}" "${ctids[$i]}"
echo "---"
done
```
### Error Recovery with API Reporting
```bash
#!/usr/bin/env bash
source api.func
# Error recovery with API reporting
retry_with_api_reporting() {
local operation="$1"
local max_attempts=3
local attempt=1
export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)"
while [[ $attempt -le $max_attempts ]]; do
echo "Attempt $attempt of $max_attempts: $operation"
if $operation; then
echo "Operation succeeded on attempt $attempt"
post_update_to_api "success" 0
return 0
else
local exit_code=$?
local error_msg=$(get_error_description $exit_code)
echo "Attempt $attempt failed: $error_msg"
post_update_to_api "failed" $exit_code
((attempt++))
if [[ $attempt -le $max_attempts ]]; then
echo "Retrying in 5 seconds..."
sleep 5
fi
fi
done
echo "Operation failed after $max_attempts attempts"
return 1
}
# Usage
retry_with_api_reporting "apt-get update"
retry_with_api_reporting "apt-get install -y package"
```
### API Reporting with Logging
```bash
#!/usr/bin/env bash
source api.func
# API reporting with detailed logging
install_with_logging_and_api() {
local app="$1"
local log_file="/var/log/${app}_installation.log"
# Set up API reporting
export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)"
export NSAPP="$app"
# Start logging
exec > >(tee -a "$log_file")
exec 2>&1
echo "Starting $app installation at $(date)"
# Report installation start
post_to_api
# Installation process
echo "Installing $app..."
# Simulate installation
if install_app "$app"; then
echo "$app installation completed successfully at $(date)"
post_update_to_api "success" 0
return 0
else
local exit_code=$?
local error_msg=$(get_error_description $exit_code)
echo "$app installation failed at $(date): $error_msg"
post_update_to_api "failed" $exit_code
return $exit_code
fi
}
# Mock installation function
install_app() {
local app="$1"
echo "Installing $app..."
sleep 2
return 0
}
# Install with logging and API reporting
install_with_logging_and_api "plex"
```

View File

@ -2,22 +2,27 @@
## Overview ## Overview
The `api.func` file provides Proxmox API integration and diagnostic reporting functionality for the Community Scripts project. It handles API communication, error reporting, and status updates to the community-scripts.org API. The `api.func` file provides PocketBase API integration and diagnostic reporting for the Community Scripts project. It handles telemetry communication, error reporting, and status updates to the PocketBase backend at `db.community-scripts.org`.
## Purpose and Use Cases ## Purpose and Use Cases
- **API Communication**: Send installation and status data to community-scripts.org API - **API Communication**: Send installation and status data to PocketBase
- **Diagnostic Reporting**: Report installation progress and errors for analytics - **Diagnostic Reporting**: Report installation progress and errors for analytics
- **Error Description**: Provide detailed error code explanations - **Error Description**: Provide detailed error code explanations (canonical source of truth)
- **Status Updates**: Track installation success/failure status - **Status Updates**: Track installation success/failure status
- **Analytics**: Contribute anonymous usage data for project improvement - **Analytics**: Contribute anonymous usage data for project improvement
## Quick Reference ## Quick Reference
### Key Function Groups ### Key Function Groups
- **Error Handling**: `get_error_description()` - Convert exit codes to human-readable messages - **Error Handling**: `explain_exit_code()` - Convert exit codes to human-readable messages
- **API Communication**: `post_to_api()`, `post_to_api_vm()` - Send installation data - **API Communication**: `post_to_api()`, `post_to_api_vm()` - Send installation data to PocketBase
- **Status Updates**: `post_update_to_api()` - Report installation completion status - **Status Updates**: `post_update_to_api()` - Report installation completion status via PATCH
### PocketBase Configuration
- **URL**: `http://db.community-scripts.org`
- **Collection**: `_dev_telemetry_data`
- **API Endpoint**: `/api/collections/_dev_telemetry_data/records`
### Dependencies ### Dependencies
- **External**: `curl` command for HTTP requests - **External**: `curl` command for HTTP requests
@ -26,7 +31,7 @@ The `api.func` file provides Proxmox API integration and diagnostic reporting fu
### Integration Points ### Integration Points
- Used by: All installation scripts for diagnostic reporting - Used by: All installation scripts for diagnostic reporting
- Uses: Environment variables from build.func and other scripts - Uses: Environment variables from build.func and other scripts
- Provides: API communication and error reporting services - Provides: API communication, error reporting, and exit code descriptions
## Documentation Files ## Documentation Files
@ -44,17 +49,18 @@ How api.func integrates with other components and provides API services.
## Key Features ## Key Features
### Error Code Descriptions ### Exit Code Descriptions
- **Comprehensive Coverage**: 50+ error codes with detailed explanations - **Canonical source**: Single authoritative `explain_exit_code()` for the entire project
- **LXC-Specific Errors**: Container creation and management errors - **Non-overlapping ranges**: Clean separation between error categories
- **System Errors**: General system and network errors - **Comprehensive Coverage**: 60+ error codes with detailed explanations
- **System Errors**: General system, curl, and network errors
- **Signal Errors**: Process termination and signal errors - **Signal Errors**: Process termination and signal errors
### API Communication ### PocketBase Integration
- **LXC Reporting**: Send LXC container installation data - **Record Creation**: POST to create telemetry records with status `installing`
- **VM Reporting**: Send VM installation data - **Record Updates**: PATCH to update with final status, exit code, and error
- **Status Updates**: Report installation success/failure - **ID Tracking**: Stores `PB_RECORD_ID` for efficient updates
- **Diagnostic Data**: Anonymous usage analytics - **Fallback Lookup**: Searches by `random_id` filter if record ID is lost
### Diagnostic Integration ### Diagnostic Integration
- **Optional Reporting**: Only sends data when diagnostics enabled - **Optional Reporting**: Only sends data when diagnostics enabled
@ -67,15 +73,13 @@ How api.func integrates with other components and provides API services.
### Basic API Setup ### Basic API Setup
```bash ```bash
#!/usr/bin/env bash #!/usr/bin/env bash
# Basic API setup
source api.func source api.func
# Set up diagnostic reporting # Set up diagnostic reporting
export DIAGNOSTICS="yes" export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)" export RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
# Report installation start # Report installation start (creates PocketBase record)
post_to_api post_to_api
``` ```
@ -85,9 +89,9 @@ post_to_api
source api.func source api.func
# Get error description # Get error description
error_msg=$(get_error_description 127) error_msg=$(explain_exit_code 137)
echo "Error 127: $error_msg" echo "Error 137: $error_msg"
# Output: Error 127: Command not found: Incorrect path or missing dependency. # Output: Error 137: Killed (SIGKILL / Out of memory?)
``` ```
### Status Updates ### Status Updates
@ -96,9 +100,9 @@ echo "Error 127: $error_msg"
source api.func source api.func
# Report successful installation # Report successful installation
post_update_to_api "success" 0 post_update_to_api "done" 0
# Report failed installation # Report failed installation with exit code
post_update_to_api "failed" 127 post_update_to_api "failed" 127
``` ```
@ -106,7 +110,7 @@ post_update_to_api "failed" 127
### Required Variables ### Required Variables
- `DIAGNOSTICS`: Enable/disable diagnostic reporting ("yes"/"no") - `DIAGNOSTICS`: Enable/disable diagnostic reporting ("yes"/"no")
- `RANDOM_UUID`: Unique identifier for tracking - `RANDOM_UUID`: Unique identifier for session tracking
### Optional Variables ### Optional Variables
- `CT_TYPE`: Container type (1 for LXC, 2 for VM) - `CT_TYPE`: Container type (1 for LXC, 2 for VM)
@ -115,33 +119,31 @@ post_update_to_api "failed" 127
- `RAM_SIZE`: RAM size in MB - `RAM_SIZE`: RAM size in MB
- `var_os`: Operating system type - `var_os`: Operating system type
- `var_version`: OS version - `var_version`: OS version
- `DISABLEIP6`: IPv6 disable setting - `NSAPP`: Application name
- `NSAPP`: Namespace application name
- `METHOD`: Installation method - `METHOD`: Installation method
### Internal Variables ### Internal Variables
- `POST_UPDATE_DONE`: Prevents duplicate status updates - `POST_UPDATE_DONE`: Prevents duplicate status updates
- `API_URL`: Community scripts API endpoint - `PB_URL`: PocketBase base URL
- `JSON_PAYLOAD`: API request payload - `PB_API_URL`: Full API endpoint URL
- `RESPONSE`: API response - `PB_RECORD_ID`: Stored PocketBase record ID for updates
## Error Code Categories ## Error Code Categories (Non-Overlapping Ranges)
### General System Errors | Range | Category |
- **0-9**: Basic system errors |-------|----------|
- **18, 22, 28, 35**: Network and I/O errors | 1-2 | Generic shell errors |
- **56, 60**: TLS/SSL errors | 6-35 | curl/wget network errors |
- **125-128**: Command execution errors | 100-102 | APT/DPKG package errors |
- **129-143**: Signal errors | 124-143 | Command execution & signal errors |
- **152**: Resource limit errors | 150-154 | Systemd/service errors |
- **255**: Unknown critical errors | 160-162 | Python/pip/uv errors |
| 170-173 | PostgreSQL errors |
### LXC-Specific Errors | 180-183 | MySQL/MariaDB errors |
- **100-101**: LXC installation errors | 190-193 | MongoDB errors |
- **200-209**: LXC creation and management errors | 200-231 | Proxmox custom codes |
| 243-249 | Node.js/npm errors |
### Docker Errors | 255 | DPKG fatal error |
- **125**: Docker container start errors
## Best Practices ## Best Practices
@ -152,48 +154,56 @@ post_update_to_api "failed" 127
4. Report both success and failure cases 4. Report both success and failure cases
### Error Handling ### Error Handling
1. Use appropriate error codes 1. Use the correct non-overlapping exit code ranges
2. Provide meaningful error descriptions 2. Use `explain_exit_code()` from api.func (canonical source)
3. Handle API communication failures gracefully 3. Handle API communication failures gracefully
4. Don't block installation on API failures 4. Don't block installation on API failures
### API Usage ### API Usage
1. Check for curl availability 1. Check for curl availability before API calls
2. Handle network failures gracefully 2. Handle network failures gracefully (all calls use `|| true`)
3. Use appropriate HTTP methods 3. Store and reuse PB_RECORD_ID for updates
4. Include all required data 4. Use proper PocketBase REST methods (POST for create, PATCH for update)
## Troubleshooting ## Troubleshooting
### Common Issues ### Common Issues
1. **API Communication Fails**: Check network connectivity and curl availability 1. **API Communication Fails**: Check network connectivity and curl availability
2. **Diagnostics Not Working**: Verify DIAGNOSTICS setting and RANDOM_UUID 2. **Diagnostics Not Working**: Verify `DIAGNOSTICS=yes` in `/usr/local/community-scripts/diagnostics`
3. **Missing Error Descriptions**: Check error code coverage 3. **Status Update Fails**: Check that `PB_RECORD_ID` was captured or `random_id` filter works
4. **Duplicate Updates**: POST_UPDATE_DONE prevents duplicates 4. **Duplicate Updates**: `POST_UPDATE_DONE` flag prevents duplicates
### Debug Mode ### Debug Mode
Enable diagnostic reporting for debugging: Enable diagnostic reporting for debugging:
```bash ```bash
export DIAGNOSTICS="yes" export DIAGNOSTICS="yes"
export RANDOM_UUID="$(uuidgen)" export RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
``` ```
### API Testing ### API Testing
Test API communication: Test PocketBase connectivity:
```bash
curl -s http://db.community-scripts.org/api/health
```
Test record creation:
```bash ```bash
source api.func source api.func
export DIAGNOSTICS="yes" export DIAGNOSTICS="yes"
export RANDOM_UUID="test-$(date +%s)" export RANDOM_UUID="test-$(date +%s)"
export NSAPP="test"
export CT_TYPE=1
post_to_api post_to_api
echo "Record ID: $PB_RECORD_ID"
``` ```
## Related Documentation ## Related Documentation
- [core.func](../core.func/) - Core utilities and error handling - [core.func](../core.func/) - Core utilities
- [error_handler.func](../error_handler.func/) - Error handling utilities - [error_handler.func](../error_handler.func/) - Error handling (fallback `explain_exit_code`)
- [build.func](../build.func/) - Container creation with API integration - [build.func](../build.func/) - Container creation with API integration
- [tools.func](../tools.func/) - Extended utilities with API integration - [tools.func](../tools.func/) - Extended utilities
--- ---
*This documentation covers the api.func file which provides API communication and diagnostic reporting for all Proxmox Community Scripts.* *This documentation covers the api.func file which provides PocketBase communication and diagnostic reporting for all Proxmox Community Scripts.*

View File

@ -3,11 +3,11 @@
# License: MIT | https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/LICENSE # License: MIT | https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/LICENSE
# ============================================================================== # ==============================================================================
# API.FUNC - TELEMETRY & DIAGNOSTICS API # API.FUNC - TELEMETRY & DIAGNOSTICS API (PocketBase)
# ============================================================================== # ==============================================================================
# #
# Provides functions for sending anonymous telemetry data to Community-Scripts # Provides functions for sending anonymous telemetry data to PocketBase
# API for analytics and diagnostics purposes. # backend at db.community-scripts.org for analytics and diagnostics.
# #
# Features: # Features:
# - Container/VM creation statistics # - Container/VM creation statistics
@ -18,6 +18,7 @@
# Usage: # Usage:
# source <(curl -fsSL .../api.func) # source <(curl -fsSL .../api.func)
# post_to_api # Report container creation # post_to_api # Report container creation
# post_to_api_vm # Report VM creation
# post_update_to_api # Report installation status # post_update_to_api # Report installation status
# #
# Privacy: # Privacy:
@ -27,6 +28,16 @@
# #
# ============================================================================== # ==============================================================================
# ==============================================================================
# PocketBase Configuration
# ==============================================================================
PB_URL="http://db.community-scripts.org"
PB_COLLECTION="_dev_telemetry_data"
PB_API_URL="${PB_URL}/api/collections/${PB_COLLECTION}/records"
# Store PocketBase record ID for update operations
PB_RECORD_ID=""
# ============================================================================== # ==============================================================================
# SECTION 1: ERROR CODE DESCRIPTIONS # SECTION 1: ERROR CODE DESCRIPTIONS
# ============================================================================== # ==============================================================================
@ -35,6 +46,8 @@
# explain_exit_code() # explain_exit_code()
# #
# - Maps numeric exit codes to human-readable error descriptions # - Maps numeric exit codes to human-readable error descriptions
# - Canonical source of truth for ALL exit code mappings
# - Used by both api.func (telemetry) and error_handler.func (error display)
# - Supports: # - Supports:
# * Generic/Shell errors (1, 2, 124, 126-130, 134, 137, 139, 141, 143) # * Generic/Shell errors (1, 2, 124, 126-130, 134, 137, 139, 141, 143)
# * curl/wget errors (6, 7, 22, 28, 35) # * curl/wget errors (6, 7, 22, 28, 35)
@ -47,7 +60,6 @@
# * Proxmox custom codes (200-231) # * Proxmox custom codes (200-231)
# * Node.js/npm errors (243, 245-249) # * Node.js/npm errors (243, 245-249)
# - Returns description string for given exit code # - Returns description string for given exit code
# - Shared function with error_handler.func for consistency
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
explain_exit_code() { explain_exit_code() {
local code="$1" local code="$1"
@ -160,7 +172,8 @@ explain_exit_code() {
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# post_to_api() # post_to_api()
# #
# - Sends LXC container creation statistics to Community-Scripts API # - Sends LXC container creation statistics to PocketBase
# - Creates a new record in the _dev_telemetry_data collection
# - Only executes if: # - Only executes if:
# * curl is available # * curl is available
# * DIAGNOSTICS=yes # * DIAGNOSTICS=yes
@ -168,62 +181,71 @@ explain_exit_code() {
# - Payload includes: # - Payload includes:
# * Container type, disk size, CPU cores, RAM # * Container type, disk size, CPU cores, RAM
# * OS type and version # * OS type and version
# * IPv6 disable status
# * Application name (NSAPP) # * Application name (NSAPP)
# * Installation method # * Installation method
# * PVE version # * PVE version
# * Status: "installing" # * Status: "installing"
# * Random UUID for session tracking # * Random UUID for session tracking
# - Stores PB_RECORD_ID for later updates
# - Anonymous telemetry (no personal data) # - Anonymous telemetry (no personal data)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
post_to_api() { post_to_api() {
if ! command -v curl &>/dev/null; then if ! command -v curl &>/dev/null; then
return return
fi fi
if [ "$DIAGNOSTICS" = "no" ]; then if [[ "${DIAGNOSTICS:-no}" == "no" ]]; then
return return
fi fi
if [ -z "$RANDOM_UUID" ]; then if [[ -z "${RANDOM_UUID:-}" ]]; then
return return
fi fi
local API_URL="http://api.community-scripts.org/dev/upload"
local pve_version="not found" local pve_version="not found"
pve_version=$(pveversion | awk -F'[/ ]' '{print $2}') if command -v pveversion &>/dev/null; then
pve_version=$(pveversion | awk -F'[/ ]' '{print $2}')
fi
local JSON_PAYLOAD
JSON_PAYLOAD=$( JSON_PAYLOAD=$(
cat <<EOF cat <<EOF
{ {
"ct_type": $CT_TYPE, "ct_type": ${CT_TYPE:-1},
"type":"lxc", "type": "lxc",
"disk_size": $DISK_SIZE, "disk_size": ${DISK_SIZE:-0},
"core_count": $CORE_COUNT, "core_count": ${CORE_COUNT:-0},
"ram_size": $RAM_SIZE, "ram_size": ${RAM_SIZE:-0},
"os_type": "$var_os", "os_type": "${var_os:-}",
"os_version": "$var_version", "os_version": "${var_version:-}",
"nsapp": "$NSAPP", "nsapp": "${NSAPP:-}",
"method": "$METHOD", "method": "${METHOD:-default}",
"pve_version": "$pve_version", "pve_version": "${pve_version}",
"status": "installing", "status": "installing",
"random_id": "$RANDOM_UUID" "random_id": "${RANDOM_UUID}"
} }
EOF EOF
) )
if [[ "$DIAGNOSTICS" == "yes" ]]; then
RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \
-H "Content-Type: application/json" \
-d "$JSON_PAYLOAD") || true
fi
local RESPONSE
RESPONSE=$(curl -s -w "\n%{http_code}" -L -X POST "${PB_API_URL}" \
-H "Content-Type: application/json" \
-d "$JSON_PAYLOAD" 2>/dev/null) || true
# Extract PocketBase record ID from response for later updates
local http_code body
http_code=$(echo "$RESPONSE" | tail -n1)
body=$(echo "$RESPONSE" | sed '$d')
if [[ "$http_code" == "200" ]] || [[ "$http_code" == "201" ]]; then
PB_RECORD_ID=$(echo "$body" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) || true
fi
} }
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# post_to_api_vm() # post_to_api_vm()
# #
# - Sends VM creation statistics to Community-Scripts API # - Sends VM creation statistics to PocketBase
# - Similar to post_to_api() but for virtual machines (not containers) # - Similar to post_to_api() but for virtual machines (not containers)
# - Reads DIAGNOSTICS from /usr/local/community-scripts/diagnostics file # - Reads DIAGNOSTICS from /usr/local/community-scripts/diagnostics file
# - Payload differences: # - Payload differences:
@ -233,66 +255,78 @@ EOF
# - Only executes if DIAGNOSTICS=yes and RANDOM_UUID is set # - Only executes if DIAGNOSTICS=yes and RANDOM_UUID is set
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
post_to_api_vm() { post_to_api_vm() {
if [[ ! -f /usr/local/community-scripts/diagnostics ]]; then if [[ ! -f /usr/local/community-scripts/diagnostics ]]; then
return return
fi fi
DIAGNOSTICS=$(grep -i "^DIAGNOSTICS=" /usr/local/community-scripts/diagnostics | awk -F'=' '{print $2}') DIAGNOSTICS=$(grep -i "^DIAGNOSTICS=" /usr/local/community-scripts/diagnostics | awk -F'=' '{print $2}')
if ! command -v curl &>/dev/null; then if ! command -v curl &>/dev/null; then
return return
fi fi
if [ "$DIAGNOSTICS" = "no" ]; then if [[ "${DIAGNOSTICS:-no}" == "no" ]]; then
return return
fi fi
if [ -z "$RANDOM_UUID" ]; then if [[ -z "${RANDOM_UUID:-}" ]]; then
return return
fi fi
local API_URL="http://api.community-scripts.org/dev/upload"
local pve_version="not found" local pve_version="not found"
pve_version=$(pveversion | awk -F'[/ ]' '{print $2}') if command -v pveversion &>/dev/null; then
pve_version=$(pveversion | awk -F'[/ ]' '{print $2}')
fi
DISK_SIZE_API=${DISK_SIZE%G} local DISK_SIZE_API="${DISK_SIZE%G}"
local JSON_PAYLOAD
JSON_PAYLOAD=$( JSON_PAYLOAD=$(
cat <<EOF cat <<EOF
{ {
"ct_type": 2, "ct_type": 2,
"type":"vm", "type": "vm",
"disk_size": $DISK_SIZE_API, "disk_size": ${DISK_SIZE_API:-0},
"core_count": $CORE_COUNT, "core_count": ${CORE_COUNT:-0},
"ram_size": $RAM_SIZE, "ram_size": ${RAM_SIZE:-0},
"os_type": "$var_os", "os_type": "${var_os:-}",
"os_version": "$var_version", "os_version": "${var_version:-}",
"nsapp": "$NSAPP", "nsapp": "${NSAPP:-}",
"method": "$METHOD", "method": "${METHOD:-default}",
"pve_version": "$pve_version", "pve_version": "${pve_version}",
"status": "installing", "status": "installing",
"random_id": "$RANDOM_UUID" "random_id": "${RANDOM_UUID}"
} }
EOF EOF
) )
if [[ "$DIAGNOSTICS" == "yes" ]]; then
RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \ local RESPONSE
-H "Content-Type: application/json" \ RESPONSE=$(curl -s -w "\n%{http_code}" -L -X POST "${PB_API_URL}" \
-d "$JSON_PAYLOAD") || true -H "Content-Type: application/json" \
-d "$JSON_PAYLOAD" 2>/dev/null) || true
# Extract PocketBase record ID from response for later updates
local http_code body
http_code=$(echo "$RESPONSE" | tail -n1)
body=$(echo "$RESPONSE" | sed '$d')
if [[ "$http_code" == "200" ]] || [[ "$http_code" == "201" ]]; then
PB_RECORD_ID=$(echo "$body" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) || true
fi fi
} }
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# post_update_to_api() # post_update_to_api()
# #
# - Reports installation completion status to API # - Reports installation completion status to PocketBase via PATCH
# - Prevents duplicate submissions via POST_UPDATE_DONE flag # - Prevents duplicate submissions via POST_UPDATE_DONE flag
# - Arguments: # - Arguments:
# * $1: status ("success" or "failed") # * $1: status ("done" or "failed")
# * $2: exit_code (default: 1 for failed, 0 for success) # * $2: exit_code (numeric, default: 1 for failed, 0 for done)
# - Uses PB_RECORD_ID if available, otherwise looks up by random_id
# - Payload includes: # - Payload includes:
# * Final status (success/failed) # * Final status (mapped: "done"→"sucess", "failed"→"failed")
# * Error description via get_error_description() # * Error description via explain_exit_code()
# * Random UUID for session correlation # * Numeric exit code
# - Only executes once per session # - Only executes once per session
# - Silently returns if: # - Silently returns if:
# * curl not available # * curl not available
@ -300,7 +334,6 @@ EOF
# * DIAGNOSTICS=no # * DIAGNOSTICS=no
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
post_update_to_api() { post_update_to_api() {
if ! command -v curl &>/dev/null; then if ! command -v curl &>/dev/null; then
return return
fi fi
@ -308,42 +341,79 @@ post_update_to_api() {
# Initialize flag if not set (prevents 'unbound variable' error with set -u) # Initialize flag if not set (prevents 'unbound variable' error with set -u)
POST_UPDATE_DONE=${POST_UPDATE_DONE:-false} POST_UPDATE_DONE=${POST_UPDATE_DONE:-false}
if [ "$POST_UPDATE_DONE" = true ]; then if [[ "$POST_UPDATE_DONE" == "true" ]]; then
return 0 return 0
fi fi
exit_code=${2:-1}
local API_URL="http://api.community-scripts.org/dev/upload/updatestatus" if [[ "${DIAGNOSTICS:-no}" == "no" ]]; then
return
fi
if [[ -z "${RANDOM_UUID:-}" ]]; then
return
fi
local status="${1:-failed}" local status="${1:-failed}"
if [[ "$status" == "failed" ]]; then local raw_exit_code="${2:-1}"
local exit_code="${2:-1}" local exit_code error pb_status
elif [[ "$status" == "success" ]]; then
local exit_code="${2:-0}" # Map status to PocketBase select values: installing, sucess, failed, unknown
case "$status" in
done | success | sucess)
pb_status="sucess"
exit_code=0
error=""
;;
failed) pb_status="failed" ;;
*) pb_status="unknown" ;;
esac
# For failed status, resolve exit code and error description
if [[ "$pb_status" == "failed" ]] || [[ "$pb_status" == "unknown" ]]; then
# If exit_code is numeric, use it; otherwise default to 1
if [[ "$raw_exit_code" =~ ^[0-9]+$ ]]; then
exit_code="$raw_exit_code"
else
exit_code=1
fi
error=$(explain_exit_code "$exit_code")
if [[ -z "$error" ]]; then
error="Unknown error"
fi
fi fi
if [[ -z "$exit_code" ]]; then # Resolve PocketBase record ID if not already known
exit_code=1 local record_id="${PB_RECORD_ID:-}"
fi
if [[ -z "$record_id" ]]; then
error=$(explain_exit_code "$exit_code") # Look up record by random_id filter
local lookup_url="${PB_API_URL}?filter=(random_id='${RANDOM_UUID}')&fields=id&perPage=1"
if [ -z "$error" ]; then local lookup_response
error="Unknown error" lookup_response=$(curl -s -L "${lookup_url}" 2>/dev/null) || true
record_id=$(echo "$lookup_response" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) || true
if [[ -z "$record_id" ]]; then
POST_UPDATE_DONE=true
return
fi
fi fi
local JSON_PAYLOAD
JSON_PAYLOAD=$( JSON_PAYLOAD=$(
cat <<EOF cat <<EOF
{ {
"status": "$status", "status": "${pb_status}",
"error": "$error", "error": "${error:-}",
"random_id": "$RANDOM_UUID" "exit_code": ${exit_code:-0}
} }
EOF EOF
) )
if [[ "$DIAGNOSTICS" == "yes" ]]; then
RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \ # PATCH to update the existing record
-H "Content-Type: application/json" \ curl -s -L -X PATCH "${PB_API_URL}/${record_id}" \
-d "$JSON_PAYLOAD") || true -H "Content-Type: application/json" \
fi -d "$JSON_PAYLOAD" &>/dev/null || true
POST_UPDATE_DONE=true POST_UPDATE_DONE=true
} }

View File

@ -5157,9 +5157,9 @@ EOF
# api_exit_script() # api_exit_script()
# #
# - Exit trap handler for reporting to API telemetry # - Exit trap handler for reporting to API telemetry
# - Captures exit code and reports to API using centralized error descriptions # - Captures exit code and reports to PocketBase using centralized error descriptions
# - Uses explain_exit_code() from error_handler.func for consistent error messages # - Uses explain_exit_code() from api.func for consistent error messages
# - Posts failure status with exit code to API (error description added automatically) # - Posts failure status with exit code to API (error description resolved automatically)
# - Only executes on non-zero exit codes # - Only executes on non-zero exit codes
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
api_exit_script() { api_exit_script() {
@ -5172,6 +5172,6 @@ api_exit_script() {
if command -v pveversion >/dev/null 2>&1; then if command -v pveversion >/dev/null 2>&1; then
trap 'api_exit_script' EXIT trap 'api_exit_script' EXIT
fi fi
trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR trap 'post_update_to_api "failed" "$?"' ERR
trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT trap 'post_update_to_api "failed" "130"' SIGINT
trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM trap 'post_update_to_api "failed" "143"' SIGTERM

View File

@ -27,100 +27,90 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# explain_exit_code() # explain_exit_code()
# #
# - Maps numeric exit codes to human-readable error descriptions # - Canonical version is defined in api.func (sourced before this file)
# - Supports: # - This section only provides a fallback if api.func was not loaded
# * Generic/Shell errors (1, 2, 126, 127, 128, 130, 137, 139, 143) # - See api.func SECTION 1 for the authoritative exit code mappings
# * Package manager errors (APT, DPKG: 100, 101, 255)
# * Node.js/npm errors (243-249, 254)
# * Python/pip/uv errors (210-212)
# * PostgreSQL errors (231-234)
# * MySQL/MariaDB errors (241-244)
# * MongoDB errors (251-254)
# * Proxmox custom codes (200-231)
# - Returns description string for given exit code
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
explain_exit_code() { if ! declare -f explain_exit_code &>/dev/null; then
local code="$1" explain_exit_code() {
case "$code" in local code="$1"
# --- Generic / Shell --- case "$code" in
1) echo "General error / Operation not permitted" ;; 1) echo "General error / Operation not permitted" ;;
2) echo "Misuse of shell builtins (e.g. syntax error)" ;; 2) echo "Misuse of shell builtins (e.g. syntax error)" ;;
126) echo "Command invoked cannot execute (permission problem?)" ;; 6) echo "curl: DNS resolution failed (could not resolve host)" ;;
127) echo "Command not found" ;; 7) echo "curl: Failed to connect (network unreachable / host down)" ;;
128) echo "Invalid argument to exit" ;; 22) echo "curl: HTTP error returned (404, 429, 500+)" ;;
130) echo "Terminated by Ctrl+C (SIGINT)" ;; 28) echo "curl: Operation timeout (network slow or server not responding)" ;;
137) echo "Killed (SIGKILL / Out of memory?)" ;; 35) echo "curl: SSL/TLS handshake failed (certificate error)" ;;
139) echo "Segmentation fault (core dumped)" ;; 100) echo "APT: Package manager error (broken packages / dependency problems)" ;;
143) echo "Terminated (SIGTERM)" ;; 101) echo "APT: Configuration error (bad sources.list, malformed config)" ;;
102) echo "APT: Lock held by another process (dpkg/apt still running)" ;;
# --- Package manager / APT / DPKG --- 124) echo "Command timed out (timeout command)" ;;
100) echo "APT: Package manager error (broken packages / dependency problems)" ;; 126) echo "Command invoked cannot execute (permission problem?)" ;;
101) echo "APT: Configuration error (bad sources.list, malformed config)" ;; 127) echo "Command not found" ;;
255) echo "DPKG: Fatal internal error" ;; 128) echo "Invalid argument to exit" ;;
130) echo "Terminated by Ctrl+C (SIGINT)" ;;
# --- Node.js / npm / pnpm / yarn --- 134) echo "Process aborted (SIGABRT - possibly Node.js heap overflow)" ;;
243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;; 137) echo "Killed (SIGKILL / Out of memory?)" ;;
245) echo "Node.js: Invalid command-line option" ;; 139) echo "Segmentation fault (core dumped)" ;;
246) echo "Node.js: Internal JavaScript Parse Error" ;; 141) echo "Broken pipe (SIGPIPE - output closed prematurely)" ;;
247) echo "Node.js: Fatal internal error" ;; 143) echo "Terminated (SIGTERM)" ;;
248) echo "Node.js: Invalid C++ addon / N-API failure" ;; 150) echo "Systemd: Service failed to start" ;;
249) echo "Node.js: Inspector error" ;; 151) echo "Systemd: Service unit not found" ;;
254) echo "npm/pnpm/yarn: Unknown fatal error" ;; 152) echo "Permission denied (EACCES)" ;;
153) echo "Build/compile failed (make/gcc/cmake)" ;;
# --- Python / pip / uv --- 154) echo "Node.js: Native addon build failed (node-gyp)" ;;
210) echo "Python: Virtualenv / uv environment missing or broken" ;; 160) echo "Python: Virtualenv / uv environment missing or broken" ;;
211) echo "Python: Dependency resolution failed" ;; 161) echo "Python: Dependency resolution failed" ;;
212) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;; 162) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;;
170) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;;
# --- PostgreSQL --- 171) echo "PostgreSQL: Authentication failed (bad user/password)" ;;
231) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;; 172) echo "PostgreSQL: Database does not exist" ;;
232) echo "PostgreSQL: Authentication failed (bad user/password)" ;; 173) echo "PostgreSQL: Fatal error in query / syntax" ;;
233) echo "PostgreSQL: Database does not exist" ;; 180) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;;
234) echo "PostgreSQL: Fatal error in query / syntax" ;; 181) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;;
182) echo "MySQL/MariaDB: Database does not exist" ;;
# --- MySQL / MariaDB --- 183) echo "MySQL/MariaDB: Fatal error in query / syntax" ;;
241) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;; 190) echo "MongoDB: Connection failed (server not running)" ;;
242) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;; 191) echo "MongoDB: Authentication failed (bad user/password)" ;;
243) echo "MySQL/MariaDB: Database does not exist" ;; 192) echo "MongoDB: Database not found" ;;
244) echo "MySQL/MariaDB: Fatal error in query / syntax" ;; 193) echo "MongoDB: Fatal query error" ;;
200) echo "Proxmox: Failed to create lock file" ;;
# --- MongoDB --- 203) echo "Proxmox: Missing CTID variable" ;;
251) echo "MongoDB: Connection failed (server not running)" ;; 204) echo "Proxmox: Missing PCT_OSTYPE variable" ;;
252) echo "MongoDB: Authentication failed (bad user/password)" ;; 205) echo "Proxmox: Invalid CTID (<100)" ;;
253) echo "MongoDB: Database not found" ;; 206) echo "Proxmox: CTID already in use" ;;
254) echo "MongoDB: Fatal query error" ;; 207) echo "Proxmox: Password contains unescaped special characters" ;;
208) echo "Proxmox: Invalid configuration (DNS/MAC/Network format)" ;;
# --- Proxmox Custom Codes --- 209) echo "Proxmox: Container creation failed" ;;
200) echo "Proxmox: Failed to create lock file" ;; 210) echo "Proxmox: Cluster not quorate" ;;
203) echo "Proxmox: Missing CTID variable" ;; 211) echo "Proxmox: Timeout waiting for template lock" ;;
204) echo "Proxmox: Missing PCT_OSTYPE variable" ;; 212) echo "Proxmox: Storage type 'iscsidirect' does not support containers (VMs only)" ;;
205) echo "Proxmox: Invalid CTID (<100)" ;; 213) echo "Proxmox: Storage type does not support 'rootdir' content" ;;
206) echo "Proxmox: CTID already in use" ;; 214) echo "Proxmox: Not enough storage space" ;;
207) echo "Proxmox: Password contains unescaped special characters" ;; 215) echo "Proxmox: Container created but not listed (ghost state)" ;;
208) echo "Proxmox: Invalid configuration (DNS/MAC/Network format)" ;; 216) echo "Proxmox: RootFS entry missing in config" ;;
209) echo "Proxmox: Container creation failed" ;; 217) echo "Proxmox: Storage not accessible" ;;
210) echo "Proxmox: Cluster not quorate" ;; 218) echo "Proxmox: Template file corrupted or incomplete" ;;
211) echo "Proxmox: Timeout waiting for template lock" ;; 219) echo "Proxmox: CephFS does not support containers - use RBD" ;;
212) echo "Proxmox: Storage type 'iscsidirect' does not support containers (VMs only)" ;; 220) echo "Proxmox: Unable to resolve template path" ;;
213) echo "Proxmox: Storage type does not support 'rootdir' content" ;; 221) echo "Proxmox: Template file not readable" ;;
214) echo "Proxmox: Not enough storage space" ;; 222) echo "Proxmox: Template download failed" ;;
215) echo "Proxmox: Container created but not listed (ghost state)" ;; 223) echo "Proxmox: Template not available after download" ;;
216) echo "Proxmox: RootFS entry missing in config" ;; 224) echo "Proxmox: PBS storage is for backups only" ;;
217) echo "Proxmox: Storage not accessible" ;; 225) echo "Proxmox: No template available for OS/Version" ;;
219) echo "Proxmox: CephFS does not support containers - use RBD" ;; 231) echo "Proxmox: LXC stack upgrade failed" ;;
224) echo "Proxmox: PBS storage is for backups only" ;; 243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;;
218) echo "Proxmox: Template file corrupted or incomplete" ;; 245) echo "Node.js: Invalid command-line option" ;;
220) echo "Proxmox: Unable to resolve template path" ;; 246) echo "Node.js: Internal JavaScript Parse Error" ;;
221) echo "Proxmox: Template file not readable" ;; 247) echo "Node.js: Fatal internal error" ;;
222) echo "Proxmox: Template download failed" ;; 248) echo "Node.js: Invalid C++ addon / N-API failure" ;;
223) echo "Proxmox: Template not available after download" ;; 249) echo "npm/pnpm/yarn: Unknown fatal error" ;;
225) echo "Proxmox: No template available for OS/Version" ;; 255) echo "DPKG: Fatal internal error" ;;
231) echo "Proxmox: LXC stack upgrade failed" ;; *) echo "Unknown error" ;;
esac
# --- Default --- }
*) echo "Unknown error" ;; fi
esac
}
# ============================================================================== # ==============================================================================
# SECTION 2: ERROR HANDLERS # SECTION 2: ERROR HANDLERS

0
misc/ingest.go Normal file
View File