diff --git a/api/.env.example b/api/.env.example deleted file mode 100644 index fc7bdbb59..000000000 --- a/api/.env.example +++ /dev/null @@ -1,5 +0,0 @@ -MONGO_USER= -MONGO_PASSWORD= -MONGO_IP= -MONGO_PORT= -MONGO_DATABASE= \ No newline at end of file diff --git a/api/go.mod b/api/go.mod deleted file mode 100644 index 044bc8428..000000000 --- a/api/go.mod +++ /dev/null @@ -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 -) diff --git a/api/go.sum b/api/go.sum deleted file mode 100644 index cb111bdb8..000000000 --- a/api/go.sum +++ /dev/null @@ -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= diff --git a/api/main.go b/api/main.go deleted file mode 100644 index 55b2f24f8..000000000 --- a/api/main.go +++ /dev/null @@ -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)) -} diff --git a/docs/api/README.md b/docs/api/README.md index 3780a0d00..83ad70f8c 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -1,38 +1,30 @@ -# API Integration Documentation (/api) - -This directory contains comprehensive documentation for API integration and the `/api` directory. +# API Integration Documentation ## 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 -### Main API Service -Located in `/api/main.go`: +### PocketBase Backend +- **URL**: `http://db.community-scripts.org` +- **Collection**: `_dev_telemetry_data` +- **Admin UI**: `http://db.community-scripts.org/_/#/collections` - RESTful API for receiving telemetry data - Installation statistics tracking - Error reporting and analytics -- Performance monitoring ### Integration with Scripts The API is integrated into all installation scripts via `api.func`: - Sends installation start/completion events -- Reports errors and exit codes +- Reports errors and exit codes with numeric values - Collects anonymous usage statistics - Enables project analytics ## 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/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 @@ -42,48 +34,92 @@ API documentation covers: The `api.func` library provides: ### `post_to_api()` -Send container installation data to API. +Send LXC container installation data to PocketBase. -**Usage**: -```bash -post_to_api CTID STATUS APP_NAME -``` +Creates a new record in `_dev_telemetry_data` with status `installing`. -### `post_update_to_api()` -Report application update status. +### `post_to_api_vm()` +Send VM installation data to PocketBase. -**Usage**: -```bash -post_update_to_api CTID APP_NAME VERSION -``` +Creates a new record with `type=vm` and `ct_type=2`. -### `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. **Usage**: ```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 ### In Container Creation (`ct/AppName.sh`) -- Called by build.func to report container creation -- Sends initial container setup data -- Reports success or failure +- Called by `build.func` to report container creation via `post_to_api` +- Sends initial container setup data with status `installing` +- Reports success or failure via `post_update_to_api` -### In Installation Scripts (`install/appname-install.sh`) -- Called at start of installation -- Called on installation completion -- Called on error conditions +### In VM Creation (`vm/AppName.sh`) +- Calls `post_to_api_vm` after VM creation +- Status updates via `post_update_to_api` -### Data Collected -- Container/VM ID -- Application name and version -- Installation duration -- Success/failure status -- Error codes (if failure) -- Anonymous usage metrics +### Data Flow +``` +Installation Scripts + │ + ├─ Call: api.func functions + │ + ├─ 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 @@ -92,55 +128,18 @@ All API data: - ✅ Aggregated for statistics - ✅ Used only for project improvement - ✅ No tracking of user identities -- ✅ Can be disabled if desired - -## 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) +- ✅ Can be disabled via diagnostics settings ## Debugging API Issues If API calls fail: 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) -4. Check API function logs -5. Report issues on GitHub - -## API Endpoint - -**Base URL**: `https://api.community-scripts.org` - -**Endpoints**: -- `POST /install` - Report container installation -- `POST /update` - Report application update -- `GET /stats` - Public statistics +4. Check that `DIAGNOSTICS=yes` in `/usr/local/community-scripts/diagnostics` +5. Report issues on [GitHub](https://git.community-scripts.org/community-scripts/ProxmoxVED/issues) --- -**Last Updated**: December 2025 +**Last Updated**: February 2026 **Maintainers**: community-scripts team diff --git a/docs/misc/api.func/API_FLOWCHART.md b/docs/misc/api.func/API_FLOWCHART.md index a46cd56e9..bc01fce9f 100644 --- a/docs/misc/api.func/API_FLOWCHART.md +++ b/docs/misc/api.func/API_FLOWCHART.md @@ -1,5 +1,9 @@ # 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 ``` @@ -10,333 +14,321 @@ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ -│ Prerequisites Check │ -│ │ -│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ -│ │ Prerequisites Validation │ │ -│ │ │ │ -│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ -│ │ │ Check curl │ │ Check │ │ Check │ │ │ -│ │ │ Availability │ │ Diagnostics │ │ Random UUID │ │ │ -│ │ │ │ │ Setting │ │ │ │ -│ │ │ • command -v │ │ • DIAGNOSTICS │ │ • RANDOM_UUID │ │ -│ │ │ curl │ │ = "yes" │ │ not empty │ │ -│ │ │ • Return if │ │ • Return if │ │ • Return if │ │ -│ │ │ not found │ │ disabled │ │ not set │ │ -│ │ │ │ │ │ │ │ │ -│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ -│ └─────────────────────────────────────────────────────────────────────────────┘ │ +│ Prerequisites Check │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────┐ │ +│ │ Prerequisites Validation │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────────┐ │ │ +│ │ │ Check curl │ │ Check │ │ Check │ │ │ +│ │ │ Availability │ │ DIAGNOSTICS │ │ RANDOM_UUID │ │ │ +│ │ │ │ │ │ │ │ │ │ +│ │ │ • command -v │ │ • Must be "yes" │ │ • Must not be │ │ │ +│ │ │ curl │ │ • Return if │ │ empty │ │ │ +│ │ │ • Return if │ │ "no" or unset │ │ • Return if │ │ │ +│ │ │ not found │ │ │ │ not set │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └──────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ -│ Data Collection │ -│ │ -│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ -│ │ System Information Gathering │ │ -│ │ │ │ -│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ -│ │ │ Get PVE │ │ Collect │ │ Prepare JSON │ │ │ -│ │ │ Version │ │ Environment │ │ Payload │ │ -│ │ │ │ │ Variables │ │ │ │ -│ │ │ • pveversion │ │ • CT_TYPE │ │ • Create JSON │ │ -│ │ │ command │ │ • DISK_SIZE │ │ structure │ │ -│ │ │ • Parse version │ │ • CORE_COUNT │ │ • Include all │ │ -│ │ │ • Extract │ │ • RAM_SIZE │ │ variables │ │ -│ │ │ major.minor │ │ • var_os │ │ • Format for API │ │ -│ │ │ │ │ • var_version │ │ │ │ -│ │ │ │ │ • NSAPP │ │ │ │ -│ │ │ │ │ • METHOD │ │ │ │ -│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ -│ └─────────────────────────────────────────────────────────────────────────────┘ │ +│ Data Collection │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────┐ │ +│ │ System Information Gathering │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────────┐ │ │ +│ │ │ Get PVE │ │ Collect Env │ │ Build JSON │ │ │ +│ │ │ Version │ │ Variables │ │ Payload │ │ │ +│ │ │ │ │ │ │ │ │ │ +│ │ │ • pveversion │ │ • CT_TYPE │ │ • Heredoc JSON │ │ │ +│ │ │ command │ │ • DISK_SIZE │ │ • Include all │ │ │ +│ │ │ • Parse version │ │ • CORE_COUNT │ │ fields │ │ │ +│ │ │ • Fallback: │ │ • RAM_SIZE │ │ • status = │ │ │ +│ │ │ "not found" │ │ • var_os │ │ "installing" │ │ │ +│ │ │ │ │ • NSAPP, METHOD │ │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └──────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ -│ API Request Execution │ -│ │ -│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ -│ │ HTTP Request Processing │ │ -│ │ │ │ -│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ -│ │ │ Prepare │ │ Execute │ │ Handle │ │ │ -│ │ │ Request │ │ HTTP Request │ │ Response │ │ -│ │ │ │ │ │ │ │ │ -│ │ │ • Set API URL │ │ • curl -s -w │ │ • Capture HTTP │ │ -│ │ │ • Set headers │ │ "%{http_code}" │ │ status code │ │ -│ │ │ • Set payload │ │ • POST request │ │ • Store response │ │ -│ │ │ • Content-Type │ │ • JSON data │ │ • Handle errors │ │ -│ │ │ application/ │ │ • Follow │ │ gracefully │ │ -│ │ │ json │ │ redirects │ │ │ │ -│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ -│ └─────────────────────────────────────────────────────────────────────────────┘ │ +│ PocketBase API Request │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────┐ │ +│ │ HTTP Request Processing │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────────┐ │ │ +│ │ │ Prepare │ │ Execute │ │ Handle │ │ │ +│ │ │ Request │ │ HTTP POST │ │ Response │ │ │ +│ │ │ │ │ │ │ │ │ │ +│ │ │ • URL: │ │ • curl -s -w │ │ • Check HTTP │ │ │ +│ │ │ PB_API_URL │ │ "%{http_code}"│ │ 200/201 │ │ │ +│ │ │ • Method: POST │ │ • -X POST │ │ • Extract "id" │ │ │ +│ │ │ • Content-Type: │ │ • -L (follow │ │ from response │ │ │ +│ │ │ application/ │ │ redirects) │ │ • Store in │ │ │ +│ │ │ json │ │ • JSON body │ │ PB_RECORD_ID │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └──────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` -## LXC API Reporting Flow +## LXC API Reporting Flow — `post_to_api()` ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ -│ POST_TO_API() Flow │ -│ Send LXC container installation data to API │ +│ post_to_api() Flow │ +│ POST → Create LXC telemetry record in PocketBase │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ -│ LXC Data Preparation │ -│ │ -│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ -│ │ 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 │ +│ Prerequisites: curl? ──► DIAGNOSTICS="yes"? ──► RANDOM_UUID set? │ +│ (return silently on any failure) │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ -│ VM Data Preparation │ -│ │ -│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ -│ │ VM-Specific Data Collection │ │ -│ │ │ │ -│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ -│ │ │ Check │ │ Set VM │ │ Process Disk │ │ │ -│ │ │ Diagnostics │ │ Type │ │ Size │ │ -│ │ │ File │ │ │ │ │ │ -│ │ │ │ │ • ct_type: 2 │ │ • Remove 'G' │ │ -│ │ │ • Check file │ │ • type: "vm" │ │ suffix │ │ -│ │ │ 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 │ +│ LXC Data Preparation │ +│ │ +│ ┌─────────────────┐ ┌─────────────────────┐ ┌───────────────────┐ │ +│ │ Set LXC type │ │ Collect variables │ │ Set initial │ │ +│ │ │ │ │ │ status │ │ +│ │ • ct_type: 1 │ │ • DISK_SIZE │ │ │ │ +│ │ • type: "lxc" │ │ • CORE_COUNT │ │ • status: │ │ +│ │ │ │ • RAM_SIZE │ │ "installing" │ │ +│ │ │ │ • var_os, var_version│ │ • random_id: │ │ +│ │ │ │ • NSAPP, METHOD │ │ RANDOM_UUID │ │ +│ │ │ │ • pve_version │ │ │ │ +│ └─────────────────┘ └─────────────────────┘ └───────────────────┘ │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ -│ Update Prevention Check │ -│ │ -│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ -│ │ Duplicate Update Prevention │ │ -│ │ │ │ -│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ -│ │ │ Check │ │ Set Flag │ │ Return Early │ │ │ -│ │ │ 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 │ │ │ │ -│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ -│ └─────────────────────────────────────────────────────────────────────────────┘ │ +│ POST → PB_API_URL │ +│ http://db.community-scripts.org/api/collections/_dev_telemetry_data/records │ +│ │ +│ Response (HTTP 200/201): │ +│ { "id": "abc123def456789", ... } │ +│ │ │ +│ └──► PB_RECORD_ID = "abc123def456789" │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` -## Error Description Flow +## VM API Reporting Flow — `post_to_api_vm()` ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ -│ GET_ERROR_DESCRIPTION() Flow │ -│ Convert numeric exit codes to human-readable explanations │ +│ post_to_api_vm() Flow │ +│ POST → Create VM telemetry record in PocketBase │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ -│ Error Code Classification │ -│ │ -│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ -│ │ 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 │ │ │ │ │ │ -│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ -│ └─────────────────────────────────────────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────────────────────────┘ +│ Read /usr/local/community-scripts/diagnostics │ +│ Extract DIAGNOSTICS=yes/no from file │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ -│ Error Message Return │ -│ │ -│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ -│ │ Error Message Formatting │ │ -│ │ │ │ -│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ -│ │ │ Match Error │ │ Return │ │ Default Case │ │ │ -│ │ │ Code │ │ Description │ │ │ │ -│ │ │ │ │ │ │ │ │ -│ │ │ • Use case │ │ • Return │ │ • Return "Unknown │ │ -│ │ │ statement │ │ human- │ │ error code │ │ -│ │ │ • Match │ │ readable │ │ (exit_code)" │ │ -│ │ │ specific │ │ message │ │ • Handle │ │ -│ │ │ codes │ │ • Include │ │ unrecognized │ │ -│ │ │ • Handle │ │ context │ │ codes │ │ -│ │ │ ranges │ │ information │ │ • Provide fallback │ │ -│ │ │ │ │ │ │ message │ │ -│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ -│ └─────────────────────────────────────────────────────────────────────────────┘ │ +│ Prerequisites: curl? ──► DIAGNOSTICS="yes"? ──► RANDOM_UUID set? │ +│ (return silently on any failure) │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ VM Data Preparation │ +│ │ +│ ┌─────────────────┐ ┌─────────────────────┐ ┌───────────────────┐ │ +│ │ Set VM type │ │ Process disk size │ │ Set initial │ │ +│ │ │ │ │ │ status │ │ +│ │ • ct_type: 2 │ │ • Strip 'G' suffix │ │ │ │ +│ │ • type: "vm" │ │ "20G" → 20 │ │ • status: │ │ +│ │ │ │ • Store in │ │ "installing" │ │ +│ │ │ │ DISK_SIZE_API │ │ • random_id: │ │ +│ │ │ │ │ │ RANDOM_UUID │ │ +│ └─────────────────┘ └─────────────────────┘ └───────────────────┘ │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ 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 | │ +│ } │ +│ │ +│ ──► 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 │ │ +│ │ 1–2 │ │ 6, 7, 22, 28, 35│ │ 100–102, 255 │ │ +│ └─────────────────┘ └──────────────────┘ └──────────────────────────┘ │ +│ │ +│ ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │ +│ │ System/Signals │ │ Systemd/Service │ │ Python/pip/uv │ │ +│ │ 124–143 │ │ 150–154 │ │ 160–162 │ │ +│ └─────────────────┘ └──────────────────┘ └──────────────────────────┘ │ +│ │ +│ ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │ +│ │ PostgreSQL │ │ MySQL/MariaDB │ │ MongoDB │ │ +│ │ 170–173 │ │ 180–183 │ │ 190–193 │ │ +│ └─────────────────┘ └──────────────────┘ └──────────────────────────┘ │ +│ │ +│ ┌─────────────────┐ ┌──────────────────┐ │ +│ │ Proxmox │ │ Node.js/npm │ │ +│ │ 200–231 │ │ 243–249 │ │ +│ └─────────────────┘ └──────────────────┘ │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ case "$code" in │ +│ ) echo "" ;; │ +│ *) 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 ### With Installation Scripts -- **build.func**: Sends LXC installation data -- **vm-core.func**: Sends VM installation data -- **install.func**: Reports installation status -- **alpine-install.func**: Reports Alpine installation data +- **build.func**: Calls `post_to_api()` for LXC creation, then `post_update_to_api()` on completion +- **vm-core.func**: Calls `post_to_api_vm()` for VM creation, then `post_update_to_api()` on completion +- **install.func / alpine-install.func**: Reports installation status via `post_update_to_api()` ### With Error Handling -- **error_handler.func**: Provides error explanations -- **core.func**: Uses error descriptions in silent execution -- **Diagnostic reporting**: Tracks error patterns +- **error_handler.func**: Uses `explain_exit_code()` for human-readable error messages +- **Diagnostic reporting**: PocketBase records track error patterns anonymously ### External Dependencies -- **curl**: HTTP client for API communication -- **Community Scripts API**: External API endpoint -- **Network connectivity**: Required for API communication +- **curl**: HTTP client for PocketBase API communication +- **PocketBase**: Backend at `http://db.community-scripts.org` +- **Network connectivity**: Required for API communication (failures are silently ignored) diff --git a/docs/misc/api.func/API_FUNCTIONS_REFERENCE.md b/docs/misc/api.func/API_FUNCTIONS_REFERENCE.md index 732261f49..87c23a8bc 100644 --- a/docs/misc/api.func/API_FUNCTIONS_REFERENCE.md +++ b/docs/misc/api.func/API_FUNCTIONS_REFERENCE.md @@ -2,63 +2,88 @@ ## 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 ### Error Description Functions -#### `get_error_description()` +#### `explain_exit_code()` + **Purpose**: Convert numeric exit codes to human-readable explanations **Parameters**: -- `$1` - Exit code to explain +- `$1` — Exit code to explain **Returns**: Human-readable error explanation string **Side Effects**: None **Dependencies**: None **Environment Variables Used**: None -**Supported Exit Codes**: -- **General System**: 0-9, 18, 22, 28, 35, 56, 60, 125-128, 129-143, 152, 255 -- **LXC-Specific**: 100-101, 200-209 -- **Docker**: 125 +> **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). + +**Supported Exit Code Ranges** (non-overlapping): + +| Range | Category | +|-------|----------| +| 1–2 | Generic / Shell | +| 6–35 | curl / wget | +| 100–102 | APT / Package manager | +| 124–143 | System / Signals | +| 150–154 | Systemd / Service | +| 160–162 | Python / pip / uv | +| 170–173 | PostgreSQL | +| 180–183 | MySQL / MariaDB | +| 190–193 | MongoDB | +| 200–231 | Proxmox custom codes | +| 243–249 | Node.js / npm | +| 255 | DPKG fatal | **Usage Example**: ```bash -error_msg=$(get_error_description 127) +error_msg=$(explain_exit_code 127) 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**: ```bash -get_error_description 0 # " " (space) -get_error_description 1 # "General error: An unspecified error occurred." -get_error_description 127 # "Command not found: Incorrect path or missing dependency." -get_error_description 200 # "LXC creation failed." -get_error_description 255 # "Unknown critical error, often due to missing permissions or broken scripts." +explain_exit_code 1 # "General error / Operation not permitted" +explain_exit_code 22 # "curl: HTTP error returned (404, 429, 500+)" +explain_exit_code 127 # "Command not found" +explain_exit_code 200 # "Proxmox: Failed to create lock file" +explain_exit_code 255 # "DPKG: Fatal internal error" +explain_exit_code 999 # "Unknown error" ``` ### API Communication Functions #### `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) **Returns**: None **Side Effects**: -- Sends HTTP POST request to API -- Stores response in RESPONSE variable -- Requires curl command and network connectivity +- Sends HTTP **POST** to `PB_API_URL` +- Stores the returned PocketBase record `id` in `PB_RECORD_ID` for later PATCH updates **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**: -- `curl` command must be available -- `DIAGNOSTICS` must be set to "yes" +- `curl` must be available +- `DIAGNOSTICS` must be `"yes"` - `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 { "ct_type": 1, @@ -68,7 +93,6 @@ get_error_description 255 # "Unknown critical error, often due to missing perm "ram_size": 2048, "os_type": "debian", "os_version": "12", - "disableip6": "true", "nsapp": "plex", "method": "install", "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**: ```bash export DIAGNOSTICS="yes" @@ -91,39 +119,39 @@ export NSAPP="plex" export METHOD="install" post_to_api +# PB_RECORD_ID is now set (e.g. "abc123def456789") ``` #### `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) **Returns**: None **Side Effects**: -- Sends HTTP POST request to API -- Stores response in RESPONSE variable -- Requires curl command and network connectivity +- Sends HTTP **POST** to `PB_API_URL` +- Stores the returned PocketBase record `id` in `PB_RECORD_ID` **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**: - `/usr/local/community-scripts/diagnostics` file must exist -- `DIAGNOSTICS` must be set to "yes" in diagnostics file -- `curl` command must be available +- `DIAGNOSTICS` must be `"yes"` in that file (read at runtime) +- `curl` must be available - `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 { "ct_type": 2, "type": "vm", - "disk_size": 8, - "core_count": 2, - "ram_size": 2048, - "os_type": "debian", - "os_version": "12", - "disableip6": "", - "nsapp": "plex", + "disk_size": 20, + "core_count": 4, + "ram_size": 4096, + "os_type": "ubuntu", + "os_version": "22.04", + "nsapp": "nextcloud", "method": "install", "pve_version": "8.0", "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**: ```bash # Create diagnostics file +mkdir -p /usr/local/community-scripts echo "DIAGNOSTICS=yes" > /usr/local/community-scripts/diagnostics export RANDOM_UUID="$(uuidgen)" -export DISK_SIZE="8G" -export CORE_COUNT=2 -export RAM_SIZE=2048 -export var_os="debian" -export var_version="12" -export NSAPP="plex" +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" post_to_api_vm +# PB_RECORD_ID is now set ``` #### `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**: -- `$1` - Status ("success" or "failed", default: "failed") -- `$2` - Exit code (default: 1) +- `$1` — Status (`"done"`, `"success"`, or `"failed"`; default: `"failed"`) +- `$2` — Exit code (numeric, default: `1`) **Returns**: None **Side Effects**: -- Sends HTTP POST request to API -- Sets POST_UPDATE_DONE=true to prevent duplicates -- Stores response in RESPONSE variable -**Dependencies**: `curl` command, `get_error_description()` -**Environment Variables Used**: `DIAGNOSTICS`, `RANDOM_UUID` +- Sends HTTP **PATCH** to `PB_API_URL/{record_id}` +- Sets `POST_UPDATE_DONE=true` to prevent duplicate calls +**Dependencies**: `curl`, `explain_exit_code()` +**Environment Variables Used**: `DIAGNOSTICS`, `RANDOM_UUID`, `PB_RECORD_ID` **Prerequisites**: -- `curl` command must be available -- `DIAGNOSTICS` must be set to "yes" +- `curl` must be available +- `DIAGNOSTICS` must be `"yes"` - `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='')&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 { - "status": "success", - "error": "Error description from get_error_description()", - "random_id": "uuid-string" + "status": "sucess", + "error": "", + "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 RANDOM_UUID="$(uuidgen)" -# Report successful installation -post_update_to_api "success" 0 +# After a successful installation +post_update_to_api "done" 0 -# Report failed installation +# After a failed installation post_update_to_api "failed" 127 ``` @@ -196,198 +255,250 @@ post_update_to_api "failed" 127 ``` post_to_api() ├── Check curl availability -├── Check DIAGNOSTICS setting -├── Check RANDOM_UUID +├── Check DIAGNOSTICS == "yes" +├── Check RANDOM_UUID is set ├── Get PVE version -├── Create JSON payload -└── Send HTTP POST request +├── Create JSON payload (ct_type=1, type="lxc", status="installing") +├── POST to PB_API_URL +└── Extract PB_RECORD_ID from response post_to_api_vm() -├── Check diagnostics file +├── Read DIAGNOSTICS from /usr/local/community-scripts/diagnostics ├── Check curl availability -├── Check DIAGNOSTICS setting -├── Check RANDOM_UUID -├── Process disk size +├── Check DIAGNOSTICS == "yes" +├── Check RANDOM_UUID is set +├── Strip 'G' suffix from DISK_SIZE ├── Get PVE version -├── Create JSON payload -└── Send HTTP POST request +├── Create JSON payload (ct_type=2, type="vm", status="installing") +├── POST to PB_API_URL +└── Extract PB_RECORD_ID from response -post_update_to_api() -├── Check POST_UPDATE_DONE flag +post_update_to_api(status, exit_code) ├── Check curl availability -├── Check DIAGNOSTICS setting -├── Check RANDOM_UUID -├── Determine status and exit code -├── Get error description -├── Create JSON payload -├── Send HTTP POST request +├── Check POST_UPDATE_DONE flag +├── Check DIAGNOSTICS == "yes" +├── Check RANDOM_UUID is set +├── Map status → pb_status ("done"→"sucess", "failed"→"failed", *→"unknown") +├── For failed/unknown: call explain_exit_code(exit_code) +├── Resolve record_id (PB_RECORD_ID or GET lookup by random_id) +├── PATCH to PB_API_URL/{record_id} └── Set POST_UPDATE_DONE=true ``` ### Error Description Flow ``` -get_error_description() -├── Match exit code -├── Return appropriate description -└── Handle unknown codes +explain_exit_code(code) +├── Match code against case statement (non-overlapping ranges) +├── Return description string +└── Default: "Unknown error" ``` ## Error Code Reference -### General System Errors +### Generic / Shell (1–2) | Code | Description | |------|-------------| -| 0 | (space) | -| 1 | General error: An unspecified error occurred. | -| 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. | +| 1 | General error / Operation not permitted | +| 2 | Misuse of shell builtins (e.g. syntax error) | -### Command Execution Errors +### curl / wget (6–35) | Code | Description | |------|-------------| -| 125 | Docker error: Container could not start. | -| 126 | Command not executable: Incorrect permissions or missing dependencies. | -| 127 | Command not found: Incorrect path or missing dependency. | -| 128 | Invalid exit signal, e.g., incorrect Git command. | +| 6 | curl: DNS resolution failed (could not resolve host) | +| 7 | curl: Failed to connect (network unreachable / host down) | +| 22 | curl: HTTP error returned (404, 429, 500+) | +| 28 | curl: Operation timeout (network slow or server not responding) | +| 35 | curl: SSL/TLS handshake failed (certificate error) | -### Signal Errors +### APT / Package Manager (100–102) | Code | Description | |------|-------------| -| 129 | Signal 1 (SIGHUP): Process terminated due to hangup. | -| 130 | Signal 2 (SIGINT): Manual termination via Ctrl+C. | -| 132 | Signal 4 (SIGILL): Illegal machine instruction. | -| 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. | +| 100 | APT: Package manager error (broken packages / dependency problems) | +| 101 | APT: Configuration error (bad sources.list, malformed config) | +| 102 | APT: Lock held by another process (dpkg/apt still running) | -### LXC-Specific Errors +### System / Signals (124–143) | Code | Description | |------|-------------| -| 100 | LXC install error: Unexpected error in create_lxc.sh. | -| 101 | LXC install error: No network connection detected. | -| 200 | LXC creation failed. | -| 201 | LXC error: Invalid Storage class. | -| 202 | User aborted menu in create_lxc.sh. | -| 203 | CTID not set in create_lxc.sh. | -| 204 | PCT_OSTYPE not set in create_lxc.sh. | -| 205 | CTID cannot be less than 100 in create_lxc.sh. | -| 206 | CTID already in use in create_lxc.sh. | -| 207 | Template not found in create_lxc.sh. | -| 208 | Error downloading template in create_lxc.sh. | -| 209 | Container creation failed, but template is intact in create_lxc.sh. | +| 124 | Command timed out (timeout command) | +| 126 | Command invoked cannot execute (permission problem?) | +| 127 | Command not found | +| 128 | Invalid argument to exit | +| 130 | Terminated by Ctrl+C (SIGINT) | +| 134 | Process aborted (SIGABRT — possibly Node.js heap overflow) | +| 137 | Killed (SIGKILL / Out of memory?) | +| 139 | Segmentation fault (core dumped) | +| 141 | Broken pipe (SIGPIPE — output closed prematurely) | +| 143 | Terminated (SIGTERM) | -### Other Errors +### Systemd / Service (150–154) | Code | Description | |------|-------------| -| 255 | Unknown critical error, often due to missing permissions or broken scripts. | -| * | Unknown error code (exit_code). | +| 150 | Systemd: Service failed to start | +| 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 (160–162) +| Code | Description | +|------|-------------| +| 160 | Python: Virtualenv / uv environment missing or broken | +| 161 | Python: Dependency resolution failed | +| 162 | Python: Installation aborted (permissions or EXTERNALLY-MANAGED) | + +### PostgreSQL (170–173) +| 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 (180–183) +| 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 (190–193) +| 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 (200–231) +| 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 (243–249) +| 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 ### Required Variables -- **`DIAGNOSTICS`**: Enable/disable diagnostic reporting ("yes"/"no") -- **`RANDOM_UUID`**: Unique identifier for tracking +- **`DIAGNOSTICS`**: Enable/disable diagnostic reporting (`"yes"` / `"no"`) +- **`RANDOM_UUID`**: Unique identifier for session tracking -### Optional Variables -- **`CT_TYPE`**: Container type (1 for LXC, 2 for VM) -- **`DISK_SIZE`**: Disk size in GB (or GB with 'G' suffix for VM) +### Container / VM Variables +- **`CT_TYPE`**: Container type (`1` for LXC, `2` for VM) +- **`DISK_SIZE`**: Disk size in GB (VMs may include `G` suffix) - **`CORE_COUNT`**: Number of CPU cores - **`RAM_SIZE`**: RAM size in MB - **`var_os`**: Operating system type - **`var_version`**: OS version -- **`DISABLEIP6`**: IPv6 disable setting -- **`NSAPP`**: Namespace application name +- **`NSAPP`**: Application name - **`METHOD`**: Installation method ### Internal Variables -- **`POST_UPDATE_DONE`**: Prevents duplicate status updates -- **`API_URL`**: Community scripts API endpoint -- **`JSON_PAYLOAD`**: API request payload -- **`RESPONSE`**: API response -- **`DISK_SIZE_API`**: Processed disk size for VM API +- **`PB_URL`**: PocketBase server URL +- **`PB_COLLECTION`**: PocketBase collection name +- **`PB_API_URL`**: Full PocketBase API endpoint +- **`PB_RECORD_ID`**: PocketBase record ID (set after POST, used for PATCH) +- **`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 ### API Communication Errors -- All API functions handle curl failures gracefully -- Network errors don't block installation process -- Missing prerequisites cause early return -- Duplicate updates are prevented +- All API functions return silently on failure — network errors never block installation +- Missing prerequisites (no curl, diagnostics disabled, no UUID) cause early return +- `POST_UPDATE_DONE` flag prevents duplicate PATCH updates +- PocketBase record lookup falls back to `GET ?filter=(random_id='...')` if `PB_RECORD_ID` is unset ### Error Description Errors -- Unknown error codes return generic message -- All error codes are handled with case statement -- Fallback message includes the actual error code - -### Prerequisites Validation -- Check curl availability before API calls -- Validate DIAGNOSTICS setting -- Ensure RANDOM_UUID is set -- Check for duplicate updates +- Unknown error codes return `"Unknown error"` +- All recognized codes are handled via a `case` statement with non-overlapping ranges +- The fallback message is generic (no error code is embedded) ## Integration Examples -### With build.func +### With build.func (LXC) ```bash #!/usr/bin/env bash source core.func source api.func source build.func -# Set up API reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" -# Report installation start +# Report LXC installation start → POST creates record post_to_api -# Container creation... -# ... build.func code ... +# ... container creation via build.func ... -# Report completion +# Report completion → PATCH updates record if [[ $? -eq 0 ]]; then - post_update_to_api "success" 0 + post_update_to_api "done" 0 else post_update_to_api "failed" $? fi ``` -### With vm-core.func +### With vm-core.func (VM) ```bash #!/usr/bin/env bash source core.func source api.func source vm-core.func -# Set up API reporting -export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" -# Report VM installation start +# Report VM installation start → POST creates record post_to_api_vm -# VM creation... -# ... vm-core.func code ... +# ... VM creation via vm-core.func ... -# Report completion -post_update_to_api "success" 0 +# Report completion → PATCH updates record +post_update_to_api "done" 0 ``` ### With error_handler.func @@ -397,37 +508,30 @@ source core.func source error_handler.func source api.func -# Use error descriptions error_code=127 -error_msg=$(get_error_description $error_code) +error_msg=$(explain_exit_code $error_code) echo "Error $error_code: $error_msg" -# Report error to API +# Report error to PocketBase post_update_to_api "failed" $error_code ``` ## Best Practices ### API Usage -1. Always check prerequisites before API calls -2. Use unique identifiers for tracking -3. Handle API failures gracefully -4. Don't block installation on API failures +1. Always check prerequisites before API calls (handled internally by each function) +2. Call `post_to_api` / `post_to_api_vm` **once** at installation start to get a `PB_RECORD_ID` +3. Call `post_update_to_api` **once** at the end to finalize the record via PATCH +4. Never block the installation on API failures ### Error Reporting -1. Use appropriate error codes -2. Provide meaningful error descriptions -3. Report both success and failure cases -4. Prevent duplicate status updates +1. Use `explain_exit_code()` for human-readable error messages +2. Pass the actual numeric exit code to `post_update_to_api` +3. Report both success (`"done"`) and failure (`"failed"`) cases +4. The `POST_UPDATE_DONE` flag automatically prevents duplicate updates ### Diagnostic Reporting -1. Respect user privacy settings -2. Only send data when diagnostics enabled -3. Use anonymous tracking identifiers -4. Include relevant system information - -### 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 +1. Respect user privacy — only send data when `DIAGNOSTICS="yes"` +2. Use anonymous random UUIDs for session tracking (no personal data) +3. Include relevant system information (PVE version, OS, app name) +4. The diagnostics file at `/usr/local/community-scripts/diagnostics` controls VM reporting diff --git a/docs/misc/api.func/API_INTEGRATION.md b/docs/misc/api.func/API_INTEGRATION.md index f325dace2..f3e3ba2c9 100644 --- a/docs/misc/api.func/API_INTEGRATION.md +++ b/docs/misc/api.func/API_INTEGRATION.md @@ -2,26 +2,42 @@ ## 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 ### External Dependencies #### Required Commands -- **`curl`**: HTTP client for API communication -- **`uuidgen`**: Generate unique identifiers (optional, can use other methods) +- **`curl`**: HTTP client for PocketBase API communication #### 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 #### 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 -- **core.func**: Provides system information variables -- **Installation scripts**: Provide application-specific variables +- **core.func**: Provides system information +- **Installation scripts**: Provide application-specific variables (`NSAPP`, `METHOD`) ## Integration Points @@ -29,48 +45,41 @@ This document describes how `api.func` integrates with other components in the P #### LXC Container Reporting ```bash -# build.func uses api.func for container reporting 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() { - # Set container parameters - export CT_TYPE=1 - export DISK_SIZE="$var_disk" - export CORE_COUNT="$var_cpu" - export RAM_SIZE="$var_ram" - export var_os="$var_os" - export var_version="$var_version" - export NSAPP="$APP" - export METHOD="install" +# Set container parameters +export CT_TYPE=1 +export DISK_SIZE="$var_disk" +export CORE_COUNT="$var_cpu" +export RAM_SIZE="$var_ram" +export var_os="$var_os" +export var_version="$var_version" +export NSAPP="$APP" +export METHOD="install" - # Report installation start - post_to_api +# POST → creates record in PocketBase, saves PB_RECORD_ID +post_to_api - # Container creation using build.func - # ... build.func container creation logic ... +# ... container creation via build.func ... - # Report completion - if [[ $? -eq 0 ]]; then - post_update_to_api "success" 0 - else - post_update_to_api "failed" $? - fi -} +# PATCH → updates the record with final status +if [[ $? -eq 0 ]]; then + post_update_to_api "done" 0 +else + post_update_to_api "failed" $? +fi ``` #### Error Reporting Integration ```bash -# build.func uses api.func for error reporting handle_container_error() { 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" post_update_to_api "failed" $exit_code @@ -81,93 +90,54 @@ handle_container_error() { #### VM Installation Reporting ```bash -# vm-core.func uses api.func for VM reporting source core.func source api.func source vm-core.func -# Set up VM API reporting +# VM reads DIAGNOSTICS from file 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() { - # Set VM parameters - export DISK_SIZE="${var_disk}G" - export CORE_COUNT="$var_cpu" - export RAM_SIZE="$var_ram" - export var_os="$var_os" - export var_version="$var_version" - export NSAPP="$APP" - export METHOD="install" +# Set VM parameters +export DISK_SIZE="${var_disk}G" +export CORE_COUNT="$var_cpu" +export RAM_SIZE="$var_ram" +export var_os="$var_os" +export var_version="$var_version" +export NSAPP="$APP" +export METHOD="install" - # Report VM installation start - post_to_api_vm +# POST → creates record in PocketBase (ct_type=2, type="vm") +post_to_api_vm - # VM creation using vm-core.func - # ... vm-core.func VM creation logic ... +# ... VM creation via vm-core.func ... - # Report completion - post_update_to_api "success" 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 -} +# PATCH → finalizes record +post_update_to_api "done" 0 ``` ### With error_handler.func #### Error Description Integration ```bash -# error_handler.func uses api.func for error descriptions source core.func source error_handler.func source api.func -# Enhanced error handler 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) + # explain_exit_code() is the canonical error description function + local error_msg=$(explain_exit_code $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)" + # PATCH the telemetry record with failure details 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 ```bash -# install.func uses api.func for installation reporting source core.func source api.func source install.func -# Installation with API reporting install_package_with_reporting() { local package="$1" - # Set up API reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" export NSAPP="$package" export METHOD="install" - # Report installation start + # POST → create telemetry record post_to_api - # Package installation using install.func if install_package "$package"; then echo "$package installed successfully" - post_update_to_api "success" 0 + post_update_to_api "done" 0 return 0 else 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" post_update_to_api "failed" $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 ### Input Data -#### Environment Variables from Other Scripts -- **`CT_TYPE`**: Container type (1 for LXC, 2 for VM) -- **`DISK_SIZE`**: Disk size in GB -- **`CORE_COUNT`**: Number of CPU cores -- **`RAM_SIZE`**: RAM size in MB -- **`var_os`**: Operating system type -- **`var_version`**: OS version -- **`DISABLEIP6`**: IPv6 disable setting -- **`NSAPP`**: Namespace application name -- **`METHOD`**: Installation method -- **`DIAGNOSTICS`**: Enable/disable diagnostic reporting -- **`RANDOM_UUID`**: Unique identifier for tracking +#### Environment Variables +| Variable | Source | Description | +|----------|--------|-------------| +| `CT_TYPE` | build.func | Container type (1=LXC, 2=VM) | +| `DISK_SIZE` | build.func / vm-core.func | Disk size in GB (VMs may have `G` suffix) | +| `CORE_COUNT` | build.func / vm-core.func | CPU core count | +| `RAM_SIZE` | build.func / vm-core.func | RAM in MB | +| `var_os` | core.func | Operating system type | +| `var_version` | core.func | OS version | +| `NSAPP` | Installation scripts | Application name | +| `METHOD` | Installation scripts | Installation method | +| `DIAGNOSTICS` | User config / diagnostics file | Enable/disable telemetry | +| `RANDOM_UUID` | Caller | Session tracking UUID | #### Function Parameters -- **Exit codes**: Passed to `get_error_description()` and `post_update_to_api()` -- **Status information**: Passed to `post_update_to_api()` -- **API endpoints**: Hardcoded in functions +- **Exit codes**: Passed to `explain_exit_code()` and `post_update_to_api()` +- **Status strings**: Passed to `post_update_to_api()` (`"done"`, `"failed"`) #### System Information -- **PVE version**: Retrieved from `pveversion` command -- **Disk size processing**: Processed for VM API (removes 'G' suffix) -- **Error codes**: Retrieved from command exit codes +- **PVE version**: Retrieved from `pveversion` command at runtime +- **Disk size**: VM disk size is stripped of `G` suffix before sending -### Processing Data +### Processing -#### API Request Preparation -- **JSON payload creation**: Format data for API consumption -- **Data validation**: Ensure required fields are present -- **Error handling**: Handle missing or invalid data -- **Content type setting**: Set appropriate HTTP headers +#### Record Creation (POST) +1. Validate prerequisites (curl, DIAGNOSTICS, RANDOM_UUID) +2. Gather PVE version +3. Build JSON payload with all telemetry fields +4. `POST` to `PB_API_URL` +5. Extract `PB_RECORD_ID` from PocketBase response (HTTP 200/201) -#### Error Processing -- **Error code mapping**: Map numeric codes to descriptions -- **Error message formatting**: Format error descriptions -- **Unknown error handling**: Handle unrecognized error codes -- **Fallback messages**: Provide default error messages - -#### API Communication -- **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 +#### Record Update (PATCH) +1. Validate prerequisites + check `POST_UPDATE_DONE` flag +2. Map status string → PocketBase select value (`"done"` → `"sucess"`) +3. For failures: call `explain_exit_code()` to get error description +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 +6. Set `POST_UPDATE_DONE=true` ### Output Data -#### API Communication -- **HTTP requests**: Sent to community-scripts.org API -- **Response codes**: Captured from API responses -- **Error information**: Reported to API -- **Status updates**: Sent to API +#### PocketBase Records +- **POST response**: Returns record with `id` field → stored in `PB_RECORD_ID` +- **PATCH response**: Updates record fields (status, error, exit_code) +- **GET response**: Used for record ID lookup by `random_id` filter -#### Error Information -- **Error descriptions**: Human-readable error messages -- **Error codes**: Mapped to descriptions -- **Context information**: Error context and details -- **Fallback messages**: Default error messages - -#### System State -- **POST_UPDATE_DONE**: Prevents duplicate updates -- **RESPONSE**: Stores API response -- **JSON_PAYLOAD**: Stores formatted API data -- **API_URL**: Stores API endpoint +#### Internal State +| Variable | Description | +|----------|-------------| +| `PB_RECORD_ID` | PocketBase record ID for PATCH calls | +| `POST_UPDATE_DONE` | Flag preventing duplicate updates | ## API Surface ### Public Functions -#### Error Description -- **`get_error_description()`**: Convert exit codes to explanations -- **Parameters**: Exit code to explain -- **Returns**: Human-readable explanation string -- **Usage**: Called by other functions and scripts +| Function | Purpose | HTTP Method | +|----------|---------|-------------| +| `explain_exit_code(code)` | Map exit code to description | — | +| `post_to_api()` | Create LXC telemetry record | POST | +| `post_to_api_vm()` | Create VM telemetry record | POST | +| `post_update_to_api(status, exit_code)` | Update record with final status | PATCH | -#### API Communication -- **`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 +### PocketBase Collection Schema -### Internal Functions +Collection: `_dev_telemetry_data` -#### None -- All functions in api.func are public -- No internal helper functions -- Direct implementation of all functionality +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `id` | text (auto) | yes | PocketBase record ID (15 chars) | +| `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 -- **`DIAGNOSTICS`**: Diagnostic reporting setting -- **`RANDOM_UUID`**: Unique tracking identifier -- **`POST_UPDATE_DONE`**: Duplicate update prevention - -#### Data Variables -- **`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 +### Configuration Variables +| Variable | Value | +|----------|-------| +| `PB_URL` | `http://db.community-scripts.org` | +| `PB_COLLECTION` | `_dev_telemetry_data` | +| `PB_API_URL` | `${PB_URL}/api/collections/${PB_COLLECTION}/records` | ## Integration Patterns @@ -479,45 +280,39 @@ run_maintenance_with_reporting() { ```bash #!/usr/bin/env bash -# Standard integration pattern -# 1. Source core.func first +# 1. Source dependencies source core.func - -# 2. Source api.func source api.func -# 3. Set up API reporting +# 2. Enable telemetry export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" -# 4. Set application parameters +# 3. Set application parameters export NSAPP="$APP" export METHOD="install" -# 5. Report installation start +# 4. POST → create telemetry record in PocketBase post_to_api -# 6. Perform installation +# 5. Perform installation # ... installation logic ... -# 7. Report completion -post_update_to_api "success" 0 +# 6. PATCH → update record with final status +post_update_to_api "done" 0 ``` ### Minimal Integration Pattern ```bash #!/usr/bin/env bash -# Minimal integration pattern - source api.func -# Basic error reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" -# Report failure +# Report failure (PATCH via record lookup) post_update_to_api "failed" 127 ``` @@ -525,13 +320,10 @@ post_update_to_api "failed" 127 ```bash #!/usr/bin/env bash -# Advanced integration pattern - source core.func source api.func source error_handler.func -# Set up comprehensive API reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" export CT_TYPE=1 @@ -542,12 +334,12 @@ export var_os="debian" export var_version="12" export METHOD="install" -# Enhanced error handling with API reporting +# Enhanced error handler with PocketBase reporting enhanced_error_handler() { local exit_code=${1:-$?} 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" post_update_to_api "failed" $exit_code @@ -556,88 +348,39 @@ enhanced_error_handler() { trap 'enhanced_error_handler' ERR -# Advanced operations with API reporting +# POST → create record post_to_api + # ... operations ... -post_update_to_api "success" 0 + +# PATCH → finalize +post_update_to_api "done" 0 ``` ## Error Handling Integration ### Automatic Error Reporting -- **Error Descriptions**: Provides human-readable error messages -- **API Integration**: Reports errors to community-scripts.org API -- **Error Tracking**: Tracks error patterns for project improvement -- **Diagnostic Data**: Contributes to anonymous usage analytics - -### 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 +- **Error Descriptions**: `explain_exit_code()` provides human-readable messages for all recognized exit codes +- **PocketBase Integration**: Errors are recorded via PATCH with `status`, `error`, and `exit_code` fields +- **Error Tracking**: Anonymous telemetry helps track common failure patterns +- **Diagnostic Data**: Contributes to project-wide analytics without PII ### API Communication Errors -- **Network Failures**: Handle API communication failures gracefully -- **Missing Prerequisites**: Check prerequisites before API calls -- **Duplicate Prevention**: Prevent duplicate status updates -- **Error Recovery**: Handle API errors without blocking installation +- **Network Failures**: All API calls use `|| true` — failures are swallowed silently +- **Missing Prerequisites**: Functions return early if curl, DIAGNOSTICS, or UUID are missing +- **Duplicate Prevention**: `POST_UPDATE_DONE` flag ensures only one PATCH per session +- **Record Lookup Fallback**: If `PB_RECORD_ID` is unset, a GET filter query resolves the record ## Performance Considerations ### API Communication Overhead -- **Minimal Impact**: API calls add minimal overhead -- **Asynchronous**: API calls don't block installation process -- **Error Handling**: API failures don't affect installation -- **Optional**: API reporting is optional and can be disabled +- **Minimal Impact**: Only 2 HTTP calls per installation (1 POST + 1 PATCH) +- **Non-blocking**: API failures never block the installation process +- **Fire-and-forget**: curl stderr is suppressed (`2>/dev/null`) +- **Optional**: Telemetry is entirely opt-in via `DIAGNOSTICS` setting -### Memory Usage -- **Minimal Footprint**: API functions use minimal memory -- **Variable Reuse**: Global variables reused across functions -- **No Memory Leaks**: Proper cleanup prevents memory leaks -- **Efficient Processing**: Efficient JSON payload creation - -### 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 +### Security Considerations +- **Anonymous**: No personal data is transmitted — only system specs and app names +- **No Auth Required**: PocketBase collection rules allow anonymous create/update +- **User Control**: Users can disable telemetry by setting `DIAGNOSTICS=no` +- **HTTP**: API uses HTTP (not HTTPS) for compatibility with minimal containers diff --git a/docs/misc/api.func/API_USAGE_EXAMPLES.md b/docs/misc/api.func/API_USAGE_EXAMPLES.md deleted file mode 100644 index 616ebc927..000000000 --- a/docs/misc/api.func/API_USAGE_EXAMPLES.md +++ /dev/null @@ -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" -``` diff --git a/docs/misc/api.func/README.md b/docs/misc/api.func/README.md index 6cf90d23d..7bd39218a 100644 --- a/docs/misc/api.func/README.md +++ b/docs/misc/api.func/README.md @@ -2,22 +2,27 @@ ## 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 -- **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 -- **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 - **Analytics**: Contribute anonymous usage data for project improvement ## Quick Reference ### Key Function Groups -- **Error Handling**: `get_error_description()` - Convert exit codes to human-readable messages -- **API Communication**: `post_to_api()`, `post_to_api_vm()` - Send installation data -- **Status Updates**: `post_update_to_api()` - Report installation completion status +- **Error Handling**: `explain_exit_code()` - Convert exit codes to human-readable messages +- **API Communication**: `post_to_api()`, `post_to_api_vm()` - Send installation data to PocketBase +- **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 - **External**: `curl` command for HTTP requests @@ -26,7 +31,7 @@ The `api.func` file provides Proxmox API integration and diagnostic reporting fu ### Integration Points - Used by: All installation scripts for diagnostic reporting - 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 @@ -44,17 +49,18 @@ How api.func integrates with other components and provides API services. ## Key Features -### Error Code Descriptions -- **Comprehensive Coverage**: 50+ error codes with detailed explanations -- **LXC-Specific Errors**: Container creation and management errors -- **System Errors**: General system and network errors +### Exit Code Descriptions +- **Canonical source**: Single authoritative `explain_exit_code()` for the entire project +- **Non-overlapping ranges**: Clean separation between error categories +- **Comprehensive Coverage**: 60+ error codes with detailed explanations +- **System Errors**: General system, curl, and network errors - **Signal Errors**: Process termination and signal errors -### API Communication -- **LXC Reporting**: Send LXC container installation data -- **VM Reporting**: Send VM installation data -- **Status Updates**: Report installation success/failure -- **Diagnostic Data**: Anonymous usage analytics +### PocketBase Integration +- **Record Creation**: POST to create telemetry records with status `installing` +- **Record Updates**: PATCH to update with final status, exit code, and error +- **ID Tracking**: Stores `PB_RECORD_ID` for efficient updates +- **Fallback Lookup**: Searches by `random_id` filter if record ID is lost ### Diagnostic Integration - **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 ```bash #!/usr/bin/env bash -# Basic API setup - source api.func # Set up diagnostic reporting 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 ``` @@ -85,9 +89,9 @@ post_to_api source api.func # Get error description -error_msg=$(get_error_description 127) -echo "Error 127: $error_msg" -# Output: Error 127: Command not found: Incorrect path or missing dependency. +error_msg=$(explain_exit_code 137) +echo "Error 137: $error_msg" +# Output: Error 137: Killed (SIGKILL / Out of memory?) ``` ### Status Updates @@ -96,9 +100,9 @@ echo "Error 127: $error_msg" source api.func # 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 ``` @@ -106,7 +110,7 @@ post_update_to_api "failed" 127 ### Required Variables - `DIAGNOSTICS`: Enable/disable diagnostic reporting ("yes"/"no") -- `RANDOM_UUID`: Unique identifier for tracking +- `RANDOM_UUID`: Unique identifier for session tracking ### Optional Variables - `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 - `var_os`: Operating system type - `var_version`: OS version -- `DISABLEIP6`: IPv6 disable setting -- `NSAPP`: Namespace application name +- `NSAPP`: Application name - `METHOD`: Installation method ### Internal Variables - `POST_UPDATE_DONE`: Prevents duplicate status updates -- `API_URL`: Community scripts API endpoint -- `JSON_PAYLOAD`: API request payload -- `RESPONSE`: API response +- `PB_URL`: PocketBase base URL +- `PB_API_URL`: Full API endpoint URL +- `PB_RECORD_ID`: Stored PocketBase record ID for updates -## Error Code Categories +## Error Code Categories (Non-Overlapping Ranges) -### General System Errors -- **0-9**: Basic system errors -- **18, 22, 28, 35**: Network and I/O errors -- **56, 60**: TLS/SSL errors -- **125-128**: Command execution errors -- **129-143**: Signal errors -- **152**: Resource limit errors -- **255**: Unknown critical errors - -### LXC-Specific Errors -- **100-101**: LXC installation errors -- **200-209**: LXC creation and management errors - -### Docker Errors -- **125**: Docker container start errors +| Range | Category | +|-------|----------| +| 1-2 | Generic shell errors | +| 6-35 | curl/wget network errors | +| 100-102 | APT/DPKG package errors | +| 124-143 | Command execution & signal errors | +| 150-154 | Systemd/service errors | +| 160-162 | Python/pip/uv errors | +| 170-173 | PostgreSQL errors | +| 180-183 | MySQL/MariaDB errors | +| 190-193 | MongoDB errors | +| 200-231 | Proxmox custom codes | +| 243-249 | Node.js/npm errors | +| 255 | DPKG fatal error | ## Best Practices @@ -152,48 +154,56 @@ post_update_to_api "failed" 127 4. Report both success and failure cases ### Error Handling -1. Use appropriate error codes -2. Provide meaningful error descriptions +1. Use the correct non-overlapping exit code ranges +2. Use `explain_exit_code()` from api.func (canonical source) 3. Handle API communication failures gracefully 4. Don't block installation on API failures ### API Usage -1. Check for curl availability -2. Handle network failures gracefully -3. Use appropriate HTTP methods -4. Include all required data +1. Check for curl availability before API calls +2. Handle network failures gracefully (all calls use `|| true`) +3. Store and reuse PB_RECORD_ID for updates +4. Use proper PocketBase REST methods (POST for create, PATCH for update) ## Troubleshooting ### Common Issues 1. **API Communication Fails**: Check network connectivity and curl availability -2. **Diagnostics Not Working**: Verify DIAGNOSTICS setting and RANDOM_UUID -3. **Missing Error Descriptions**: Check error code coverage -4. **Duplicate Updates**: POST_UPDATE_DONE prevents duplicates +2. **Diagnostics Not Working**: Verify `DIAGNOSTICS=yes` in `/usr/local/community-scripts/diagnostics` +3. **Status Update Fails**: Check that `PB_RECORD_ID` was captured or `random_id` filter works +4. **Duplicate Updates**: `POST_UPDATE_DONE` flag prevents duplicates ### Debug Mode Enable diagnostic reporting for debugging: ```bash export DIAGNOSTICS="yes" -export RANDOM_UUID="$(uuidgen)" +export RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" ``` ### API Testing -Test API communication: +Test PocketBase connectivity: +```bash +curl -s http://db.community-scripts.org/api/health +``` + +Test record creation: ```bash source api.func export DIAGNOSTICS="yes" export RANDOM_UUID="test-$(date +%s)" +export NSAPP="test" +export CT_TYPE=1 post_to_api +echo "Record ID: $PB_RECORD_ID" ``` ## Related Documentation -- [core.func](../core.func/) - Core utilities and error handling -- [error_handler.func](../error_handler.func/) - Error handling utilities +- [core.func](../core.func/) - Core utilities +- [error_handler.func](../error_handler.func/) - Error handling (fallback `explain_exit_code`) - [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.* diff --git a/misc/api.func b/misc/api.func index 657d786ba..4c323a954 100644 --- a/misc/api.func +++ b/misc/api.func @@ -3,11 +3,11 @@ # 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 -# API for analytics and diagnostics purposes. +# Provides functions for sending anonymous telemetry data to PocketBase +# backend at db.community-scripts.org for analytics and diagnostics. # # Features: # - Container/VM creation statistics @@ -18,6 +18,7 @@ # Usage: # source <(curl -fsSL .../api.func) # post_to_api # Report container creation +# post_to_api_vm # Report VM creation # post_update_to_api # Report installation status # # 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 # ============================================================================== @@ -35,6 +46,8 @@ # explain_exit_code() # # - 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: # * Generic/Shell errors (1, 2, 124, 126-130, 134, 137, 139, 141, 143) # * curl/wget errors (6, 7, 22, 28, 35) @@ -47,7 +60,6 @@ # * Proxmox custom codes (200-231) # * Node.js/npm errors (243, 245-249) # - Returns description string for given exit code -# - Shared function with error_handler.func for consistency # ------------------------------------------------------------------------------ explain_exit_code() { local code="$1" @@ -160,7 +172,8 @@ explain_exit_code() { # ------------------------------------------------------------------------------ # 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: # * curl is available # * DIAGNOSTICS=yes @@ -168,62 +181,71 @@ explain_exit_code() { # - Payload includes: # * Container type, disk size, CPU cores, RAM # * OS type and version -# * IPv6 disable status # * Application name (NSAPP) # * Installation method # * PVE version # * Status: "installing" # * Random UUID for session tracking +# - Stores PB_RECORD_ID for later updates # - Anonymous telemetry (no personal data) # ------------------------------------------------------------------------------ post_to_api() { - if ! command -v curl &>/dev/null; then return fi - if [ "$DIAGNOSTICS" = "no" ]; then + if [[ "${DIAGNOSTICS:-no}" == "no" ]]; then return fi - if [ -z "$RANDOM_UUID" ]; then + if [[ -z "${RANDOM_UUID:-}" ]]; then return fi - local API_URL="http://api.community-scripts.org/dev/upload" 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=$( cat </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() # -# - 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) # - Reads DIAGNOSTICS from /usr/local/community-scripts/diagnostics file # - Payload differences: @@ -233,66 +255,78 @@ EOF # - Only executes if DIAGNOSTICS=yes and RANDOM_UUID is set # ------------------------------------------------------------------------------ post_to_api_vm() { - if [[ ! -f /usr/local/community-scripts/diagnostics ]]; then return fi DIAGNOSTICS=$(grep -i "^DIAGNOSTICS=" /usr/local/community-scripts/diagnostics | awk -F'=' '{print $2}') + if ! command -v curl &>/dev/null; then return fi - if [ "$DIAGNOSTICS" = "no" ]; then + if [[ "${DIAGNOSTICS:-no}" == "no" ]]; then return fi - if [ -z "$RANDOM_UUID" ]; then + if [[ -z "${RANDOM_UUID:-}" ]]; then return fi - local API_URL="http://api.community-scripts.org/dev/upload" 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=$( cat </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_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 # - Arguments: -# * $1: status ("success" or "failed") -# * $2: exit_code (default: 1 for failed, 0 for success) +# * $1: status ("done" or "failed") +# * $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: -# * Final status (success/failed) -# * Error description via get_error_description() -# * Random UUID for session correlation +# * Final status (mapped: "done"→"sucess", "failed"→"failed") +# * Error description via explain_exit_code() +# * Numeric exit code # - Only executes once per session # - Silently returns if: # * curl not available @@ -300,7 +334,6 @@ EOF # * DIAGNOSTICS=no # ------------------------------------------------------------------------------ post_update_to_api() { - if ! command -v curl &>/dev/null; then return fi @@ -308,42 +341,79 @@ post_update_to_api() { # Initialize flag if not set (prevents 'unbound variable' error with set -u) POST_UPDATE_DONE=${POST_UPDATE_DONE:-false} - if [ "$POST_UPDATE_DONE" = true ]; then + if [[ "$POST_UPDATE_DONE" == "true" ]]; then return 0 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}" - if [[ "$status" == "failed" ]]; then - local exit_code="${2:-1}" - elif [[ "$status" == "success" ]]; then - local exit_code="${2:-0}" + local raw_exit_code="${2:-1}" + local exit_code error pb_status + + # 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 - if [[ -z "$exit_code" ]]; then - exit_code=1 - fi - - error=$(explain_exit_code "$exit_code") - - if [ -z "$error" ]; then - error="Unknown error" + # Resolve PocketBase record ID if not already known + local record_id="${PB_RECORD_ID:-}" + + if [[ -z "$record_id" ]]; then + # Look up record by random_id filter + local lookup_url="${PB_API_URL}?filter=(random_id='${RANDOM_UUID}')&fields=id&perPage=1" + local lookup_response + 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 + local JSON_PAYLOAD JSON_PAYLOAD=$( cat </dev/null || true POST_UPDATE_DONE=true } diff --git a/misc/build.func b/misc/build.func index 6a73e688c..ff6908d3a 100644 --- a/misc/build.func +++ b/misc/build.func @@ -5157,9 +5157,9 @@ EOF # api_exit_script() # # - Exit trap handler for reporting to API telemetry -# - Captures exit code and reports to API using centralized error descriptions -# - Uses explain_exit_code() from error_handler.func for consistent error messages -# - Posts failure status with exit code to API (error description added automatically) +# - Captures exit code and reports to PocketBase using centralized error descriptions +# - Uses explain_exit_code() from api.func for consistent error messages +# - Posts failure status with exit code to API (error description resolved automatically) # - Only executes on non-zero exit codes # ------------------------------------------------------------------------------ api_exit_script() { @@ -5172,6 +5172,6 @@ api_exit_script() { if command -v pveversion >/dev/null 2>&1; then trap 'api_exit_script' EXIT fi -trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR -trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT -trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM +trap 'post_update_to_api "failed" "$?"' ERR +trap 'post_update_to_api "failed" "130"' SIGINT +trap 'post_update_to_api "failed" "143"' SIGTERM diff --git a/misc/error_handler.func b/misc/error_handler.func index 9af40f0fa..7fec71817 100644 --- a/misc/error_handler.func +++ b/misc/error_handler.func @@ -27,100 +27,90 @@ # ------------------------------------------------------------------------------ # explain_exit_code() # -# - Maps numeric exit codes to human-readable error descriptions -# - Supports: -# * Generic/Shell errors (1, 2, 126, 127, 128, 130, 137, 139, 143) -# * 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 +# - Canonical version is defined in api.func (sourced before this file) +# - This section only provides a fallback if api.func was not loaded +# - See api.func SECTION 1 for the authoritative exit code mappings # ------------------------------------------------------------------------------ -explain_exit_code() { - local code="$1" - case "$code" in - # --- Generic / Shell --- - 1) echo "General error / Operation not permitted" ;; - 2) echo "Misuse of shell builtins (e.g. syntax error)" ;; - 126) echo "Command invoked cannot execute (permission problem?)" ;; - 127) echo "Command not found" ;; - 128) echo "Invalid argument to exit" ;; - 130) echo "Terminated by Ctrl+C (SIGINT)" ;; - 137) echo "Killed (SIGKILL / Out of memory?)" ;; - 139) echo "Segmentation fault (core dumped)" ;; - 143) echo "Terminated (SIGTERM)" ;; - - # --- Package manager / APT / DPKG --- - 100) echo "APT: Package manager error (broken packages / dependency problems)" ;; - 101) echo "APT: Configuration error (bad sources.list, malformed config)" ;; - 255) echo "DPKG: Fatal internal error" ;; - - # --- Node.js / npm / pnpm / yarn --- - 243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;; - 245) echo "Node.js: Invalid command-line option" ;; - 246) echo "Node.js: Internal JavaScript Parse Error" ;; - 247) echo "Node.js: Fatal internal error" ;; - 248) echo "Node.js: Invalid C++ addon / N-API failure" ;; - 249) echo "Node.js: Inspector error" ;; - 254) echo "npm/pnpm/yarn: Unknown fatal error" ;; - - # --- Python / pip / uv --- - 210) echo "Python: Virtualenv / uv environment missing or broken" ;; - 211) echo "Python: Dependency resolution failed" ;; - 212) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;; - - # --- PostgreSQL --- - 231) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;; - 232) echo "PostgreSQL: Authentication failed (bad user/password)" ;; - 233) echo "PostgreSQL: Database does not exist" ;; - 234) echo "PostgreSQL: Fatal error in query / syntax" ;; - - # --- MySQL / MariaDB --- - 241) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;; - 242) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;; - 243) echo "MySQL/MariaDB: Database does not exist" ;; - 244) echo "MySQL/MariaDB: Fatal error in query / syntax" ;; - - # --- MongoDB --- - 251) echo "MongoDB: Connection failed (server not running)" ;; - 252) echo "MongoDB: Authentication failed (bad user/password)" ;; - 253) echo "MongoDB: Database not found" ;; - 254) echo "MongoDB: Fatal query error" ;; - - # --- Proxmox Custom Codes --- - 200) echo "Proxmox: Failed to create lock file" ;; - 203) echo "Proxmox: Missing CTID variable" ;; - 204) echo "Proxmox: Missing PCT_OSTYPE variable" ;; - 205) echo "Proxmox: Invalid CTID (<100)" ;; - 206) echo "Proxmox: CTID already in use" ;; - 207) echo "Proxmox: Password contains unescaped special characters" ;; - 208) echo "Proxmox: Invalid configuration (DNS/MAC/Network format)" ;; - 209) echo "Proxmox: Container creation failed" ;; - 210) echo "Proxmox: Cluster not quorate" ;; - 211) echo "Proxmox: Timeout waiting for template lock" ;; - 212) echo "Proxmox: Storage type 'iscsidirect' does not support containers (VMs only)" ;; - 213) echo "Proxmox: Storage type does not support 'rootdir' content" ;; - 214) echo "Proxmox: Not enough storage space" ;; - 215) echo "Proxmox: Container created but not listed (ghost state)" ;; - 216) echo "Proxmox: RootFS entry missing in config" ;; - 217) echo "Proxmox: Storage not accessible" ;; - 219) echo "Proxmox: CephFS does not support containers - use RBD" ;; - 224) echo "Proxmox: PBS storage is for backups only" ;; - 218) echo "Proxmox: Template file corrupted or incomplete" ;; - 220) echo "Proxmox: Unable to resolve template path" ;; - 221) echo "Proxmox: Template file not readable" ;; - 222) echo "Proxmox: Template download failed" ;; - 223) echo "Proxmox: Template not available after download" ;; - 225) echo "Proxmox: No template available for OS/Version" ;; - 231) echo "Proxmox: LXC stack upgrade failed" ;; - - # --- Default --- - *) echo "Unknown error" ;; - esac -} +if ! declare -f explain_exit_code &>/dev/null; then + explain_exit_code() { + local code="$1" + case "$code" in + 1) echo "General error / Operation not permitted" ;; + 2) echo "Misuse of shell builtins (e.g. syntax error)" ;; + 6) echo "curl: DNS resolution failed (could not resolve host)" ;; + 7) echo "curl: Failed to connect (network unreachable / host down)" ;; + 22) echo "curl: HTTP error returned (404, 429, 500+)" ;; + 28) echo "curl: Operation timeout (network slow or server not responding)" ;; + 35) echo "curl: SSL/TLS handshake failed (certificate error)" ;; + 100) echo "APT: Package manager error (broken packages / dependency problems)" ;; + 101) echo "APT: Configuration error (bad sources.list, malformed config)" ;; + 102) echo "APT: Lock held by another process (dpkg/apt still running)" ;; + 124) echo "Command timed out (timeout command)" ;; + 126) echo "Command invoked cannot execute (permission problem?)" ;; + 127) echo "Command not found" ;; + 128) echo "Invalid argument to exit" ;; + 130) echo "Terminated by Ctrl+C (SIGINT)" ;; + 134) echo "Process aborted (SIGABRT - possibly Node.js heap overflow)" ;; + 137) echo "Killed (SIGKILL / Out of memory?)" ;; + 139) echo "Segmentation fault (core dumped)" ;; + 141) echo "Broken pipe (SIGPIPE - output closed prematurely)" ;; + 143) echo "Terminated (SIGTERM)" ;; + 150) echo "Systemd: Service failed to start" ;; + 151) echo "Systemd: Service unit not found" ;; + 152) echo "Permission denied (EACCES)" ;; + 153) echo "Build/compile failed (make/gcc/cmake)" ;; + 154) echo "Node.js: Native addon build failed (node-gyp)" ;; + 160) echo "Python: Virtualenv / uv environment missing or broken" ;; + 161) echo "Python: Dependency resolution failed" ;; + 162) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;; + 170) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;; + 171) echo "PostgreSQL: Authentication failed (bad user/password)" ;; + 172) echo "PostgreSQL: Database does not exist" ;; + 173) echo "PostgreSQL: Fatal error in query / syntax" ;; + 180) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;; + 181) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;; + 182) echo "MySQL/MariaDB: Database does not exist" ;; + 183) echo "MySQL/MariaDB: Fatal error in query / syntax" ;; + 190) echo "MongoDB: Connection failed (server not running)" ;; + 191) echo "MongoDB: Authentication failed (bad user/password)" ;; + 192) echo "MongoDB: Database not found" ;; + 193) echo "MongoDB: Fatal query error" ;; + 200) echo "Proxmox: Failed to create lock file" ;; + 203) echo "Proxmox: Missing CTID variable" ;; + 204) echo "Proxmox: Missing PCT_OSTYPE variable" ;; + 205) echo "Proxmox: Invalid CTID (<100)" ;; + 206) echo "Proxmox: CTID already in use" ;; + 207) echo "Proxmox: Password contains unescaped special characters" ;; + 208) echo "Proxmox: Invalid configuration (DNS/MAC/Network format)" ;; + 209) echo "Proxmox: Container creation failed" ;; + 210) echo "Proxmox: Cluster not quorate" ;; + 211) echo "Proxmox: Timeout waiting for template lock" ;; + 212) echo "Proxmox: Storage type 'iscsidirect' does not support containers (VMs only)" ;; + 213) echo "Proxmox: Storage type does not support 'rootdir' content" ;; + 214) echo "Proxmox: Not enough storage space" ;; + 215) echo "Proxmox: Container created but not listed (ghost state)" ;; + 216) echo "Proxmox: RootFS entry missing in config" ;; + 217) echo "Proxmox: Storage not accessible" ;; + 218) echo "Proxmox: Template file corrupted or incomplete" ;; + 219) echo "Proxmox: CephFS does not support containers - use RBD" ;; + 220) echo "Proxmox: Unable to resolve template path" ;; + 221) echo "Proxmox: Template file not readable" ;; + 222) echo "Proxmox: Template download failed" ;; + 223) echo "Proxmox: Template not available after download" ;; + 224) echo "Proxmox: PBS storage is for backups only" ;; + 225) echo "Proxmox: No template available for OS/Version" ;; + 231) echo "Proxmox: LXC stack upgrade failed" ;; + 243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;; + 245) echo "Node.js: Invalid command-line option" ;; + 246) echo "Node.js: Internal JavaScript Parse Error" ;; + 247) echo "Node.js: Fatal internal error" ;; + 248) echo "Node.js: Invalid C++ addon / N-API failure" ;; + 249) echo "npm/pnpm/yarn: Unknown fatal error" ;; + 255) echo "DPKG: Fatal internal error" ;; + *) echo "Unknown error" ;; + esac + } +fi # ============================================================================== # SECTION 2: ERROR HANDLERS diff --git a/misc/ingest.go b/misc/ingest.go new file mode 100644 index 000000000..e69de29bb