Merge pull request #25 from boltcard/db_settings

Move settings to database
This commit is contained in:
Peter Rounce 2023-01-27 18:50:59 +00:00 committed by GitHub
commit 226768557b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 169 additions and 95 deletions

5
.gitignore vendored
View file

@ -6,6 +6,7 @@
*.dylib
boltcard
createboltcard/createboltcard
wipeboltcard/wipeboltcard
# Test binary, built with `go test -c`
*.test
@ -16,9 +17,9 @@ createboltcard/createboltcard
# Dependency directories (remove the comment below to include it)
# vendor/
# secrets
# possible secrets
tls.cert
*.macaroon*
add_test_data.sql
Caddyfile
boltcard.service
*.secret

View file

@ -10,57 +10,13 @@ Restart=always
RestartSec=10
User=ubuntu
# boltcard service settings
# LOG_LEVEL is DEBUG or PRODUCTION
Environment="LOG_LEVEL=DEBUG"
# AES_DECRYPT_KEY is the hex value of the server decrypt key for hosted bolt cards
Environment="AES_DECRYPT_KEY=00000000000000000000000000000000"
# DB_ values are for the postgres database connection
# postgres database connection settings
Environment="DB_HOST=localhost"
Environment="DB_PORT=5432"
Environment="DB_USER=cardapp"
Environment="DB_PASSWORD=database_password"
Environment="DB_NAME=card_db"
# HOST_ values are for https calls
Environment="HOST_DOMAIN=card.yourdomain.com"
Environment="HOST_PORT=9000"
# MIN_WITHDRAW_SATS & MAX_WITHDRAW_SATS set the values for the lnurlw response
#
# as of Nov 2022 it is advisable to set MAX_WITHDRAW_SATS higher than the card tx limits
# the Point Of Sale use will work as expected
# the Gift Card use should try to withdraw MAX_WITHDRAW_SATS and fail on the card tx limit
Environment="MIN_WITHDRAW_SATS=1"
Environment="MAX_WITHDRAW_SATS=1000000"
# LN_ values are for the lightning server used for making payments
Environment="LN_HOST=ln.host.io"
Environment="LN_PORT=10009"
Environment="LN_TLS_FILE=/home/ubuntu/boltcard/tls.cert"
Environment="LN_MACAROON_FILE=/home/ubuntu/boltcard/SendPaymentV2.macaroon"
# The maximum lightning network fee to be paid is the base FEE_LIMIT_SAT + the FEE_LIMIT_PERCENT of the amount.
Environment="FEE_LIMIT_SAT=5"
Environment="FEE_LIMIT_PERCENT=0.5"
# email
# Environment="AWS_SES_ID="
# Environment="AWS_SES_SECRET="
# Environment="AWS_SES_EMAIL_FROM="
# Environment="EMAIL_MAX_TXS=10"
# LN_TESTNODE may be used in testing and will then only pay to the defined test node pubkey
#Environment="LN_TESTNODE=000000000000000000000000000000000000000000000000000000000000000000"
# set which functions are available on the server
Environment="FUNCTION_LNURLW=ENABLE"
#Environment="FUNCTION_LNURLP=ENABLE"
#Environment="FUNCTION_EMAIL=ENABLE"
ExecStart=/bin/bash /home/ubuntu/boltcard/s_launch
[Install]

View file

@ -6,6 +6,13 @@ CREATE USER cardapp WITH PASSWORD 'database_password';
\c card_db;
CREATE TABLE settings (
setting_id INT GENERATED ALWAYS AS IDENTITY,
name VARCHAR(30) UNIQUE NOT NULL DEFAULT '',
value VARCHAR(128) NOT NULL DEFAULT '',
PRIMARY KEY(setting_id)
);
CREATE TABLE cards (
card_id INT GENERATED ALWAYS AS IDENTITY,
k0_auth_key CHAR(32) NOT NULL,
@ -55,10 +62,12 @@ CREATE TABLE card_receipts (
amount_msats BIGINT CHECK (amount_msats > 0),
receipt_status VARCHAR(100) NOT NULL DEFAULT '',
receipt_status_time TIMESTAMPTZ,
PRIMARY KEY(card_receipt_id),
CONSTRAINT fk_card FOREIGN KEY(card_id) REFERENCES cards(card_id)
);
GRANT ALL PRIVILEGES ON TABLE settings TO cardapp;
GRANT ALL PRIVILEGES ON TABLE cards TO cardapp;
GRANT ALL PRIVILEGES ON TABLE card_payments TO cardapp;
GRANT ALL PRIVILEGES ON TABLE card_receipts TO cardapp;

View file

@ -27,6 +27,27 @@ func db_open() (*sql.DB, error) {
return db, nil
}
func db_get_setting(setting_name string) (string) {
setting_value := ""
db, err := db_open()
if err != nil {
return ""
}
defer db.Close()
sqlStatement := `select value from settings where name=$1;`
row := db.QueryRow(sqlStatement, setting_name)
err = row.Scan(&setting_value)
if err != nil {
return ""
}
return setting_value
}
func db_get_card_name_count(card_name string) (card_count int, err error) {
card_count = 0

View file

@ -7,7 +7,6 @@ import (
"fmt"
log "github.com/sirupsen/logrus"
qrcode "github.com/skip2/go-qrcode"
"os"
"strings"
)
@ -71,7 +70,7 @@ func main() {
// show a QR code on the console for the URI + one_time_code
hostdomain := os.Getenv("HOST_DOMAIN")
hostdomain := db_get_setting("HOST_DOMAIN")
url := ""
if strings.HasSuffix(hostdomain, ".onion") {
url = "http://" + hostdomain + "/new?a=" + one_time_code

View file

@ -65,6 +65,27 @@ func db_open() (*sql.DB, error) {
return db, nil
}
func db_get_setting(setting_name string) (string) {
setting_value := ""
db, err := db_open()
if err != nil {
return ""
}
defer db.Close()
sqlStatement := `select value from settings where name=$1;`
row := db.QueryRow(sqlStatement, setting_name)
err = row.Scan(&setting_value)
if err != nil {
return ""
}
return setting_value
}
func db_get_new_card(one_time_code string) (*card, error) {
c := card{}

20
docs/SETTINGS.md Normal file
View file

@ -0,0 +1,20 @@
# Settings
The database connection settings are in the system environment variables.
All other settings are in the database in a `settings` table.
- LOG_LEVEL
- AES_DECRYPT_KEY
- HOST_DOMAIN
- MIN_WITHDRAW_SATS
- MAX_WITHDRAW_SATS
- LN_HOST
- LN_PORT
- LN_TLS_FILE
- LN_MACAROON_FILE
- FEE_LIMIT_SAT
- FEE_LIMIT_PERCENT
- LN_TESTNODE
- FUNCTION_LNURLW
- FUNCTION_LNURLP
- FUNCTION_EMAIL

View file

@ -7,7 +7,6 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ses"
log "github.com/sirupsen/logrus"
"os"
"strconv"
"strings"
)
@ -26,7 +25,7 @@ func send_balance_email(recipient_email string, card_id int) {
return
}
email_max_txs, err := strconv.Atoi(os.Getenv("EMAIL_MAX_TXS"))
email_max_txs, err := strconv.Atoi(db_get_setting("EMAIL_MAX_TXS"))
if err != nil {
log.Warn(err.Error())
return
@ -89,9 +88,9 @@ func send_balance_email(recipient_email string, card_id int) {
func send_email(recipient string, subject string, htmlBody string, textBody string) {
aws_ses_id := os.Getenv("AWS_SES_ID")
aws_ses_secret := os.Getenv("AWS_SES_SECRET")
sender := os.Getenv("AWS_SES_EMAIL_FROM")
aws_ses_id := db_get_setting("AWS_SES_ID")
aws_ses_secret := db_get_setting("AWS_SES_SECRET")
sender := db_get_setting("AWS_SES_EMAIL_FROM")
sess, err := session.NewSession(&aws.Config{
Region: aws.String("us-east-1"),

View file

@ -7,7 +7,6 @@ import (
"io"
"io/ioutil"
log "github.com/sirupsen/logrus"
"os"
"strconv"
"time"
"crypto/sha256"
@ -75,7 +74,7 @@ func getGrpcConn(hostname string, port int, tlsFile, macaroonFile string) *grpc.
func add_invoice(amount_sat int64, metadata string) (payment_request string, r_hash []byte, return_err error) {
ln_port, err := strconv.Atoi(os.Getenv("LN_PORT"))
ln_port, err := strconv.Atoi(db_get_setting("LN_PORT"))
if err != nil {
return "", nil, err
}
@ -83,10 +82,10 @@ func add_invoice(amount_sat int64, metadata string) (payment_request string, r_h
dh := sha256.Sum256([]byte(metadata))
connection := getGrpcConn(
os.Getenv("LN_HOST"),
db_get_setting("LN_HOST"),
ln_port,
os.Getenv("LN_TLS_FILE"),
os.Getenv("LN_MACAROON_FILE"))
db_get_setting("LN_TLS_FILE"),
db_get_setting("LN_MACAROON_FILE"))
l_client := lnrpc.NewLightningClient(connection)
@ -113,17 +112,17 @@ func monitor_invoice_state(r_hash []byte) () {
// get node parameters from environment variables
ln_port, err := strconv.Atoi(os.Getenv("LN_PORT"))
ln_port, err := strconv.Atoi(db_get_setting("LN_PORT"))
if err != nil {
log.Warn(err)
return
}
connection := getGrpcConn(
os.Getenv("LN_HOST"),
db_get_setting("LN_HOST"),
ln_port,
os.Getenv("LN_TLS_FILE"),
os.Getenv("LN_MACAROON_FILE"))
db_get_setting("LN_TLS_FILE"),
db_get_setting("LN_MACAROON_FILE"))
i_client := invoicesrpc.NewInvoicesClient(connection)
@ -196,28 +195,28 @@ func pay_invoice(card_payment_id int, invoice string) {
// get node parameters from environment variables
ln_port, err := strconv.Atoi(os.Getenv("LN_PORT"))
ln_port, err := strconv.Atoi(db_get_setting("LN_PORT"))
if err != nil {
log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err)
return
}
connection := getGrpcConn(
os.Getenv("LN_HOST"),
db_get_setting("LN_HOST"),
ln_port,
os.Getenv("LN_TLS_FILE"),
os.Getenv("LN_MACAROON_FILE"))
db_get_setting("LN_TLS_FILE"),
db_get_setting("LN_MACAROON_FILE"))
r_client := routerrpc.NewRouterClient(connection)
fee_limit_sat_str := os.Getenv("FEE_LIMIT_SAT")
fee_limit_sat_str := db_get_setting("FEE_LIMIT_SAT")
fee_limit_sat, err := strconv.ParseInt(fee_limit_sat_str, 10, 64)
if err != nil {
log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err)
return
}
fee_limit_percent_str := os.Getenv("FEE_LIMIT_PERCENT")
fee_limit_percent_str := db_get_setting("FEE_LIMIT_PERCENT")
fee_limit_percent, err := strconv.ParseFloat(fee_limit_percent_str, 64)
if err != nil {
log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err)

View file

@ -1,7 +1,6 @@
package main
import (
"os"
log "github.com/sirupsen/logrus"
"github.com/gorilla/mux"
"net/http"
@ -10,7 +9,7 @@ import (
)
func lnurlp_callback(w http.ResponseWriter, r *http.Request) {
if os.Getenv("FUNCTION_LNURLP") != "ENABLE" {
if db_get_setting("FUNCTION_LNURLP") != "ENABLE" {
log.Debug("LNURLp function is not enabled")
return
}
@ -34,7 +33,7 @@ func lnurlp_callback(w http.ResponseWriter, r *http.Request) {
"req.Host": r.Host,
},).Info("lnurlp_callback")
domain := os.Getenv("HOST_DOMAIN")
domain := db_get_setting("HOST_DOMAIN")
if r.Host != domain {
log.Warn("wrong host domain")
write_error(w)

View file

@ -1,14 +1,13 @@
package main
import (
"os"
log "github.com/sirupsen/logrus"
"github.com/gorilla/mux"
"net/http"
)
func lnurlp_response(w http.ResponseWriter, r *http.Request) {
if os.Getenv("FUNCTION_LNURLP") != "ENABLE" {
if db_get_setting("FUNCTION_LNURLP") != "ENABLE" {
log.Debug("LNURLp function is not enabled")
return
}
@ -22,9 +21,9 @@ func lnurlp_response(w http.ResponseWriter, r *http.Request) {
"r.Host": r.Host,
},).Info("lnurlp_response")
// look up domain in env vars (HOST_DOMAIN)
// look up domain setting (HOST_DOMAIN)
domain := os.Getenv("HOST_DOMAIN")
domain := db_get_setting("HOST_DOMAIN")
if r.Host != domain {
log.Warn("wrong host domain")
write_error(w)

View file

@ -4,12 +4,11 @@ import (
decodepay "github.com/fiatjaf/ln-decodepay"
log "github.com/sirupsen/logrus"
"net/http"
"os"
)
func lnurlw_callback(w http.ResponseWriter, req *http.Request) {
env_host_domain := os.Getenv("HOST_DOMAIN")
env_host_domain := db_get_setting("HOST_DOMAIN")
if req.Host != env_host_domain {
log.Warn("wrong host domain")
write_error(w)
@ -78,7 +77,7 @@ func lnurlw_callback(w http.ResponseWriter, req *http.Request) {
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Debug("checking payment rules")
// check if we are only sending funds to a defined test node
testnode := os.Getenv("LN_TESTNODE")
testnode := db_get_setting("LN_TESTNODE")
if testnode != "" && bolt11.Payee != testnode {
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Info("rejected as not the defined test node")
write_error(w)

View file

@ -150,7 +150,7 @@ func parse_request(req *http.Request) (int, error) {
// decrypt p with aes_decrypt_key
aes_decrypt_key := os.Getenv("AES_DECRYPT_KEY")
aes_decrypt_key := db_get_setting("AES_DECRYPT_KEY")
key_sdm_file_read, err := hex.DecodeString(aes_decrypt_key)
@ -246,7 +246,7 @@ func parse_request(req *http.Request) (int, error) {
func lnurlw_response(w http.ResponseWriter, req *http.Request) {
env_host_domain := os.Getenv("HOST_DOMAIN")
env_host_domain := db_get_setting("HOST_DOMAIN")
if req.Host != env_host_domain {
log.Warn("wrong host domain")
write_error(w)
@ -286,7 +286,7 @@ func lnurlw_response(w http.ResponseWriter, req *http.Request) {
lnurlw_cb_url = "https://" + req.Host + "/cb"
}
min_withdraw_sats_str := os.Getenv("MIN_WITHDRAW_SATS")
min_withdraw_sats_str := db_get_setting("MIN_WITHDRAW_SATS")
min_withdraw_sats, err := strconv.Atoi(min_withdraw_sats_str)
if err != nil {
@ -295,7 +295,7 @@ func lnurlw_response(w http.ResponseWriter, req *http.Request) {
return
}
max_withdraw_sats_str := os.Getenv("MAX_WITHDRAW_SATS")
max_withdraw_sats_str := db_get_setting("MAX_WITHDRAW_SATS")
max_withdraw_sats, err := strconv.Atoi(max_withdraw_sats_str)
if err != nil {

21
main.go
View file

@ -5,7 +5,6 @@ import (
"github.com/gorilla/mux"
"net/http"
"time"
"os"
)
var router = mux.NewRouter()
@ -25,13 +24,17 @@ func write_error_message(w http.ResponseWriter, message string) {
}
func main() {
log_level := os.Getenv("LOG_LEVEL")
log_level := db_get_setting("LOG_LEVEL")
if log_level == "DEBUG" {
log.SetLevel(log.DebugLevel)
log.Info("bolt card service started - debug log level")
} else {
log.Info("bolt card service started - production log level")
switch log_level {
case "DEBUG":
log.SetLevel(log.DebugLevel)
log.Info("bolt card service started - debug log level")
case "PRODUCTION":
log.Info("bolt card service started - production log level")
default:
// log.Fatal calls os.Exit(1) after logging the error
log.Fatal("error getting a valid LOG_LEVEL setting from the database")
}
log.SetFormatter(&log.JSONFormatter{
@ -47,8 +50,8 @@ func main() {
router.Path("/.well-known/lnurlp/{name}").Methods("GET").HandlerFunc(lnurlp_response)
router.Path("/lnurlp/{name}").Methods("GET").HandlerFunc(lnurlp_callback)
port := os.Getenv("HOST_PORT")
if len(port) == 0 {
port := db_get_setting("HOST_PORT")
if port == "" {
port = "9000"
}

View file

@ -5,7 +5,6 @@ import (
"encoding/json"
log "github.com/sirupsen/logrus"
"net/http"
"os"
)
/**
@ -70,7 +69,7 @@ func new_card_request(w http.ResponseWriter, req *http.Request) {
return
}
k1_decrypt_key := os.Getenv("AES_DECRYPT_KEY")
k1_decrypt_key := db_get_setting("AES_DECRYPT_KEY")
response := NewCardResponse{}
response.PROTOCOL_NAME = "create_bolt_card_response"

View file

@ -1,3 +1,8 @@
cd createboltcard
go build
cd ../wipeboltcard
go build
cd ..
go build
sudo cp boltcard.service /etc/systemd/system/boltcard.service
sudo systemctl daemon-reload

View file

@ -13,6 +13,7 @@ read x
if [ "$x" = "y" ]; then
psql postgres -f create_db.sql
psql postgres -f settings.sql
echo Database created
else
echo No action

23
settings.sql Normal file
View file

@ -0,0 +1,23 @@
\c card_db;
DELETE FROM settings;
INSERT INTO settings (name, value) VALUES ('LOG_LEVEL', '');
INSERT INTO settings (name, value) VALUES ('AES_DECRYPT_KEY', '');
INSERT INTO settings (name, value) VALUES ('HOST_DOMAIN', '');
INSERT INTO settings (name, value) VALUES ('MIN_WITHDRAW_SATS', '');
INSERT INTO settings (name, value) VALUES ('MAX_WITHDRAW_SATS', '');
INSERT INTO settings (name, value) VALUES ('LN_HOST', '');
INSERT INTO settings (name, value) VALUES ('LN_PORT', '');
INSERT INTO settings (name, value) VALUES ('LN_TLS_FILE', '');
INSERT INTO settings (name, value) VALUES ('LN_MACAROON_FILE', '');
INSERT INTO settings (name, value) VALUES ('FEE_LIMIT_SAT', '');
INSERT INTO settings (name, value) VALUES ('FEE_LIMIT_PERCENT', '');
INSERT INTO settings (name, value) VALUES ('LN_TESTNODE', '');
INSERT INTO settings (name, value) VALUES ('FUNCTION_LNURLW', '');
INSERT INTO settings (name, value) VALUES ('FUNCTION_LNURLP', '');
INSERT INTO settings (name, value) VALUES ('FUNCTION_EMAIL', '');
INSERT INTO settings (name, value) VALUES ('AWS_SES_ID', '');
INSERT INTO settings (name, value) VALUES ('AWS_SES_SECRET', '');
INSERT INTO settings (name, value) VALUES ('AWS_SES_EMAIL_FROM', '');
INSERT INTO settings (name, value) VALUES ('EMAIL_MAX_TXS', '');

View file

@ -27,6 +27,27 @@ func db_open() (*sql.DB, error) {
return db, nil
}
func db_get_setting(setting_name string) (string) {
setting_value := ""
db, err := db_open()
if err != nil {
return ""
}
defer db.Close()
sqlStatement := `select value from settings where name=$1;`
row := db.QueryRow(sqlStatement, setting_name)
err = row.Scan(&setting_value)
if err != nil {
return ""
}
return setting_value
}
func db_get_card_name_count(card_name string) (card_count int, err error) {
card_count = 0
@ -91,7 +112,7 @@ func db_wipe_card(card_name string) (*card_wipe_info, error) {
return &card_wipe_info, err
}
card_wipe_info.k1 = os.Getenv("AES_DECRYPT_KEY")
card_wipe_info.k1 = db_get_setting("AES_DECRYPT_KEY")
return &card_wipe_info, nil
}