commit ebb0b8205b01574ca66db7198c8a9a11f22c7b97
parent b98cfd344d0500d720cc551bfb861436a9c6aa04
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date: Sat, 14 Feb 2026 17:39:43 +0100
Database refactor: This removes the GORM dependency and implements Taler-like database versioning. This also removes the sqlite in-memory testing database, which means you need to have a database running (PQ) to run the tests
Diffstat:
9 files changed, 678 insertions(+), 155 deletions(-)
diff --git a/cmd/taldir-server/main.go b/cmd/taldir-server/main.go
@@ -34,9 +34,10 @@ import (
"os"
"path"
+ "database/sql"
+ _ "github.com/lib/pq"
"github.com/schanzen/taler-go/pkg/merchant"
"gopkg.in/ini.v1"
- "gorm.io/driver/postgres"
"rsc.io/getopt"
taldir "taler.net/taldir/pkg/taldir"
)
@@ -100,7 +101,12 @@ func main() {
cfg.Section("taldir-pq").Key("user").MustString("taldir"),
cfg.Section("taldir-pq").Key("password").MustString("secret"),
cfg.Section("taldir-pq").Key("db_name").MustString("taldir"))
- db := postgres.Open(psqlconn)
+
+ db, err := sql.Open("postgres", psqlconn)
+ if err != nil {
+ log.Panic(err)
+ }
+ defer db.Close()
merchURL := cfg.Section("taldir").Key("base_url_merchant").MustString("https://backend.demo.taler.net")
merchToken := cfg.Section("taldir").Key("merchant_token").MustString("sandbox")
cfg.WriteTo(os.Stdout)
diff --git a/cmd/taldir-server/main_test.go b/cmd/taldir-server/main_test.go
@@ -20,18 +20,20 @@ package main_test
import (
"bytes"
+ "database/sql"
"fmt"
"io"
"log"
"net/http"
"net/http/httptest"
"os"
+ "os/exec"
"strings"
"testing"
+ _ "github.com/mattn/go-sqlite3"
"github.com/schanzen/taler-go/pkg/merchant"
"gopkg.in/ini.v1"
- "gorm.io/driver/sqlite"
_ "taler.net/taldir/cmd/taldir-server"
"taler.net/taldir/internal/util"
taldir "taler.net/taldir/pkg/taldir"
@@ -96,13 +98,103 @@ const merchantConfigResponse = `{
"version": "18:0:15"
}`
+func CheckVersioning(db *sql.DB) (bool, error) {
+ rows, err := db.Query(`SELECT schema_name FROM information_schema.schemata WHERE schema_name='_v';`)
+ if err != nil {
+ return false, err
+ }
+ defer rows.Close()
+ if rows.Next() {
+ fmt.Println("Versioning applied")
+ return true, nil
+ }
+ fmt.Printf("Versioning not applied: %v", rows.Err())
+ return false, nil
+}
+
+func CheckPatch(db *sql.DB, patchName string) (bool, error) {
+ rows, err := db.Query(`SELECT applied_by FROM _v.patches WHERE patch_name=$1 LIMIT 1;`, patchName)
+ if err != nil {
+ return false, err
+ }
+ defer rows.Close()
+ if rows.Next() {
+ return true, nil
+ }
+ return false, nil
+}
+
+func RunSQL(db *sql.DB, patchName string) error {
+ path, err := exec.LookPath("psql")
+ if err != nil {
+ return err
+ }
+ out, err := exec.Command(path, "taler-directory", "-f", patchName, "-q", "--set", "ON_ERROR_STOP=1").Output()
+ // FIXME logger
+ fmt.Printf("Executing `%s %s %s %s %s %s %s`\n", path, "taler-directory", "-f", patchName, "-q", "--set", "ON_ERROR_STOP=1")
+ if err != nil {
+ fmt.Printf("%s, %v\n", out, err)
+ return err
+ }
+ return nil
+
+}
+
+func DBInit(db *sql.DB) error {
+ applied, err := CheckVersioning(db)
+ loadSuffix := "../../sql/"
+ if err != nil {
+ fmt.Printf("%v\n", err)
+ }
+ if !applied {
+ err := RunSQL(db, fmt.Sprintf("%s%s", loadSuffix, "versioning.sql"))
+ if err != nil {
+ return err
+ }
+ }
+ for i := range 10000 {
+ patchName := fmt.Sprintf("taler-directory-%04d", i+1)
+ applied, err := CheckPatch(db, patchName)
+ if err != nil {
+ return err
+ }
+ if applied {
+ fmt.Printf("Patch %s already applied\n", patchName)
+ continue
+ }
+ patchFile := fmt.Sprintf("%s%s.sql", loadSuffix, patchName)
+ if _, err := os.Stat(patchFile); err != nil {
+ fmt.Printf("Patch %s not found, up-to-date.\n", patchFile)
+ break
+ }
+ fmt.Printf("Applying patch %s\n", patchName)
+ err = RunSQL(db, patchFile)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
func TestMain(m *testing.M) {
cfg, err := ini.LooseLoad("testdata/taldir-test.conf")
if err != nil {
log.Fatalf("Failed to read config: %v", err)
- os.Exit(1)
}
- db := sqlite.Open("file::memory:?cache=shared")
+ psqlconn := fmt.Sprintf("host=%s port=%d dbname=%s sslmode=disable",
+ cfg.Section("taldir-pq").Key("host").MustString("localhost"),
+ cfg.Section("taldir-pq").Key("port").MustInt64(5432),
+ cfg.Section("taldir-pq").Key("db_name").MustString("taler-directory"))
+
+ db, err := sql.Open("postgres", psqlconn)
+ if err != nil {
+ log.Panic(err)
+ }
+ defer db.Close()
+ err = DBInit(db)
+ if err != nil {
+ log.Fatalf("Failed to apply versioning or patches: %v", err)
+ }
merchServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/config" {
w.WriteHeader(http.StatusOK)
diff --git a/cmd/taldir-server/testdata/taldir-test.conf b/cmd/taldir-server/testdata/taldir-test.conf
@@ -34,4 +34,4 @@ host = "localhost"
port = 5432
user = "taldir"
password = "secret"
-db_name = "taldir"
+db_name = "taler-directory"
diff --git a/debian/rules b/debian/rules
@@ -12,6 +12,9 @@ override_dh_auto_clean:
override_dh_auto_test:
override_dh_installsystemd:
dh_installsystemd -ptaler-directory --no-start --no-enable taler-directory.service
+ # final invocation to generate daemon reload
+ dh_installsystemd
+
#override_dh_auto_build:
#override_dh_auto_install:
# wget -N --progress=dot:mega $(URL)
diff --git a/go.mod b/go.mod
@@ -1,34 +1,27 @@
module taler.net/taldir
-go 1.24.0
+go 1.25.0
require (
github.com/gertd/go-pluralize v0.2.1
github.com/gorilla/mux v1.8.1
github.com/kataras/i18n v0.0.8
+ github.com/lib/pq v1.11.2
+ github.com/mattn/go-sqlite3 v1.14.34
github.com/schanzen/taler-go v1.1.1
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
gopkg.in/ini.v1 v1.67.0
- gorm.io/driver/postgres v1.6.0
- gorm.io/driver/sqlite v1.6.0
- gorm.io/gorm v1.31.1
rsc.io/getopt v0.0.0-20170811000552-20be20937449
)
require (
github.com/BurntSushi/toml v1.5.0 // indirect
- github.com/jackc/pgpassfile v1.0.0 // indirect
- github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
- github.com/jackc/pgx/v5 v5.7.6 // indirect
- github.com/jackc/puddle/v2 v2.2.2 // indirect
- github.com/jinzhu/inflection v1.0.0 // indirect
- github.com/jinzhu/now v1.1.5 // indirect
- github.com/kr/text v0.1.0 // indirect
- github.com/mattn/go-sqlite3 v1.14.22 // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/kr/pretty v0.3.0 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
- golang.org/x/crypto v0.46.0 // indirect
golang.org/x/net v0.48.0 // indirect
- golang.org/x/sync v0.19.0 // indirect
golang.org/x/text v0.32.0 // indirect
+ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
@@ -1,67 +1,49 @@
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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/gertd/go-pluralize v0.2.1 h1:M3uASbVjMnTsPb0PNqg+E/24Vwigyo/tvyMTtAlLgiA=
github.com/gertd/go-pluralize v0.2.1/go.mod h1:rbYaKDbsXxmRfr8uygAEKhOWsjyrrqrkHVpZvoOp8zk=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
-github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
-github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
-github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
-github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
-github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk=
-github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
-github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
-github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
-github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
-github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
-github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
-github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/kataras/i18n v0.0.8 h1:thiDRqq4fN2sQOK5CwR5Yh1yT7swP6B3wnadILhqjhk=
github.com/kataras/i18n v0.0.8/go.mod h1:M/yRAqQ3Y7z2oSpotxG/+nPrgsJLas6t0kEJfIJk98E=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
-github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs=
+github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
+github.com/mattn/go-sqlite3 v1.14.34 h1:3NtcvcUnFBPsuRcno8pUtupspG/GM+9nZ88zgJcp6Zk=
+github.com/mattn/go-sqlite3 v1.14.34/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/schanzen/taler-go v1.1.1 h1:No/N8Wa9CZjwLDqS47sdSLtWno08I7B43OKOUNpzjgg=
github.com/schanzen/taler-go v1.1.1/go.mod h1:+l2TVAPZkF2d15X/XPLYZI5R6PdW6gc6Wft12jrl7tA=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
-golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
-golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
-golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
-gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
-gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
-gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
-gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
-gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
rsc.io/getopt v0.0.0-20170811000552-20be20937449 h1:UukjJOsjQH0DIuyyrcod6CXHS6cdaMMuJmrt+SN1j4A=
rsc.io/getopt v0.0.0-20170811000552-20be20937449/go.mod h1:dhCdeqAxkyt5u3/sKRkUXuHaMXUu1Pt13GTQAM2xnig=
diff --git a/pkg/taldir/config.go b/pkg/taldir/config.go
@@ -19,9 +19,10 @@
package taldir
import (
+ "database/sql"
+
"github.com/schanzen/taler-go/pkg/merchant"
"gopkg.in/ini.v1"
- "gorm.io/gorm"
)
type TaldirConfig struct {
@@ -35,7 +36,7 @@ type TaldirConfig struct {
Datahome string
// The database connection to use
- Db gorm.Dialector
+ Db *sql.DB
// The merchant connection to use
Merchant merchant.Merchant
diff --git a/pkg/taldir/db.go b/pkg/taldir/db.go
@@ -19,15 +19,56 @@
package taldir
import (
- "gorm.io/gorm"
+ "context"
+ "database/sql"
+ "errors"
+ "fmt"
"time"
)
+// Validation is the object created when a registration for an entry is initiated.
+// The Validation stores the identity key (sha256(identity)) the secret
+// Validation reference. The Validation reference is sent to the identity
+// depending on the out-of-band channel defined through the identity key type.
+type Validation struct {
+ // When was this entry created in microseconds
+ CreatedAt int64 `json:"-"`
+
+ // The hash (SHA512) of the alias
+ HAlias string `json:"h_alias"`
+
+ // For how long should the registration last
+ Duration int64 `json:"duration"`
+
+ // Target URI to associate with this alias
+ TargetURI string `json:"target_uri"`
+
+ // The activation code sent to the client
+ Challenge string `json:"-"`
+
+ // The challenge has been sent already
+ ChallengeSent bool `json:"-"`
+
+ // true if this validation also requires payment
+ RequiresPayment bool `json:"-"`
+
+ // How often was a solution for this validation tried
+ SolutionAttemptCount int
+
+ // The beginning of the last solution timeframe
+ LastSolutionTimeframeStart int64
+
+ // The order ID associated with this validation
+ OrderID string `json:"-"`
+
+ // Name of the validator
+ ValidatorName string
+}
+
// Entry is a mapping from the alias hash to a target URI
type Entry struct {
-
- // ORM
- gorm.Model `json:"-"`
+ // When was this entry created in microseconds
+ CreatedAt int64 `json:"-"`
// The salted hash (SHA512) of the hashed alias
HsAlias string `json:"-"`
@@ -36,5 +77,420 @@ type Entry struct {
TargetURI string `json:"target_uri"`
// How long the registration lasts in microseconds
- Duration time.Duration `json:"-"`
+ Duration int64 `json:"-"`
+}
+
+// Update validation in database
+func UpdateValidationInDatabase(db *sql.DB, v *Validation) error {
+ query := `UPDATE taler_directory.validations
+ SET
+ "created_at" = $2,
+ "duration" = $3,
+ "target_uri" = $4,
+ "challenge" = $5,
+ "challenge_sent" = $6,
+ "requires_payment" = $7,
+ "solution_attempt_count" = $8,
+ "last_solution_timeframe_start" = $9,
+ "order_id" = $10,
+ "validator_name" = $11
+ WHERE "h_alias" = $1;`
+ rows, err := db.Query(query, v.HAlias, v.CreatedAt, v.Duration, v.TargetURI, v.Challenge, v.ChallengeSent, v.RequiresPayment, v.SolutionAttemptCount, v.LastSolutionTimeframeStart, v.OrderID, v.ValidatorName)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+ return nil
+}
+
+// Insert new validation into database
+func InsertValidationIntoDatabase(db *sql.DB, v *Validation) error {
+ query := `INSERT INTO taler_directory.validations
+ VALUES (DEFAULT, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11);`
+ rows, err := db.Query(query, v.CreatedAt, v.HAlias, v.Duration, v.TargetURI, v.Challenge, v.ChallengeSent, v.RequiresPayment, v.SolutionAttemptCount, v.LastSolutionTimeframeStart, v.OrderID, v.ValidatorName)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+ return nil
+}
+
+// Get Validation from database
+func GetValidationFromDatabase(db *sql.DB, v *Validation, hAlias string, targetURI string, duration time.Duration) error {
+ query := `SELECT
+ "created_at",
+ "h_alias",
+ "duration",
+ "target_uri",
+ "challenge",
+ "challenge_sent",
+ "requires_payment",
+ "solution_attempt_count",
+ "last_solution_timeframe_start",
+ "order_id",
+ "validator_name"
+ FROM taler_directory.validations
+ WHERE
+ "h_alias"=$1 AND
+ "target_uri"=$2 AND
+ "duration"=$3
+ ;`
+ // Execute Query
+ rows, err := db.Query(query, hAlias, targetURI, duration.Microseconds())
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+ // Iterate over first
+ if !rows.Next() {
+ fmt.Printf("error val %v\n", rows.Err())
+ return errors.New("Validation does not exist")
+ }
+ return rows.Scan(
+ &v.CreatedAt,
+ &v.HAlias,
+ &v.Duration,
+ &v.TargetURI,
+ &v.Challenge,
+ &v.ChallengeSent,
+ &v.RequiresPayment,
+ &v.SolutionAttemptCount,
+ &v.LastSolutionTimeframeStart,
+ &v.OrderID,
+ &v.ValidatorName,
+ )
+}
+
+// Get all Validations by hash-salted alias from database
+func GetAllValidationsByHAliasFromDatabase(db *sql.DB, hAlias string) ([]Validation, error) {
+ query := `SELECT
+ "created_at",
+ "h_alias",
+ "duration",
+ "target_uri",
+ "challenge",
+ "challenge_sent",
+ "requires_payment",
+ "solution_attempt_count",
+ "last_solution_timeframe_start",
+ "order_id",
+ "validator_name"
+ FROM taler_directory.validations
+ WHERE
+ "h_alias" = $1
+ ;`
+ // Execute Query
+ rows, err := db.Query(query, hAlias)
+ if err != nil {
+ return []Validation{}, err
+ }
+ defer rows.Close()
+ var validations = make([]Validation, 0)
+ // Iterate over first
+ for rows.Next() {
+ var v Validation
+ err = rows.Scan(
+ &v.CreatedAt,
+ &v.HAlias,
+ &v.Duration,
+ &v.TargetURI,
+ &v.Challenge,
+ &v.ChallengeSent,
+ &v.RequiresPayment,
+ &v.SolutionAttemptCount,
+ &v.LastSolutionTimeframeStart,
+ &v.OrderID,
+ &v.ValidatorName,
+ )
+ if err != nil {
+ return validations, err
+ }
+ validations = append(validations, v)
+ }
+ return validations, nil
+}
+
+// Get Hash-salted alias from database
+func GetFirstValidationByHAliasFromDatabase(db *sql.DB, v *Validation, hAlias string) error {
+ query := `SELECT
+ "created_at",
+ "h_alias",
+ "duration",
+ "target_uri",
+ "challenge",
+ "challenge_sent",
+ "requires_payment",
+ "solution_attempt_count",
+ "last_solution_timeframe_start",
+ "order_id",
+ "validator_name"
+ FROM taler_directory.validations
+ WHERE
+ "h_alias"=$1
+ ;`
+ // Execute Query
+ rows, err := db.Query(query, hAlias)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+ // Iterate over first
+ if !rows.Next() {
+ return errors.New("Validation not found")
+ }
+ return rows.Scan(
+ &v.CreatedAt,
+ &v.HAlias,
+ &v.Duration,
+ &v.TargetURI,
+ &v.Challenge,
+ &v.ChallengeSent,
+ &v.RequiresPayment,
+ &v.SolutionAttemptCount,
+ &v.LastSolutionTimeframeStart,
+ &v.OrderID,
+ &v.ValidatorName,
+ )
+}
+
+// Get Hash-salted alias from database
+func GetAllEntriesFromDatabase(db *sql.DB) ([]Entry, error) {
+ query := `SELECT
+ "hs_alias",
+ "created_at",
+ "target_uri",
+ "duration"
+ FROM taler_directory.entries
+ WHERE
+ "1" = "1"
+ ;`
+ // Execute Query
+ rows, err := db.Query(query)
+ if err != nil {
+ return []Entry{}, err
+ }
+ defer rows.Close()
+ var entries = make([]Entry, 0)
+ for rows.Next() {
+ var e Entry
+ err = rows.Scan(
+ &e.HsAlias,
+ &e.CreatedAt,
+ &e.TargetURI,
+ &e.Duration,
+ )
+ if err != nil {
+ return entries, err
+ }
+ entries = append(entries, e)
+ }
+ return entries, nil
+}
+
+// Get Hash-salted alias from database
+func GetEntryByHsAliasFromDatabase(db *sql.DB, e *Entry, hsAlias string) error {
+ query := `SELECT
+ "hs_alias",
+ "created_at",
+ "target_uri",
+ "duration"
+ FROM taler_directory.entries
+ WHERE
+ "hs_alias"=$1
+ ;`
+ // Execute Query
+ rows, err := db.Query(query, hsAlias)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+ // Iterate over first
+ if !rows.Next() {
+ return errors.New("Entry not found")
+ }
+ return rows.Scan(
+ &e.HsAlias,
+ &e.CreatedAt,
+ &e.TargetURI,
+ &e.Duration,
+ )
+}
+
+// DeleteStaleValidationsFromDatabase purges stale validations
+func DeleteStaleValidationsFromDatabase(db *sql.DB, validationExpiration time.Duration) (int64, error) {
+ var ctx context.Context
+ ctx, stop := context.WithCancel(context.Background())
+ defer stop()
+ conn, err := db.Conn(ctx)
+ if err != nil {
+ return 0, err
+ }
+ defer conn.Close()
+ query := `DELETE
+ FROM taler_directory.validations
+ WHERE
+ "created_at" < $1
+ ;`
+ // Execute Query
+ cutoffTime := time.Now().Add(-validationExpiration)
+ result, err := conn.ExecContext(ctx, query, cutoffTime.UnixMicro())
+ if err != nil {
+ return 0, err
+ }
+ rows, err := result.RowsAffected()
+ if err != nil {
+ return 0, err
+ }
+ return rows, nil
+}
+
+func ClearDatabase(db *sql.DB) error {
+ _, err := DeleteAllEntriesFromDatabase(db)
+ if err != nil {
+ return err
+ }
+ _, err = DeleteAllValidationsFromDatabase(db)
+ return err
+}
+
+// DeleteValidationsByHAliasFromDatabase purges Validations
+func DeleteValidationsByHAliasFromDatabase(db *sql.DB, hAlias string) (int64, error) {
+ var ctx context.Context
+ ctx, stop := context.WithCancel(context.Background())
+ defer stop()
+ conn, err := db.Conn(ctx)
+ if err != nil {
+ return 0, err
+ }
+ defer conn.Close()
+ query := `DELETE
+ FROM taler_directory.validations
+ WHERE
+ "h_alias" = $1
+ ;`
+ // Execute Query
+ result, err := conn.ExecContext(ctx, query, hAlias)
+ if err != nil {
+ return 0, err
+ }
+ rows, err := result.RowsAffected()
+ if err != nil {
+ return 0, err
+ }
+ return rows, nil
+}
+
+// Purge Validations
+func DeleteAllValidationsFromDatabase(db *sql.DB) (int64, error) {
+ var ctx context.Context
+ ctx, stop := context.WithCancel(context.Background())
+ defer stop()
+ conn, err := db.Conn(ctx)
+ if err != nil {
+ return 0, err
+ }
+ defer conn.Close()
+ query := `DELETE
+ FROM taler_directory.validations
+ WHERE
+ 1 = 1
+ ;`
+ // Execute Query
+ result, err := conn.ExecContext(ctx, query)
+ if err != nil {
+ return 0, err
+ }
+ rows, err := result.RowsAffected()
+ if err != nil {
+ return 0, err
+ }
+ return rows, nil
+}
+
+// Purge Validations
+func DeleteValidationFromDatabase(db *sql.DB, v *Validation) (int64, error) {
+ return DeleteValidationsByHAliasFromDatabase(db, v.HAlias)
+}
+
+// Purge Entries
+func DeleteAllEntriesFromDatabase(db *sql.DB) (int64, error) {
+ var ctx context.Context
+ ctx, stop := context.WithCancel(context.Background())
+ defer stop()
+ conn, err := db.Conn(ctx)
+ if err != nil {
+ return 0, err
+ }
+ defer conn.Close()
+ query := `DELETE
+ FROM taler_directory.entries
+ WHERE
+ 1=1
+ ;`
+ // Execute Query
+ result, err := conn.ExecContext(ctx, query)
+ if err != nil {
+ return 0, err
+ }
+ rows, err := result.RowsAffected()
+ if err != nil {
+ return 0, err
+ }
+ return rows, nil
+}
+
+// Delete Entry
+func DeleteEntryFromDatabase(db *sql.DB, e *Entry) (int64, error) {
+ var ctx context.Context
+ ctx, stop := context.WithCancel(context.Background())
+ defer stop()
+ conn, err := db.Conn(ctx)
+ if err != nil {
+ return 0, err
+ }
+ defer conn.Close()
+ query := `DELETE
+ FROM taler_directory.entries
+ WHERE
+ "hs_alias" = $1
+ ;`
+ // Execute Query
+ result, err := conn.ExecContext(ctx, query, e.HsAlias)
+ if err != nil {
+ return 0, err
+ }
+ rows, err := result.RowsAffected()
+ if err != nil {
+ return 0, err
+ }
+ return rows, nil
+}
+
+// Update Entry in database
+func UpdateEntryInDatabase(db *sql.DB, e *Entry) error {
+ query := `UPDATE taler_directory.entries
+ SET
+ "created_at" = $2,
+ "target_uri" = $3,
+ "duration" = $4
+ WHERE "hs_alias" = $1;`
+ rows, err := db.Query(query, e.HsAlias, e.CreatedAt, e.TargetURI, e.Duration)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+ return nil
+}
+
+// Insert new Entry into database
+func InsertEntryIntoDatabase(db *sql.DB, e *Entry) error {
+ e.CreatedAt = time.Now().UnixMicro()
+ query := `INSERT INTO taler_directory.entries
+ VALUES (DEFAULT, $1, $2, $3, $4);`
+ rows, err := db.Query(query, e.HsAlias, e.CreatedAt, e.TargetURI, e.Duration)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+ return nil
}
diff --git a/pkg/taldir/taldir.go b/pkg/taldir/taldir.go
@@ -40,15 +40,15 @@ import (
"strings"
"time"
+ "database/sql"
"github.com/gertd/go-pluralize"
"github.com/gorilla/mux"
"github.com/kataras/i18n"
+ _ "github.com/lib/pq"
"github.com/schanzen/taler-go/pkg/merchant"
tos "github.com/schanzen/taler-go/pkg/rest"
talerutil "github.com/schanzen/taler-go/pkg/util"
"github.com/skip2/go-qrcode"
- "gorm.io/gorm"
- "gorm.io/gorm/logger"
"taler.net/taldir/internal/gana"
"taler.net/taldir/internal/util"
)
@@ -60,7 +60,7 @@ type Taldir struct {
Router *mux.Router
// The main DB handle
- DB *gorm.DB
+ DB *sql.DB
// Our configuration from the config.json
Cfg TaldirConfig
@@ -182,46 +182,6 @@ type RegisterMessage struct {
Duration int64 `json:"duration"`
}
-// Validation is the object created when a registration for an entry is initiated.
-// The Validation stores the identity key (sha256(identity)) the secret
-// Validation reference. The Validation reference is sent to the identity
-// depending on the out-of-band channel defined through the identity key type.
-type Validation struct {
-
- // ORM
- gorm.Model `json:"-"`
-
- // The hash (SHA512) of the alias
- HAlias string `json:"h_alias"`
-
- // For how long should the registration last
- Duration int64 `json:"duration"`
-
- // Target URI to associate with this alias
- TargetURI string `json:"target_uri"`
-
- // The activation code sent to the client
- Challenge string `json:"-"`
-
- // The challenge has been sent already
- ChallengeSent bool `json:"-"`
-
- // true if this validation also requires payment
- RequiresPayment bool `json:"-"`
-
- // How often was a solution for this validation tried
- SolutionAttemptCount int
-
- // The beginning of the last solution timeframe
- LastSolutionTimeframeStart time.Time
-
- // The order ID associated with this validation
- OrderID string `json:"-"`
-
- // Name of the validator
- ValidatorName string
-}
-
// ErrorDetail is the detailed error payload returned from Taldir endpoints
type ErrorDetail struct {
@@ -295,7 +255,7 @@ func (t *Taldir) getSingleEntry(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
var entry Entry
hsAlias := saltHAlias(vars["h_alias"], t.Salt)
- var err = t.DB.First(&entry, "hs_alias = ?", hsAlias).Error
+ var err = GetEntryByHsAliasFromDatabase(t.DB, &entry, hsAlias)
if err == nil {
w.Header().Set("Content-Type", "application/json")
resp, _ := json.Marshal(entry)
@@ -328,8 +288,10 @@ func (t *Taldir) disseminateStart(e Entry) {
// Disseminate all entries
func (t *Taldir) disseminateEntries() error {
- var entries []Entry
- t.DB.Where("1 = 1").Find(&entries)
+ entries, err := GetAllEntriesFromDatabase(t.DB)
+ if nil != err {
+ return err
+ }
for _, e := range entries {
t.disseminateStart(e)
}
@@ -378,75 +340,89 @@ func (t *Taldir) validationRequest(w http.ResponseWriter, r *http.Request) {
w.Write(resp)
return
}
- err = t.DB.First(&validation, "h_alias = ?", vars["h_alias"]).Error
+ err = GetFirstValidationByHAliasFromDatabase(t.DB, &validation, vars["h_alias"])
+ t.Logger.Logf(LogDebug, "Got validation %v", validation)
if err != nil {
w.WriteHeader(http.StatusNotFound)
return
}
validation.SolutionAttemptCount++
- if validation.LastSolutionTimeframeStart.Add(t.SolutionTimeframe).After(time.Now()) {
+ if time.UnixMicro(validation.LastSolutionTimeframeStart + t.SolutionTimeframe.Microseconds()).After(time.Now()) {
if validation.SolutionAttemptCount > t.SolutionAttemptsMax {
w.WriteHeader(http.StatusTooManyRequests)
return
}
} else {
t.Logger.Logf(LogDebug, "New solution timeframe set.")
- validation.LastSolutionTimeframeStart = time.Now()
+ validation.LastSolutionTimeframeStart = time.Now().UnixMicro()
validation.SolutionAttemptCount = 1
}
- t.DB.Save(&validation)
+ UpdateValidationInDatabase(t.DB, &validation)
+ t.Logger.Logf(LogDebug, "Generating solution from %s and %s", validation.TargetURI, validation.Challenge)
expectedSolution := util.GenerateSolution(validation.TargetURI, validation.Challenge)
+
t.Logger.Logf(LogDebug, "Expected solution: `%s', given: `%s'\n", expectedSolution, confirm.Solution)
if confirm.Solution != expectedSolution {
w.WriteHeader(http.StatusForbidden)
return
}
- err = t.DB.Delete(&validation).Error
+ _, err = DeleteValidationFromDatabase(t.DB, &validation)
if err != nil {
- t.Logger.Logf(LogError, "Error deleting validation")
+ t.Logger.Logf(LogError, "Error deleting validation: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
entry.HsAlias = saltHAlias(validation.HAlias, t.Salt)
- entry.TargetURI = validation.TargetURI
- tmpDuration := (entry.Duration.Microseconds() + validation.Duration) * 1000
- entry.Duration = time.Duration(tmpDuration)
- err = t.DB.First(&entry, "hs_alias = ?", entry.HsAlias).Error
+ tmpDuration := (entry.Duration + validation.Duration) * 1000
+ err = GetEntryByHsAliasFromDatabase(t.DB, &entry, entry.HsAlias)
if err == nil {
if validation.TargetURI == "" {
- t.Logger.Logf(LogDebug, "Deleted entry for '%s´\n", entry.HsAlias)
- err = t.DB.Delete(&entry).Error
+ _, err = DeleteEntryFromDatabase(t.DB, &entry)
if err != nil {
+ t.Logger.Logf(LogError, "Error deleting entry: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
+ t.Logger.Logf(LogDebug, "Deleted entry for '%s´\n", entry.HsAlias)
t.disseminateStop(entry)
} else {
- t.DB.Save(&entry)
+ entry.TargetURI = validation.TargetURI
+ entry.Duration = tmpDuration
+ err = UpdateEntryInDatabase(t.DB, &entry)
+ t.Logger.Logf(LogDebug, "Updated entry in database to: %v", entry)
+ if err != nil {
+ t.Logger.Logf(LogError, "Error updating entry: %v", err)
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
t.disseminateStart(entry)
}
} else {
+ t.Logger.Logf(LogError, "Entry does not yet exist: %v", err)
if validation.TargetURI == "" {
t.Logger.Logf(LogWarning, "Validated a deletion request but no entry found for `%s'\n", entry.HsAlias)
} else {
- err = t.DB.Create(&entry).Error
+ entry.TargetURI = validation.TargetURI
+ err = InsertEntryIntoDatabase(t.DB, &entry)
if err != nil {
+ t.Logger.Logf(LogError, "Error inserting entry: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
+ t.Logger.Logf(LogError, "Inserted entry: %v", entry)
}
}
w.WriteHeader(http.StatusNoContent)
}
func (t *Taldir) isRateLimited(hAlias string) (bool, error) {
- var validations []Validation
- res := t.DB.Where("h_alias = ?", hAlias).Find(&validations)
+ validations, err := GetAllValidationsByHAliasFromDatabase(t.DB, hAlias)
// NOTE: Check rate limit
- if res.Error == nil {
+ if err == nil {
// Limit re-initiation attempts to ValidationInitiationMax times
// within the expiration timeframe of a validation.
- return res.RowsAffected >= t.ValidationInitiationMax, nil
+ t.Logger.Logf(LogDebug, "Pending validations are %d", len(validations))
+ return len(validations) >= int(t.ValidationInitiationMax), nil
}
return false, nil
}
@@ -457,6 +433,7 @@ func (t *Taldir) registerRequest(w http.ResponseWriter, r *http.Request) {
var errDetail ErrorDetail
var validation Validation
var entry Entry
+
// Check if this validation method is supported or not.
validator, ok := t.Validators[vars["alias_type"]]
if !ok {
@@ -482,6 +459,7 @@ func (t *Taldir) registerRequest(w http.ResponseWriter, r *http.Request) {
w.Write(resp)
return
}
+ t.Logger.Logf(LogDebug, "Received registerRequest %v", req)
if req.TargetURI != "" {
err = t.isPMSValid(req.TargetURI)
@@ -503,17 +481,20 @@ func (t *Taldir) registerRequest(w http.ResponseWriter, r *http.Request) {
validation.HAlias = hAlias
validation.ValidatorName = validator.Name()
hsAlias := saltHAlias(validation.HAlias, t.Salt)
- err = t.DB.First(&entry, "hs_alias = ?", hsAlias).Error
+ err = GetEntryByHsAliasFromDatabase(t.DB, &entry, hsAlias)
// Round to the nearest multiple of a month
reqDuration := time.Duration(req.Duration * 1000)
reqDuration = reqDuration.Round(monthDuration)
if err == nil {
+ t.Logger.Logf(LogDebug, "Found entry in database %v matching %v", entry, hsAlias)
// Check if this entry is to be modified or extended
entryModified := (req.TargetURI != entry.TargetURI)
- entryValidity := entry.CreatedAt.Add(entry.Duration)
+ entryValidity := time.UnixMicro(entry.CreatedAt + entry.Duration)
// NOTE: The extension must be at least one month
+ t.Logger.Logf(LogDebug, "Entry to be modified: %t, requested (rounded) duration: %d", entryModified, reqDuration.Microseconds())
if (reqDuration.Microseconds() == 0) && !entryModified {
// Nothing changed. Return validity
+ t.Logger.Logf(LogDebug, "Returning validity of entry")
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
w.Write(fmt.Appendf(make([]byte, 0), "{\"valid_for\": %d}", time.Until(entryValidity).Microseconds()))
@@ -534,19 +515,26 @@ func (t *Taldir) registerRequest(w http.ResponseWriter, r *http.Request) {
}
jsonResp, _ := json.Marshal(rlResponse)
w.Write(jsonResp)
- t.DB.Delete(&validation)
+ DeleteValidationFromDatabase(t.DB, &validation)
return
}
- err = t.DB.First(&validation, "h_alias = ? AND target_uri = ? AND duration = ?",
- hAlias, req.TargetURI, reqDuration).Error
+ t.Logger.Logf(LogDebug, "Looking for validation with %v %v %v\n", hAlias, req.TargetURI, reqDuration.Microseconds())
+ err = GetValidationFromDatabase(t.DB, &validation, hAlias, req.TargetURI, reqDuration)
validationExists := (nil == err)
+ t.Logger.Logf(LogDebug, "Validation exists %v\n", validationExists)
// FIXME: Always set new challenge?
validation.Challenge = util.GenerateChallenge(t.ChallengeBytes)
- if !validationExists {
- validation.TargetURI = req.TargetURI
- validation.SolutionAttemptCount = 0
- validation.LastSolutionTimeframeStart = time.Now()
- validation.Duration = reqDuration.Microseconds()
+ validation.TargetURI = req.TargetURI
+ validation.SolutionAttemptCount = 0
+ validation.LastSolutionTimeframeStart = time.Now().UnixMicro()
+ validation.Duration = reqDuration.Microseconds()
+ validation.CreatedAt = validation.LastSolutionTimeframeStart
+ t.Logger.Logf(LogDebug, "Storing new validation %v\n", validation)
+ err = InsertValidationIntoDatabase(t.DB, &validation)
+ if nil != err {
+ t.Logger.Logf(LogError, "Error inserting validation! %v", err)
+ w.WriteHeader(http.StatusInternalServerError)
+ return
}
sliceDuration := time.Duration(validation.Duration * 1000)
@@ -583,14 +571,19 @@ func (t *Taldir) registerRequest(w http.ResponseWriter, r *http.Request) {
return
}
if len(payto) != 0 {
- t.DB.Save(&validation)
+ err = UpdateValidationInDatabase(t.DB, &validation)
+ if nil != err {
+ t.Logger.Logf(LogError, "Error inserting validation! %v", err)
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
w.WriteHeader(http.StatusPaymentRequired)
w.Header().Set("Taler", payto) // FIXME no idea what to do with this.
return
}
// In this case, this order was paid
}
- err = t.DB.Save(&validation).Error
+ err = UpdateValidationInDatabase(t.DB, &validation)
if err != nil {
t.Logger.Logf(LogError, "%s\n", err.Error())
w.WriteHeader(http.StatusInternalServerError)
@@ -602,7 +595,7 @@ func (t *Taldir) registerRequest(w http.ResponseWriter, r *http.Request) {
redirectionLink, err := validator.RegistrationStart(topic, link, message, req.Alias, validation.Challenge)
if err != nil {
t.Logger.Logf(LogError, "%s\n", err.Error())
- t.DB.Delete(&validation)
+ DeleteValidationFromDatabase(t.DB, &validation)
w.WriteHeader(http.StatusInternalServerError)
return
}
@@ -667,7 +660,7 @@ func (t *Taldir) validationPage(w http.ResponseWriter, r *http.Request) {
var png []byte
var validation Validation
- err := t.DB.First(&validation, "h_alias = ?", vars["h_alias"]).Error
+ err := GetFirstValidationByHAliasFromDatabase(t.DB, &validation, vars["h_alias"])
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if err != nil {
// This validation does not exist.
@@ -740,8 +733,10 @@ func (t *Taldir) validationPage(w http.ResponseWriter, r *http.Request) {
// ClearDatabase nukes the database (for tests)
func (t *Taldir) ClearDatabase() {
- t.DB.Where("1 = 1").Delete(&Entry{})
- t.DB.Where("1 = 1").Delete(&Validation{})
+ err := ClearDatabase(t.DB)
+ if err != nil {
+ t.Logger.Logf(LogWarning, "Error clearing database: %v", err)
+ }
}
func (t *Taldir) termsResponse(w http.ResponseWriter, r *http.Request) {
@@ -842,7 +837,7 @@ func (t *Taldir) typeLookupResultPage(w http.ResponseWriter, r *http.Request) {
hAliasBin := HashAlias(val.Name(), r.URL.Query().Get("alias"))
hAlias := util.Base32CrockfordEncode(hAliasBin[:])
hsAlias := saltHAlias(hAlias, t.Salt)
- err = t.DB.First(&entry, "hs_alias = ?", hsAlias).Error
+ err := GetEntryByHsAliasFromDatabase(t.DB, &entry, hsAlias)
if err != nil {
t.Logger.Logf(LogError, "`%s` not found.\n", hAlias)
} else {
@@ -968,6 +963,7 @@ func (t *Taldir) Initialize(cfg TaldirConfig) {
t.Cfg = cfg
t.Logger = TaldirLogger{
InternalLogger: log.New(os.Stdout, "taler-directory:", log.LstdFlags),
+ logLevel: cfg.Loglevel,
}
// FIXME localedir
i18n, err := i18n.New(i18n.Glob("./locales/*/*", i18n.LoaderConfig{
@@ -1046,23 +1042,14 @@ func (t *Taldir) Initialize(cfg TaldirConfig) {
}
t.MonthlyFee = cfg.Ini.Section("taldir").Key("monthly_fee").MustString("KUDOS:0")
- _db, err := gorm.Open(cfg.Db, &gorm.Config{
- Logger: logger.Default.LogMode(logger.Silent),
- })
- if err != nil {
- panic(err)
- }
- t.DB = _db
- if err := t.DB.AutoMigrate(&Entry{}); err != nil {
- panic(err)
- }
- if err := t.DB.AutoMigrate(&Validation{}); err != nil {
- panic(err)
- }
+ t.DB = cfg.Db
if cfg.Ini.Section("taldir").Key("purge_mappings_on_startup_dangerous").MustBool(false) {
t.Logger.Logf(LogWarning, "DANGER Purging mappings!")
- tx := t.DB.Where("1 = 1").Delete(&Entry{})
- t.Logger.Logf(LogDebug, "Deleted %d entries.\n", tx.RowsAffected)
+ num, err := DeleteAllEntriesFromDatabase(t.DB)
+ if err != nil {
+ t.Logger.Logf(LogDebug, "Error purging entries: `%v'.\n", err)
+ }
+ t.Logger.Logf(LogDebug, "Deleted %d entries.\n", num)
}
// Clean up validations
validationExpStr := cfg.Ini.Section("taldir").Key("validation_expiration").MustString("24h")
@@ -1073,8 +1060,11 @@ func (t *Taldir) Initialize(cfg TaldirConfig) {
}
go func() {
for {
- tx := t.DB.Where("created_at < ?", time.Now().Add(-validationExp)).Delete(&Validation{})
- t.Logger.Logf(LogInfo, "Cleaned up %d stale validations.\n", tx.RowsAffected)
+ num, err := DeleteStaleValidationsFromDatabase(t.DB, validationExp)
+ if err != nil {
+ t.Logger.Logf(LogDebug, "Error purging stale validations: `%v'.\n", err)
+ }
+ t.Logger.Logf(LogInfo, "Cleaned up %d stale validations.\n", num)
time.Sleep(validationExp)
}
}()