Replace Go API with PocketBase; update docs

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

View File

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

View File

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

View File

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

View File

@ -1,450 +0,0 @@
// Copyright (c) 2021-2025 community-scripts ORG
// Author: Michel Roegl-Brunner (michelroegl-brunner)
// License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"strconv"
"time"
"github.com/gorilla/mux"
"github.com/joho/godotenv"
"github.com/rs/cors"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var client *mongo.Client
var collection *mongo.Collection
func loadEnv() {
if err := godotenv.Load(); err != nil {
log.Fatal("Error loading .env file")
}
}
// DataModel represents a single document in MongoDB
type DataModel struct {
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"`
CT_TYPE uint `json:"ct_type" bson:"ct_type"`
DISK_SIZE float32 `json:"disk_size" bson:"disk_size"`
CORE_COUNT uint `json:"core_count" bson:"core_count"`
RAM_SIZE uint `json:"ram_size" bson:"ram_size"`
OS_TYPE string `json:"os_type" bson:"os_type"`
OS_VERSION string `json:"os_version" bson:"os_version"`
DISABLEIP6 string `json:"disableip6" bson:"disableip6"`
NSAPP string `json:"nsapp" bson:"nsapp"`
METHOD string `json:"method" bson:"method"`
CreatedAt time.Time `json:"created_at" bson:"created_at"`
PVEVERSION string `json:"pve_version" bson:"pve_version"`
STATUS string `json:"status" bson:"status"`
RANDOM_ID string `json:"random_id" bson:"random_id"`
TYPE string `json:"type" bson:"type"`
ERROR string `json:"error" bson:"error"`
}
type StatusModel struct {
RANDOM_ID string `json:"random_id" bson:"random_id"`
ERROR string `json:"error" bson:"error"`
STATUS string `json:"status" bson:"status"`
}
type CountResponse struct {
TotalEntries int64 `json:"total_entries"`
StatusCount map[string]int64 `json:"status_count"`
NSAPPCount map[string]int64 `json:"nsapp_count"`
}
// ConnectDatabase initializes the MongoDB connection
func ConnectDatabase() {
loadEnv()
mongoURI := fmt.Sprintf("mongodb://%s:%s@%s:%s",
os.Getenv("MONGO_USER"),
os.Getenv("MONGO_PASSWORD"),
os.Getenv("MONGO_IP"),
os.Getenv("MONGO_PORT"))
database := os.Getenv("MONGO_DATABASE")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
var err error
client, err = mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
if err != nil {
log.Fatal("Failed to connect to MongoDB!", err)
}
collection = client.Database(database).Collection("data_models")
fmt.Println("Connected to MongoDB on 10.10.10.18")
}
// UploadJSON handles API requests and stores data as a document in MongoDB
func UploadJSON(w http.ResponseWriter, r *http.Request) {
var input DataModel
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
input.CreatedAt = time.Now()
_, err := collection.InsertOne(context.Background(), input)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
log.Println("Received data:", input)
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(map[string]string{"message": "Data saved successfully"})
}
// UpdateStatus updates the status of a record based on RANDOM_ID
func UpdateStatus(w http.ResponseWriter, r *http.Request) {
var input StatusModel
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
filter := bson.M{"random_id": input.RANDOM_ID}
update := bson.M{"$set": bson.M{"status": input.STATUS, "error": input.ERROR}}
_, err := collection.UpdateOne(context.Background(), filter, update)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
log.Println("Updated data:", input)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{"message": "Record updated successfully"})
}
// GetDataJSON fetches all data from MongoDB
func GetDataJSON(w http.ResponseWriter, r *http.Request) {
var records []DataModel
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
cursor, err := collection.Find(ctx, bson.M{})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var record DataModel
if err := cursor.Decode(&record); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
records = append(records, record)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(records)
}
func GetPaginatedData(w http.ResponseWriter, r *http.Request) {
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
if page < 1 {
page = 1
}
if limit < 1 {
limit = 10
}
skip := (page - 1) * limit
var records []DataModel
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
options := options.Find().SetSkip(int64(skip)).SetLimit(int64(limit))
cursor, err := collection.Find(ctx, bson.M{}, options)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var record DataModel
if err := cursor.Decode(&record); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
records = append(records, record)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(records)
}
func GetSummary(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
totalCount, err := collection.CountDocuments(ctx, bson.M{})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
statusCount := make(map[string]int64)
nsappCount := make(map[string]int64)
pipeline := []bson.M{
{"$group": bson.M{"_id": "$status", "count": bson.M{"$sum": 1}}},
}
cursor, err := collection.Aggregate(ctx, pipeline)
if err == nil {
for cursor.Next(ctx) {
var result struct {
ID string `bson:"_id"`
Count int64 `bson:"count"`
}
if err := cursor.Decode(&result); err == nil {
statusCount[result.ID] = result.Count
}
}
}
pipeline = []bson.M{
{"$group": bson.M{"_id": "$nsapp", "count": bson.M{"$sum": 1}}},
}
cursor, err = collection.Aggregate(ctx, pipeline)
if err == nil {
for cursor.Next(ctx) {
var result struct {
ID string `bson:"_id"`
Count int64 `bson:"count"`
}
if err := cursor.Decode(&result); err == nil {
nsappCount[result.ID] = result.Count
}
}
}
response := CountResponse{
TotalEntries: totalCount,
StatusCount: statusCount,
NSAPPCount: nsappCount,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
func GetByNsapp(w http.ResponseWriter, r *http.Request) {
nsapp := r.URL.Query().Get("nsapp")
var records []DataModel
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
cursor, err := collection.Find(ctx, bson.M{"nsapp": nsapp})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var record DataModel
if err := cursor.Decode(&record); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
records = append(records, record)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(records)
}
func GetByDateRange(w http.ResponseWriter, r *http.Request) {
startDate := r.URL.Query().Get("start_date")
endDate := r.URL.Query().Get("end_date")
if startDate == "" || endDate == "" {
http.Error(w, "Both start_date and end_date are required", http.StatusBadRequest)
return
}
start, err := time.Parse("2006-01-02T15:04:05.999999+00:00", startDate+"T00:00:00+00:00")
if err != nil {
http.Error(w, "Invalid start_date format", http.StatusBadRequest)
return
}
end, err := time.Parse("2006-01-02T15:04:05.999999+00:00", endDate+"T23:59:59+00:00")
if err != nil {
http.Error(w, "Invalid end_date format", http.StatusBadRequest)
return
}
var records []DataModel
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
cursor, err := collection.Find(ctx, bson.M{
"created_at": bson.M{
"$gte": start,
"$lte": end,
},
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var record DataModel
if err := cursor.Decode(&record); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
records = append(records, record)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(records)
}
func GetByStatus(w http.ResponseWriter, r *http.Request) {
status := r.URL.Query().Get("status")
var records []DataModel
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
cursor, err := collection.Find(ctx, bson.M{"status": status})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var record DataModel
if err := cursor.Decode(&record); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
records = append(records, record)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(records)
}
func GetByOS(w http.ResponseWriter, r *http.Request) {
osType := r.URL.Query().Get("os_type")
osVersion := r.URL.Query().Get("os_version")
var records []DataModel
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
cursor, err := collection.Find(ctx, bson.M{"os_type": osType, "os_version": osVersion})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var record DataModel
if err := cursor.Decode(&record); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
records = append(records, record)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(records)
}
func GetErrors(w http.ResponseWriter, r *http.Request) {
errorCount := make(map[string]int)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
cursor, err := collection.Find(ctx, bson.M{"error": bson.M{"$ne": ""}})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var record DataModel
if err := cursor.Decode(&record); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if record.ERROR != "" {
errorCount[record.ERROR]++
}
}
type ErrorCountResponse struct {
Error string `json:"error"`
Count int `json:"count"`
}
var errorCounts []ErrorCountResponse
for err, count := range errorCount {
errorCounts = append(errorCounts, ErrorCountResponse{
Error: err,
Count: count,
})
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(struct {
ErrorCounts []ErrorCountResponse `json:"error_counts"`
}{
ErrorCounts: errorCounts,
})
}
func main() {
ConnectDatabase()
router := mux.NewRouter()
router.HandleFunc("/upload", UploadJSON).Methods("POST")
router.HandleFunc("/upload/updatestatus", UpdateStatus).Methods("POST")
router.HandleFunc("/data/json", GetDataJSON).Methods("GET")
router.HandleFunc("/data/paginated", GetPaginatedData).Methods("GET")
router.HandleFunc("/data/summary", GetSummary).Methods("GET")
router.HandleFunc("/data/nsapp", GetByNsapp).Methods("GET")
router.HandleFunc("/data/date", GetByDateRange).Methods("GET")
router.HandleFunc("/data/status", GetByStatus).Methods("GET")
router.HandleFunc("/data/os", GetByOS).Methods("GET")
router.HandleFunc("/data/errors", GetErrors).Methods("GET")
c := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST"},
AllowedHeaders: []string{"Content-Type", "Authorization"},
AllowCredentials: true,
})
handler := c.Handler(router)
fmt.Println("Server running on port 8080")
log.Fatal(http.ListenAndServe(":8080", handler))
}

View File

@ -1,38 +1,30 @@
# API Integration Documentation (/api)
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

View File

@ -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 | <numeric>
│ } │
│ │
│ ──► POST_UPDATE_DONE = true (prevents future calls) │
└─────────────────────────────────────────────────────────────────────────────────┘
```
## Error Description Flow — `explain_exit_code()`
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ explain_exit_code(code) Flow │
│ Convert numeric exit codes to human-readable descriptions │
│ Canonical function — used by api.func AND error_handler.func │
└─────────────────────┬───────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Exit Code Classification (non-overlapping ranges) │
│ │
│ ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ Generic/Shell │ │ curl/wget │ │ APT/DPKG │ │
│ │ 12 │ │ 6, 7, 22, 28, 35│ │ 100102, 255 │ │
│ └─────────────────┘ └──────────────────┘ └──────────────────────────┘ │
│ │
│ ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ System/Signals │ │ Systemd/Service │ │ Python/pip/uv │ │
│ │ 124143 │ │ 150154 │ │ 160162 │ │
│ └─────────────────┘ └──────────────────┘ └──────────────────────────┘ │
│ │
│ ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ PostgreSQL │ │ MySQL/MariaDB │ │ MongoDB │ │
│ │ 170173 │ │ 180183 │ │ 190193 │ │
│ └─────────────────┘ └──────────────────┘ └──────────────────────────┘ │
│ │
│ ┌─────────────────┐ ┌──────────────────┐ │
│ │ Proxmox │ │ Node.js/npm │ │
│ │ 200231 │ │ 243249 │ │
│ └─────────────────┘ └──────────────────┘ │
└─────────────────────┬───────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐
│ case "$code" in │
<matched>) echo "<description>" ;; │
│ *) echo "Unknown error" ;; │
│ esac │
└─────────────────────────────────────────────────────────────────────────────────┘
```
## Complete Installation Lifecycle
```
┌──────────────────────────────────────────────────────────────┐
│ Installation Script (e.g. build.func / vm-core.func) │
└────────┬─────────────────────────────────────────────────────┘
│ 1. source api.func
│ 2. Set DIAGNOSTICS, RANDOM_UUID, NSAPP, etc.
┌──────────────────────────────────────────────────────────────┐
│ post_to_api() / post_to_api_vm() │
│ │
│ POST → PB_API_URL │
│ Body: { ..., "status": "installing", "random_id": "..." } │
│ │
│ Response → PB_RECORD_ID = "abc123def456789" │
└────────┬─────────────────────────────────────────────────────┘
│ 3. Installation proceeds...
│ (container/VM creation, package install, etc.)
┌──────────────────────────────────────────────────────────────┐
│ post_update_to_api("done", 0) │
│ or │
│ post_update_to_api("failed", $exit_code) │
│ │
│ PATCH → PB_API_URL/{PB_RECORD_ID} │
│ Body: { "status": "sucess", "error": "", "exit_code": 0 } │
│ or { "status": "failed", "error": "...", "exit_code": N }│
│ │
│ POST_UPDATE_DONE = true │
└──────────────────────────────────────────────────────────────┘
```
## Integration Points
### 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)

View File

@ -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 |
|-------|----------|
| 12 | Generic / Shell |
| 635 | curl / wget |
| 100102 | APT / Package manager |
| 124143 | System / Signals |
| 150154 | Systemd / Service |
| 160162 | Python / pip / uv |
| 170173 | PostgreSQL |
| 180183 | MySQL / MariaDB |
| 190193 | MongoDB |
| 200231 | Proxmox custom codes |
| 243249 | Node.js / npm |
| 255 | DPKG fatal |
**Usage Example**:
```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='<RANDOM_UUID>')&fields=id&perPage=1
```
3. If no record is found, the function sets `POST_UPDATE_DONE=true` and returns.
**JSON Payload Structure**:
**Status Mapping** (PocketBase select field values: `installing`, `sucess`, `failed`, `unknown`):
| Input Status | PocketBase `status` | `exit_code` | `error` |
|---|---|---|---|
| `"done"` / `"success"` / `"sucess"` | `"sucess"` | `0` | `""` |
| `"failed"` | `"failed"` | *from $2* | *from `explain_exit_code()`* |
| anything else | `"unknown"` | *from $2* | *from `explain_exit_code()`* |
> **Note**: The PocketBase schema intentionally spells success as `"sucess"`.
**API Endpoint**: `PATCH http://db.community-scripts.org/api/collections/_dev_telemetry_data/records/{record_id}`
**JSON Payload**:
```json
{
"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 (12)
| 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 (635)
| 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 (100102)
| 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 (124143)
| 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 (150154)
| 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 (160162)
| Code | Description |
|------|-------------|
| 160 | Python: Virtualenv / uv environment missing or broken |
| 161 | Python: Dependency resolution failed |
| 162 | Python: Installation aborted (permissions or EXTERNALLY-MANAGED) |
### PostgreSQL (170173)
| Code | Description |
|------|-------------|
| 170 | PostgreSQL: Connection failed (server not running / wrong socket) |
| 171 | PostgreSQL: Authentication failed (bad user/password) |
| 172 | PostgreSQL: Database does not exist |
| 173 | PostgreSQL: Fatal error in query / syntax |
### MySQL / MariaDB (180183)
| Code | Description |
|------|-------------|
| 180 | MySQL/MariaDB: Connection failed (server not running / wrong socket) |
| 181 | MySQL/MariaDB: Authentication failed (bad user/password) |
| 182 | MySQL/MariaDB: Database does not exist |
| 183 | MySQL/MariaDB: Fatal error in query / syntax |
### MongoDB (190193)
| Code | Description |
|------|-------------|
| 190 | MongoDB: Connection failed (server not running) |
| 191 | MongoDB: Authentication failed (bad user/password) |
| 192 | MongoDB: Database not found |
| 193 | MongoDB: Fatal query error |
### Proxmox Custom Codes (200231)
| Code | Description |
|------|-------------|
| 200 | Proxmox: Failed to create lock file |
| 203 | Proxmox: Missing CTID variable |
| 204 | Proxmox: Missing PCT_OSTYPE variable |
| 205 | Proxmox: Invalid CTID (<100) |
| 206 | Proxmox: CTID already in use |
| 207 | Proxmox: Password contains unescaped special characters |
| 208 | Proxmox: Invalid configuration (DNS/MAC/Network format) |
| 209 | Proxmox: Container creation failed |
| 210 | Proxmox: Cluster not quorate |
| 211 | Proxmox: Timeout waiting for template lock |
| 212 | Proxmox: Storage type 'iscsidirect' does not support containers (VMs only) |
| 213 | Proxmox: Storage type does not support 'rootdir' content |
| 214 | Proxmox: Not enough storage space |
| 215 | Proxmox: Container created but not listed (ghost state) |
| 216 | Proxmox: RootFS entry missing in config |
| 217 | Proxmox: Storage not accessible |
| 218 | Proxmox: Template file corrupted or incomplete |
| 219 | Proxmox: CephFS does not support containers — use RBD |
| 220 | Proxmox: Unable to resolve template path |
| 221 | Proxmox: Template file not readable |
| 222 | Proxmox: Template download failed |
| 223 | Proxmox: Template not available after download |
| 224 | Proxmox: PBS storage is for backups only |
| 225 | Proxmox: No template available for OS/Version |
| 231 | Proxmox: LXC stack upgrade failed |
### Node.js / npm (243249)
| Code | Description |
|------|-------------|
| 243 | Node.js: Out of memory (JavaScript heap out of memory) |
| 245 | Node.js: Invalid command-line option |
| 246 | Node.js: Internal JavaScript Parse Error |
| 247 | Node.js: Fatal internal error |
| 248 | Node.js: Invalid C++ addon / N-API failure |
| 249 | npm/pnpm/yarn: Unknown fatal error |
### DPKG (255)
| Code | Description |
|------|-------------|
| 255 | DPKG: Fatal internal error |
### Default
| Code | Description |
|------|-------------|
| * | Unknown error |
## Environment Variable Dependencies
### 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

View File

@ -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

View File

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

View File

@ -2,22 +2,27 @@
## Overview
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.*

View File

@ -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 <<EOF
{
"ct_type": $CT_TYPE,
"type":"lxc",
"disk_size": $DISK_SIZE,
"core_count": $CORE_COUNT,
"ram_size": $RAM_SIZE,
"os_type": "$var_os",
"os_version": "$var_version",
"nsapp": "$NSAPP",
"method": "$METHOD",
"pve_version": "$pve_version",
"ct_type": ${CT_TYPE:-1},
"type": "lxc",
"disk_size": ${DISK_SIZE:-0},
"core_count": ${CORE_COUNT:-0},
"ram_size": ${RAM_SIZE:-0},
"os_type": "${var_os:-}",
"os_version": "${var_version:-}",
"nsapp": "${NSAPP:-}",
"method": "${METHOD:-default}",
"pve_version": "${pve_version}",
"status": "installing",
"random_id": "$RANDOM_UUID"
"random_id": "${RANDOM_UUID}"
}
EOF
)
if [[ "$DIAGNOSTICS" == "yes" ]]; then
RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \
-H "Content-Type: application/json" \
-d "$JSON_PAYLOAD") || true
fi
local RESPONSE
RESPONSE=$(curl -s -w "\n%{http_code}" -L -X POST "${PB_API_URL}" \
-H "Content-Type: application/json" \
-d "$JSON_PAYLOAD" 2>/dev/null) || true
# Extract PocketBase record ID from response for later updates
local http_code body
http_code=$(echo "$RESPONSE" | tail -n1)
body=$(echo "$RESPONSE" | sed '$d')
if [[ "$http_code" == "200" ]] || [[ "$http_code" == "201" ]]; then
PB_RECORD_ID=$(echo "$body" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) || true
fi
}
# ------------------------------------------------------------------------------
# post_to_api_vm()
#
# - 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 <<EOF
{
"ct_type": 2,
"type":"vm",
"disk_size": $DISK_SIZE_API,
"core_count": $CORE_COUNT,
"ram_size": $RAM_SIZE,
"os_type": "$var_os",
"os_version": "$var_version",
"nsapp": "$NSAPP",
"method": "$METHOD",
"pve_version": "$pve_version",
"type": "vm",
"disk_size": ${DISK_SIZE_API:-0},
"core_count": ${CORE_COUNT:-0},
"ram_size": ${RAM_SIZE:-0},
"os_type": "${var_os:-}",
"os_version": "${var_version:-}",
"nsapp": "${NSAPP:-}",
"method": "${METHOD:-default}",
"pve_version": "${pve_version}",
"status": "installing",
"random_id": "$RANDOM_UUID"
"random_id": "${RANDOM_UUID}"
}
EOF
)
if [[ "$DIAGNOSTICS" == "yes" ]]; then
RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \
-H "Content-Type: application/json" \
-d "$JSON_PAYLOAD") || true
local RESPONSE
RESPONSE=$(curl -s -w "\n%{http_code}" -L -X POST "${PB_API_URL}" \
-H "Content-Type: application/json" \
-d "$JSON_PAYLOAD" 2>/dev/null) || true
# Extract PocketBase record ID from response for later updates
local http_code body
http_code=$(echo "$RESPONSE" | tail -n1)
body=$(echo "$RESPONSE" | sed '$d')
if [[ "$http_code" == "200" ]] || [[ "$http_code" == "201" ]]; then
PB_RECORD_ID=$(echo "$body" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) || true
fi
}
# ------------------------------------------------------------------------------
# post_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 <<EOF
{
"status": "$status",
"error": "$error",
"random_id": "$RANDOM_UUID"
"status": "${pb_status}",
"error": "${error:-}",
"exit_code": ${exit_code:-0}
}
EOF
)
if [[ "$DIAGNOSTICS" == "yes" ]]; then
RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \
-H "Content-Type: application/json" \
-d "$JSON_PAYLOAD") || true
fi
# PATCH to update the existing record
curl -s -L -X PATCH "${PB_API_URL}/${record_id}" \
-H "Content-Type: application/json" \
-d "$JSON_PAYLOAD" &>/dev/null || true
POST_UPDATE_DONE=true
}

View File

@ -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

View File

@ -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

0
misc/ingest.go Normal file
View File