From fe0b6fafb4781da144d20f9f4cd0254e627db631 Mon Sep 17 00:00:00 2001 From: Peter Rounce Date: Thu, 2 Feb 2023 11:54:41 +0000 Subject: [PATCH] API for createboltcard call added --- createboltcard.go | 139 ++++++++++++++++++++++++++++++++++++++++++++++ database.go | 70 +++++++++++++++++++++++ main.go | 12 ++-- 3 files changed, 215 insertions(+), 6 deletions(-) create mode 100644 createboltcard.go diff --git a/createboltcard.go b/createboltcard.go new file mode 100644 index 0000000..d7e962f --- /dev/null +++ b/createboltcard.go @@ -0,0 +1,139 @@ +package main + +import ( + "crypto/rand" + "encoding/hex" + log "github.com/sirupsen/logrus" + "net/http" + "strconv" + "strings" +) + +func random_hex() string { + b := make([]byte, 16) + _, err := rand.Read(b) + if err != nil { + log.Warn(err.Error()) + return "" + } + + return hex.EncodeToString(b) +} + +func createboltcard(w http.ResponseWriter, r *http.Request) { + if db_get_setting("FUNCTION_INTERNAL_API") != "ENABLE" { + msg := "Internal API function is not enabled" + log.Debug(msg) + write_error_message(w, msg) + return + } + + tx_max_str := r.URL.Query().Get("tx_max") + tx_max, err := strconv.Atoi(tx_max_str) + if err != nil { + msg := "tx_max is not a valid integer" + log.Warn(msg) + write_error_message(w, msg) + return + } + + day_max_str := r.URL.Query().Get("day_max") + day_max, err := strconv.Atoi(day_max_str) + if err != nil { + msg := "day_max is not a valid integer" + log.Warn(msg) + write_error_message(w, msg) + return + } + + enable_flag_str := r.URL.Query().Get("enable") + enable_flag, err := strconv.ParseBool(enable_flag_str) + if err != nil { + msg := "enable is not a valid boolean" + log.Warn(msg) + write_error_message(w, msg) + return + } + + card_name := r.URL.Query().Get("card_name") + + uid_privacy_flag_str := r.URL.Query().Get("uid_privacy") + uid_privacy_flag, err := strconv.ParseBool(uid_privacy_flag_str) + if err != nil { + msg := "uid_privacy is not a valid boolean" + log.Warn(msg) + write_error_message(w, msg) + return + } + + allow_neg_bal_flag_str := r.URL.Query().Get("allow_neg_bal") + allow_neg_bal_flag, err := strconv.ParseBool(allow_neg_bal_flag_str) + if err != nil { + msg := "allow_neg_bal is not a valid boolean" + log.Warn(msg) + write_error_message(w, msg) + return + } + + // check if card_name already exists + + card_count, err := db_get_card_name_count(card_name) + if err != nil { + log.Warn(err.Error()) + return + } + + if card_count > 0 { + msg := "the card name already exists in the database" + log.Warn(msg) + write_error_message(w, msg) + return + } + + // log the request + + log.WithFields(log.Fields{ + "card_name": card_name, "tx_max": tx_max, "day_max": day_max, + "enable": enable_flag, "uid_privacy": uid_privacy_flag, + "allow_neg_bal": allow_neg_bal_flag}).Info("createboltcard API request") + + // create the keys + + one_time_code := random_hex() + k0_auth_key := random_hex() + k2_cmac_key := random_hex() + k3 := random_hex() + k4 := random_hex() + + // create the new card record + + err = db_insert_card(one_time_code, k0_auth_key, k2_cmac_key, k3, k4, + tx_max, day_max, enable_flag, card_name, + uid_privacy_flag, allow_neg_bal_flag) + if err != nil { + log.Warn(err.Error()) + return + } + + // return the URI + one_time_code + + hostdomain := db_get_setting("HOST_DOMAIN") + url := "" + if strings.HasSuffix(hostdomain, ".onion") { + url = "http://" + hostdomain + "/new?a=" + one_time_code + } else { + url = "https://" + hostdomain + "/new?a=" + one_time_code + } + + // log the response + + log.WithFields(log.Fields{ + "card_name": card_name, "url": url}).Info("createboltcard API response") + + jsonData := []byte(`{"status":"OK",` + + `"url":"` + url + `"}`) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write(jsonData) +} diff --git a/database.go b/database.go index 8eac299..7351fd1 100644 --- a/database.go +++ b/database.go @@ -706,3 +706,73 @@ func db_get_card_total_sats(card_id int) (int, error) { return card_total_sats, nil } + +func db_get_card_name_count(card_name string) (card_count int, err error) { + + card_count = 0 + + db, err := db_open() + if err != nil { + return 0, err + } + defer db.Close() + + sqlStatement := `SELECT COUNT(card_id) FROM cards WHERE card_name = $1;` + + row := db.QueryRow(sqlStatement, card_name) + err = row.Scan(&card_count) + if err != nil { + return 0, err + } + + return card_count, nil +} + +func db_insert_card(one_time_code string, k0_auth_key string, k2_cmac_key string, k3 string, k4 string, + tx_max_sats int, day_max_sats int, lnurlw_enable bool, card_name string, uid_privacy bool, + allow_neg_bal_ptr bool) error { + + lnurlw_enable_yn := "N" + if lnurlw_enable { + lnurlw_enable_yn = "Y" + } + + uid_privacy_yn := "N" + if uid_privacy { + uid_privacy_yn = "Y" + } + + allow_neg_bal_yn := "N" + if allow_neg_bal_ptr { + allow_neg_bal_yn = "Y" + } + + db, err := db_open() + if err != nil { + return err + } + defer db.Close() + + // insert a new record into cards + + sqlStatement := `INSERT INTO cards` + + ` (one_time_code, k0_auth_key, k2_cmac_key, k3, k4, uid, last_counter_value,` + + ` lnurlw_request_timeout_sec, tx_limit_sats, day_limit_sats, lnurlw_enable,` + + ` one_time_code_used, card_name, uid_privacy, allow_negative_balance)` + + ` VALUES ($1, $2, $3, $4, $5, '', 0, 60, $6, $7, $8, 'N', $9, $10, $11);` + res, err := db.Exec(sqlStatement, one_time_code, k0_auth_key, k2_cmac_key, k3, k4, + tx_max_sats, day_max_sats, lnurlw_enable_yn, card_name, uid_privacy_yn, + allow_neg_bal_yn) + if err != nil { + return err + } + count, err := res.RowsAffected() + if err != nil { + return err + } + if count != 1 { + return errors.New("not one card record inserted") + } + + return nil +} diff --git a/main.go b/main.go index 0e80910..ffb52b3 100644 --- a/main.go +++ b/main.go @@ -61,8 +61,8 @@ func main() { // ping internal_router.Path("/ping").Methods("GET").HandlerFunc(internal_ping) - //internal_router.Path("/createboltcard").Methods("POST").HandlerFunc(adminapi_createboltcard) - //internal_router.Path("/wipeboltcard").Methods("POST").HandlerFunc(adminapi_wipeboltcard) + internal_router.Path("/createboltcard").Methods("GET").HandlerFunc(createboltcard) + //internal_router.Path("/wipeboltcard").Methods("GET").HandlerFunc(wipeboltcard) port := db_get_setting("HOST_PORT") if port == "" { @@ -77,10 +77,10 @@ func main() { } internal_server := &http.Server{ - Handler: internal_router, - Addr: ":9001", - WriteTimeout: 5 * time.Second, - ReadTimeout: 5 * time.Second, + Handler: internal_router, + Addr: ":9001", + WriteTimeout: 5 * time.Second, + ReadTimeout: 5 * time.Second, } go external_server.ListenAndServe()