commit dfa72ebc7df107045f5a5ac0e0aa7ba5f4f3c1fa
parent be6f360667fff3c98797df97dd307fd31368c117
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date: Thu, 7 Dec 2023 18:23:56 +0100
merge conflict
Diffstat:
7 files changed, 404 insertions(+), 131 deletions(-)
diff --git a/gns-registrar.conf b/gns-registrar.conf
@@ -1,20 +1,19 @@
[gns-registrar]
production = false
-host = "https://taldir.gnunet.org"
+base_url = "http://localhost:7776"
+base_url_gnunet = "http://localhost:7776"
+gnunet_auth = ""
+base_url_merchant = https://backend.demo.taler.net
+merchant_token = sandbox
bind_to = "localhost:11000"
delegations_public = no
-monthly_fee = KUDOS:1
default_doc_filetype = text/html
default_doc_lang = en
default_tos_path = terms/
default_pp_path = privacy/
supported_doc_filetypes = text/html application/pdf application/epub application/xml text/plain
-merchant_baseurl_private = http://merchant.taldir/instances/myInstance
-merchant_token = superSecretToken
registrar_landing = web/templates/landing.html
registrar_name = web/templates/name.html
registration_failed = templates/registration_failed.html
-registration_duration = 1y
root_zone_name = test
suffix_hint = gnunet.gns.alt
-registration_policy = fcfs
diff --git a/go.mod b/go.mod
@@ -3,8 +3,15 @@ module gnunet.org/gnunet-gns-registrar
go 1.18
require (
- github.com/gorilla/mux v1.8.0
+ github.com/gorilla/mux v1.8.1
+ github.com/schanzen/taler-go v0.0.3
+ github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
+ golang.org/x/text v0.14.0
gopkg.in/ini.v1 v1.67.0
)
-require github.com/schanzen/taler-go v0.0.3 // indirect
+require (
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/pkg/rest/gnsregistrar.go b/pkg/rest/gnsregistrar.go
@@ -20,18 +20,36 @@ package gnsregistrar
import (
"bytes"
+ "encoding/base64"
"encoding/json"
+ "errors"
"fmt"
"html/template"
"io"
"net/http"
"os"
+ "strconv"
+ "strings"
+ "time"
"github.com/gorilla/mux"
+ "github.com/skip2/go-qrcode"
+ "golang.org/x/text/currency"
+ "golang.org/x/text/language"
+ "golang.org/x/text/message"
"gopkg.in/ini.v1"
"github.com/schanzen/taler-go/pkg/merchant"
+ talerutil "github.com/schanzen/taler-go/pkg/util"
)
+
+type RegistrationMetadata struct {
+ Expiration uint64 `json:"expiration"`
+ Paid bool `json:"paid"`
+ OrderID string `json:"order_id"`
+ NeedsPaymentUntil time.Time `json:"needs_payment_until"`
+}
+
type IdentityInfo struct {
Pubkey string `json:"pubkey"`
Name string `json:"name"`
@@ -51,6 +69,7 @@ type RecordData struct {
IsRelativeExpiration bool `json:"is_relative_expiration"`
IsSupplemental bool `json:"is_supplemental"`
IsShadow bool `json:"is_shadow"`
+ IsMaintenance bool `json:"is_maintenance"`
}
@@ -77,19 +96,24 @@ type Registrar struct {
// name page
NameTpl *template.Template
- // all names page
- AllNamesTpl *template.Template
+ // buy names page
+ BuyTpl *template.Template
// Merchant object
Merchant merchant.Merchant
// Relative record expiration (NOT registration expiration!)
- RelativeDelegationExpiration uint64
+ RelativeDelegationExpiration time.Duration
+
+ // Registration expiration (NOT record expiration!)
+ RelativeRegistrationExpiration time.Duration
+
+ // Payment expiration (time you have to pay for registration)
+ PaymentExpiration time.Duration
// Name of our root zone
RootZoneName string
-
// Key of our root zone
RootZoneKey string
@@ -99,14 +123,12 @@ type Registrar struct {
// Gnunet REST API basename
GnunetUrl string
- // Registration policy; fcfs or paid
- RegistrationPolicy string
-
+ // Registrar base URL
+ BaseUrl string
+
// Cost for a registration
- RegistrationCost uint64
+ RegistrationCost *talerutil.Amount
- // Currency to use
- RegistrationCostCurrency string
}
type VersionResponse struct {
@@ -125,6 +147,22 @@ func (t *Registrar) configResponse(w http.ResponseWriter, r *http.Request) {
w.Write(response)
}
+// FIXME: Implement https://docs.taler.net/design-documents/051-fractional-digits.html and move to taler-go
+func (t *Registrar) localizedAmountString() (string) {
+ p := message.NewPrinter(language.English)
+ costStr := t.RegistrationCost.String()
+ costSplit := strings.Split(costStr, ":")
+ left := costSplit[0]
+ right := costSplit[1]
+ cur, err := currency.ParseISO(left)
+ if nil != err {
+ return fmt.Sprintf("%s %s", right, left)
+ }
+ amf, _ := strconv.ParseFloat(right, 8)
+ v := currency.Symbol(cur.Amount(amf))
+ return p.Sprint(v)
+}
+
func (t *Registrar) landingPage(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
@@ -139,68 +177,149 @@ func (t *Registrar) landingPage(w http.ResponseWriter, r *http.Request) {
func (t *Registrar) registerName(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
var namestoreRequest NamestoreRecord
- var delegationRecord RecordData
+ var delegationRecord RecordData
+ var metadataRecord RecordData
var gnunetError GnunetError
+ var registrationMetadata RegistrationMetadata
w.Header().Set("Content-Type", "text/html; charset=utf-8")
delegationRecord.IsPrivate = false
delegationRecord.IsRelativeExpiration = true
delegationRecord.IsSupplemental = false
+ delegationRecord.IsMaintenance = false
delegationRecord.IsShadow = false
- delegationRecord.RecordType = "PKEY" // FIXME
- delegationRecord.RelativeExpiration = t.RelativeDelegationExpiration
+ delegationRecord.RecordType = guessDelegationRecordType(r.URL.Query().Get("zkey"))
+ delegationRecord.RelativeExpiration = uint64(t.RelativeDelegationExpiration.Microseconds())
delegationRecord.Value = r.URL.Query().Get("zkey")
+ metadataRecord.IsPrivate = true
+ metadataRecord.IsRelativeExpiration = true
+ metadataRecord.IsSupplemental = false
+ metadataRecord.IsMaintenance = true
+ metadataRecord.IsShadow = false
+ metadataRecord.RecordType = "TXT" // FIXME use new recory type
+ metadataRecord.RelativeExpiration = uint64(t.RelativeDelegationExpiration.Microseconds())
+ registrationMetadata = RegistrationMetadata{
+ Paid: false,
+ Expiration: uint64(time.Now().Add(t.RelativeRegistrationExpiration).UnixMicro()),
+ }
+ metadataRecordValue, err := json.Marshal(registrationMetadata)
+ if nil != err {
+ http.Redirect(w, r, "/name?label="+vars["label"] + "&error=Registration failed", http.StatusSeeOther)
+ return
+ }
+ metadataRecord.Value = string(metadataRecordValue)
namestoreRequest.RecordName = vars["label"]
- namestoreRequest.Records = []RecordData{delegationRecord}
- reqString, _ := json.Marshal(namestoreRequest)
- // FIXME handle errors here
- fmt.Println(namestoreRequest)
+ namestoreRequest.Records = []RecordData{delegationRecord,metadataRecord}
+ reqString, _ := json.Marshal(namestoreRequest)
+ // FIXME handle errors here
+ fmt.Println(namestoreRequest)
resp, err := http.Post(t.GnunetUrl+"/namestore/" + t.RootZoneName, "application/json", bytes.NewBuffer(reqString))
- resp.Body.Close()
+ resp.Body.Close()
if http.StatusNoContent != resp.StatusCode {
fmt.Printf("Got error: %d\n", resp.StatusCode)
json.NewDecoder(resp.Body).Decode(&gnunetError)
fmt.Println(gnunetError.Code)
fmt.Println(err)
- http.Redirect(w, r, "/name?label="+r.URL.Query().Get("label") + "&error=" + gnunetError.Description, http.StatusSeeOther)
+ http.Redirect(w, r, "/name?label="+vars["label"] + "&error=" + gnunetError.Description, http.StatusSeeOther)
return
}
http.Redirect(w, r, "/name/"+ vars["label"] + "?registered=true", http.StatusSeeOther)
return
}
-
func (t *Registrar) searchPage(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
http.Redirect(w, r, "/name/"+r.URL.Query().Get("label"), http.StatusSeeOther)
return
}
-func (t *Registrar) buyPage(w http.ResponseWriter, r *http.Request) {
- vars := mux.Vars(r)
- if t.RegistrationCost == 0 {
- http.Redirect(w, r, "/name/"+vars["label"]+"/register?zkey="+r.URL.Query().Get("zkey"), http.StatusSeeOther)
+func (t *Registrar) expireRegistration(label string) (error) {
+ var gnunetError GnunetError
+ client := &http.Client{}
+ req, _ := http.NewRequest(http.MethodDelete,t.GnunetUrl+"/namestore/" + t.RootZoneName + "/" + label, nil)
+ resp, err := client.Do(req)
+ resp.Body.Close()
+ if err != nil {
+ return err
}
- http.Redirect(w, r, "/name/"+vars["label"]+"?error=Not implemented", http.StatusSeeOther)
- return
+ if http.StatusNotFound == resp.StatusCode {
+ return nil
+ }
+ if http.StatusNoContent != resp.StatusCode {
+ fmt.Printf("Got error: %d\n", resp.StatusCode)
+ err = json.NewDecoder(resp.Body).Decode(&gnunetError)
+ return errors.New("GNUnet REST API error: " + gnunetError.Description)
+ }
+ return nil
}
-func (t *Registrar) namePage(w http.ResponseWriter, r *http.Request) {
- var namestoreResponse NamestoreRecord
+func (t *Registrar) createOrUpdateRegistration(nsRecord *NamestoreRecord) (error) {
+ var gnunetError GnunetError
+ reqString, _ := json.Marshal(nsRecord)
+ fmt.Println(nsRecord)
+ client := &http.Client{}
+ req, _ := http.NewRequest(http.MethodPut,t.GnunetUrl+"/namestore/" + t.RootZoneName, bytes.NewBuffer(reqString))
+ resp, err := client.Do(req)
+ if nil != err {
+ return err
+ }
+ resp.Body.Close()
+ if http.StatusNoContent != resp.StatusCode {
+ fmt.Printf("Got error: %d\n", resp.StatusCode)
+ err = json.NewDecoder(resp.Body).Decode(&gnunetError)
+ return errors.New("GNUnet REST API error: " + gnunetError.Description)
+ }
+ return nil
+}
+
+func (t *Registrar) setupRegistrationMetadataBeforePayment(label string, zkey string, orderId string, paymentUntil time.Time) (error) {
+ var namestoreRequest NamestoreRecord
+ var delegationRecord RecordData
+ var metadataRecord RecordData
+ var registrationMetadata RegistrationMetadata
+ delegationRecord.IsPrivate = true // Private until payment is through
+ delegationRecord.IsRelativeExpiration = true
+ delegationRecord.IsSupplemental = false
+ delegationRecord.IsMaintenance = false
+ delegationRecord.IsShadow = false
+ delegationRecord.RecordType = guessDelegationRecordType(zkey)
+ delegationRecord.RelativeExpiration = uint64(t.RelativeDelegationExpiration.Microseconds())
+ delegationRecord.Value = zkey
+ metadataRecord.IsPrivate = true
+ metadataRecord.IsRelativeExpiration = true
+ metadataRecord.IsSupplemental = false
+ metadataRecord.IsMaintenance = true
+ metadataRecord.IsShadow = false
+ metadataRecord.RecordType = "TXT" // FIXME use new recory type
+ metadataRecord.RelativeExpiration = uint64(t.RelativeDelegationExpiration.Microseconds())
+ registrationMetadata = RegistrationMetadata{
+ Paid: false,
+ OrderID: orderId,
+ NeedsPaymentUntil: paymentUntil,
+ Expiration: uint64(time.Now().Add(t.RelativeRegistrationExpiration).UnixMicro()),
+ }
+ metadataRecordValue, err := json.Marshal(registrationMetadata)
+ if nil != err {
+ return err
+ }
+ metadataRecord.Value = string(metadataRecordValue)
+ namestoreRequest.RecordName = label
+ namestoreRequest.Records = []RecordData{delegationRecord,metadataRecord}
+ return t.createOrUpdateRegistration(&namestoreRequest)
+}
+
+
+func (t *Registrar) buyPage(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
+ var namestoreResponse NamestoreRecord
+ var regMetadata *RegistrationMetadata
w.Header().Set("Content-Type", "text/html; charset=utf-8")
- var available = false
- var value = ""
- var registered = r.URL.Query().Get("registered") == "true"
- // FIXME redirect back if label empty
- resp, err := http.Get(t.GnunetUrl + "/namestore/" + t.RootZoneName + "/" + vars["label"])
+ resp, err := http.Get(t.GnunetUrl + "/namestore/" + t.RootZoneName + "/" + vars["label"] + "?include_maintenance=yes")
if err != nil {
fmt.Printf("Failed to get zone contents")
return
}
defer resp.Body.Close()
- if http.StatusNotFound == resp.StatusCode {
- available = true
- } else if http.StatusOK == resp.StatusCode {
+ if http.StatusOK == resp.StatusCode {
respData, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Failed to get zone contents" + err.Error())
@@ -211,51 +330,184 @@ func (t *Registrar) namePage(w http.ResponseWriter, r *http.Request) {
fmt.Printf("Failed to get zone contents" + err.Error())
return
}
- value = namestoreResponse.Records[0].Value
- } else {
- fmt.Printf("Failed to get zone contents" + err.Error())
+ regMetadata, err = t.getCurrentRegistrationMetadata(vars["label"], &namestoreResponse)
+ if err != nil {
+ fmt.Printf("Failed to get registration metadata" + err.Error())
+ return
+ }
+ } else if http.StatusNotFound != resp.StatusCode {
+ http.Redirect(w, r, "/name/" + vars["label"] + "?error=Registration failed: Error determining zone status", http.StatusSeeOther)
+ return
+ }
+ var errorMsg = ""
+ if nil != regMetadata {
+ http.Redirect(w, r, "/name/"+vars["label"] + "?error=Registration failed: Pending buy order", http.StatusSeeOther)
+ return
+ }
+ orderID, newOrderErr := t.Merchant.AddNewOrder(*t.RegistrationCost, "GNS registrar name registration", "/name/" + vars["label"])
+ if newOrderErr != nil {
+ fmt.Println(newOrderErr)
+ http.Redirect(w, r, "/name/"+vars["label"] + "?error=Registration failed: Unable to create order", http.StatusSeeOther)
+ return
+ }
+ payto, paytoErr := t.Merchant.IsOrderPaid(orderID)
+ if paytoErr != nil {
+ http.Redirect(w, r, "/name/"+vars["label"] + "?error=Registration failed: Error getting payment data", http.StatusSeeOther)
return
}
+ qrPng, qrErr := qrcode.Encode(payto, qrcode.Medium, 256)
+ if qrErr != nil {
+ http.Redirect(w, r, "/name/"+vars["label"] + "?error=Registration failed: Error generating QR code", http.StatusSeeOther)
+ return
+ }
+ paymentUntil := time.Now().Add(t.PaymentExpiration)
+ err = t.setupRegistrationMetadataBeforePayment(vars["label"], r.URL.Query().Get("zkey"), orderID, paymentUntil)
+ if err != nil {
+ fmt.Println(err)
+ http.Redirect(w, r, "/name/"+vars["label"] + "?error=Registration failed: Internal error", http.StatusSeeOther)
+ return
+ }
+ encodedPng := base64.StdEncoding.EncodeToString(qrPng)
fullData := map[string]interface{}{
+ "qrCode": template.URL("data:image/png;base64," + encodedPng),
+ "payto": template.URL(payto),
+ "fulfillmentUrl": template.URL(t.BaseUrl + "/name/" + vars["label"]),
"label": vars["label"],
- "error": r.URL.Query().Get("error"),
- "cost": t.RegistrationCost,
- "currency": t.RegistrationCostCurrency,
- "available": available,
- "currentValue": value,
+ "error": errorMsg,
+ "cost": t.localizedAmountString(),
"suffixHint": t.SuffixHint,
- "registrationPolicy": t.RegistrationPolicy,
- "registrationSuccess": registered,
}
- t.NameTpl.Execute(w, fullData)
+ t.BuyTpl.Execute(w, fullData)
return
}
-func (t *Registrar) allNamesPage(w http.ResponseWriter, r *http.Request) {
- var namestoreResponse []NamestoreRecord
+func (t *Registrar) getCurrentRegistrationMetadata(label string, nsRecord *NamestoreRecord) (*RegistrationMetadata, error) {
+ var regMetadata RegistrationMetadata
+ var haveMetadata = false
+ for _, record := range nsRecord.Records {
+ if record.RecordType == "TXT" {
+ err := json.Unmarshal([]byte(record.Value), ®Metadata)
+ if err != nil {
+ fmt.Printf("Failed to get zone contents" + err.Error())
+ return nil, err
+ }
+ haveMetadata = true
+ }
+ }
+ if !haveMetadata {
+ return nil, nil
+ }
+ if !regMetadata.Paid {
+ payto, paytoErr := t.Merchant.IsOrderPaid(regMetadata.OrderID)
+ if nil != paytoErr {
+ return nil, errors.New("Error determining payment status")
+ }
+ if "" == payto {
+ // Order was paid!
+ regMetadata.Paid = true
+ var newZkeyRecord RecordData
+ var newMetaRecord RecordData
+ for _, record := range nsRecord.Records {
+ if isDelegationRecordType(record.RecordType) {
+ record.IsPrivate = false
+ newZkeyRecord = record
+ }
+ if record.RecordType == "TXT" {
+ metadataRecordValue, err := json.Marshal(regMetadata)
+ if nil != err {
+ return nil, err
+ }
+ record.Value = string(metadataRecordValue)
+ newMetaRecord = record
+ }
+ }
+ nsRecord.Records = []RecordData{newMetaRecord, newZkeyRecord}
+ t.createOrUpdateRegistration(nsRecord)
+ } else {
+ if time.Now().After(regMetadata.NeedsPaymentUntil) {
+ fmt.Printf("Payment request for %s has expired, removing\n", label)
+ t.expireRegistration(label)
+ return nil, nil
+ }
+ }
+ } else {
+ if time.Now().After(time.UnixMicro(int64(regMetadata.Expiration))) {
+ fmt.Printf("Registration for %s has expired, removing\n", label)
+ t.expireRegistration(label)
+ return nil, nil
+ }
+ }
+ return ®Metadata, nil
+}
+
+func guessDelegationRecordType(val string) (string) {
+ if strings.HasPrefix(val, "000G00") {
+ return "PKEY"
+ } else {
+ return "EDKEY"
+ }
+}
+
+func isDelegationRecordType(typ string) (bool) {
+ return typ == "PKEY" || typ == "EDKEY"
+}
+
+func (t *Registrar) namePage(w http.ResponseWriter, r *http.Request) {
+ var namestoreResponse NamestoreRecord
+ vars := mux.Vars(r)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
- resp, err := http.Get(t.GnunetUrl + "/namestore/" + t.RootZoneName)
+ var value = ""
+ var registeredUntil = ""
+ var regMetadata *RegistrationMetadata
+ var registered = r.URL.Query().Get("registered") == "true"
+ // FIXME redirect back if label empty
+ resp, err := http.Get(t.GnunetUrl + "/namestore/" + t.RootZoneName + "/" + vars["label"] + "?include_maintenance=yes")
if err != nil {
fmt.Printf("Failed to get zone contents")
return
}
defer resp.Body.Close()
- if http.StatusOK != resp.StatusCode {
- fmt.Printf("Failed to get zone contents. Retcode=%d", resp.StatusCode)
- return
+ if http.StatusOK == resp.StatusCode {
+ respData, err := io.ReadAll(resp.Body)
+ if err != nil {
+ fmt.Println("Failed to get zone contents: " + err.Error())
+ return
+ }
+ err = json.NewDecoder(bytes.NewReader(respData)).Decode(&namestoreResponse)
+ if err != nil {
+ fmt.Println("Failed to get zone contents: " + err.Error())
+ return
+ }
+ regMetadata, err = t.getCurrentRegistrationMetadata(vars["label"], &namestoreResponse)
+ if err != nil {
+ fmt.Println("Failed to get registration metadata: " + err.Error())
+ return
+ }
+ } else if http.StatusNotFound != resp.StatusCode {
+ http.Redirect(w, r, "/name/" + vars["label"] + "?error=Error retrieving zone information.", http.StatusSeeOther)
+ return
}
- respData, err := io.ReadAll(resp.Body)
- if err != nil {
- fmt.Printf("Failed to get zone contents" + err.Error())
- return
+ for _, record := range namestoreResponse.Records {
+ if isDelegationRecordType(record.RecordType) {
+ value = record.Value
+ }
}
- err = json.NewDecoder(bytes.NewReader(respData)).Decode(&namestoreResponse)
- if err != nil {
- fmt.Printf("Failed to get zone contents" + err.Error())
- return
+ if regMetadata != nil {
+ if regMetadata.Paid {
+ registeredUntil = time.UnixMicro(int64(regMetadata.Expiration)).String()
+ }
}
- fmt.Println(namestoreResponse)
- t.AllNamesTpl.Execute(w, namestoreResponse)
+ fullData := map[string]interface{}{
+ "label": vars["label"],
+ "error": r.URL.Query().Get("error"),
+ "cost": t.localizedAmountString(),
+ "available": regMetadata == nil,
+ "currentValue": value,
+ "suffixHint": t.SuffixHint,
+ "registeredUntil": registeredUntil,
+ "registrationSuccess": registered,
+ }
+ t.NameTpl.Execute(w, fullData)
return
}
@@ -267,7 +519,6 @@ func (t *Registrar) setupHandlers() {
t.Router.HandleFunc("/name/{label}/buy", t.buyPage).Methods("GET")
t.Router.HandleFunc("/name/{label}/register", t.registerName).Methods("GET")
t.Router.HandleFunc("/search", t.searchPage).Methods("GET")
- t.Router.HandleFunc("/names", t.allNamesPage).Methods("GET")
/* ToS API */
// t.Router.HandleFunc("/terms", t.termsResponse).Methods("GET")
@@ -302,17 +553,25 @@ func (t *Registrar) Initialize(cfgfile string) {
if err != nil {
fmt.Println(err)
}
- allNamesTplFile := t.Cfg.Section("gns-registrar").Key("registrar_all_names").MustString("web/templates/names.html")
- t.AllNamesTpl, err = template.ParseFiles(allNamesTplFile)
+ buyTplFile := t.Cfg.Section("gns-registrar").Key("buy_template").MustString("web/templates/buy.html")
+ t.BuyTpl, err = template.ParseFiles(buyTplFile)
if err != nil {
fmt.Println(err)
}
- t.RegistrationCost = t.Cfg.Section("gns-registrar").Key("registration_cost").MustUint64(0)
- t.RelativeDelegationExpiration = t.Cfg.Section("gns-registrar").Key("relative_delegation_expiration").MustUint64(86400000)
- t.RegistrationCostCurrency = t.Cfg.Section("gns-registrar").Key("registration_cost_currency").MustString("EUR")
+ paymentExp := t.Cfg.Section("gns-registrar").Key("payment_required_expiration").MustString("48h")
+ recordExp := t.Cfg.Section("gns-registrar").Key("relative_delegation_expiration").MustString("24h")
+ registrationExp := t.Cfg.Section("gns-registrar").Key("registration_expiration").MustString("8760h")
+ t.RelativeRegistrationExpiration, _ = time.ParseDuration(registrationExp)
+ t.RelativeDelegationExpiration, _ = time.ParseDuration(recordExp)
+ t.PaymentExpiration, _ = time.ParseDuration(paymentExp)
+ fmt.Println(t.RelativeDelegationExpiration)
+ fmt.Println(t.RelativeRegistrationExpiration)
+ costStr := t.Cfg.Section("gns-registrar").Key("registration_cost").MustString("KUDOS:0.3")
+ t.RegistrationCost, err = talerutil.ParseAmount(costStr)
+ t.BaseUrl = t.Cfg.Section("gns-registrar").Key("base_url").MustString("http://localhost:11000")
t.SuffixHint = t.Cfg.Section("gns-registrar").Key("suffix_hint").MustString("example.alt")
- t.RootZoneName = t.Cfg.Section("gns-registrar").Key("root_zone_name").MustString("master")
- t.GnunetUrl = t.Cfg.Section("gns-registrar").Key("gnunet_baseurl_private").MustString("http://localhost:7776")
+ t.RootZoneName = t.Cfg.Section("gns-registrar").Key("root_zone_name").MustString("test")
+ t.GnunetUrl = t.Cfg.Section("gns-registrar").Key("base_url_gnunet").MustString("http://localhost:7776")
resp, err := http.Get(t.GnunetUrl + "/identity/name/" + t.RootZoneName)
if err != nil {
fmt.Printf("Failed to get zone key")
@@ -338,8 +597,8 @@ func (t *Registrar) Initialize(cfgfile string) {
fmt.Printf("Failed to get zone contents" + err.Error())
os.Exit(1)
}
- merchURL := t.Cfg.Section("gns-registrar").Key("merchant_baseurl_private").MustString("http://merchant.gnsregistrar/instances/myInstance")
- merchToken := t.Cfg.Section("gns-registrar").Key("merchant_token").MustString("secretAccessToken")
+ merchURL := t.Cfg.Section("gns-registrar").Key("base_url_merchant").MustString("https://backend.demo.taler.net")
+ merchToken := t.Cfg.Section("gns-registrar").Key("merchant_token").MustString("sandbox")
t.Merchant = merchant.NewMerchant(merchURL, merchToken)
t.setupHandlers()
}
diff --git a/web/templates/buy.html b/web/templates/buy.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <!-- Required meta tags -->
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+ <link href="/css/bootstrap.min.css" rel="stylesheet">
+ <title>Buy</title>
+ </head>
+ <body>
+ <div class ="container text-center">
+ <h1 class="mb-3">To register your name <i class="text-primary">{{.label}}</i>.<i class="text-secondary">{{.suffixHint}}</i> please pay here:</h1>
+
+ <a href="{{.payto}}" class="btn btn-success mb-3">Pay with TALER</a><br/>
+ Alternatively, you can pay using your mobile wallet by scanning the QR code below:<br/>
+ <a href="{{.fulfillmentUrl}}">
+ <img class="qr" src="{{.qrCode}}"/>
+ </a><br/>
+ After you pay with a mobile wallet, please click on the QR code to finalize your registration.
+ </div>
+ </body>
+</html>
diff --git a/web/templates/landing.html b/web/templates/landing.html
@@ -9,13 +9,19 @@
</head>
<body>
<div class="container pt-5">
- <h1 class="text-center mb-5">.{{.suffixHint}} is {{.zoneKey}}</h1>
- <form action="/search" method="get">
- <label for="label" class="form-label">Check availability:</label>
- <div class="input-group mb-2 w-50">
- <input id="label" name="label" class="form-control form-control-lg text-end" maxlength="63" type="text" aria-describedby="reg-suffix" required autofocus>
- <span class="input-group-text" id="reg-suffix">.{{.suffixHint}}</span>
- <input class="btn btn-primary" type="submit" value="Check!">
+ <h1 class="text-center mb-5">GNUnet GNS Registrar<br/>
+ <span class="text-center fs-5 text-secondary">Zone: {{.zoneKey}}</span>
+ </h1>
+ <form action="/search" method="get" class="row">
+ <div class="row">
+ <div class="col-lg-6 offset-lg-3">
+ <label for="label" class="form-label">Check name availability:</label>
+ <div class="input-group mb-2">
+ <input id="label" name="label" class="form-control form-control-lg text-end" maxlength="63" type="text" aria-describedby="reg-suffix" required autofocus>
+ <span class="input-group-text" id="reg-suffix">.{{.suffixHint}}</span>
+ <input class="btn btn-primary" type="submit" value="Check!">
+ </div>
+ </div>
</div>
</form>
</div>
diff --git a/web/templates/name.html b/web/templates/name.html
@@ -30,35 +30,35 @@
{{if .available}}
<h1 class="mb-5"><i class="text-primary">{{.label}}</i> is still <span class="text-success">available</span> for registration.</h1>
<form action="/name/{{.label}}/buy" method="get" class="align-items-center">
- <div class="input-group mb-2 w-75">
- <span class="input-group-text" id="reg-prefix"><i class="text-primary">{{.label}}</i>.<i class="text-secondary">{{.suffixHint}}</i>: </span>
- <input type="hidden" name="label" value="{{.label}}">
- <input name="zkey" class="form-control form-control-lg" maxlength="63" type="text" placeholder="Enter your zone key here!" required autofocus>
- {{if eq .cost 0}}
- <input class="btn btn-primary" type="submit" value="Register for free">
- {{else}}
- <input class="btn btn-primary" type="submit" value="Register for {{.cost}} {{.currency}}">
- {{end}}
+ <div class="row">
+ <div class="col-lg-12">
+ <div class="input-group mb-2">
+ <span class="input-group-text" id="reg-prefix"><i class="text-primary">{{.label}}</i>.<i class="text-secondary">{{.suffixHint}}</i>: </span>
+ <input name="zkey" class="form-control form-control-lg" maxlength="63" type="text" placeholder="Enter your zone key here!" required autofocus>
+ <input class="btn btn-primary" type="submit" value="Register for {{.cost}}">
+ </div>
+ </div>
</div>
</form>
{{else}}
- <h1 class="mb-5"><i class="text-primary">{{.label}}</i> is already <span class="text-danger">taken</span>!</h1>
+ {{if eq .registeredUntil ""}}
+ <h1 class="mb-5"><i class="text-primary">{{.label}}</i> is already <span class="text-secondary">reserved</span>!</h1>
+ {{else}}
+ <h1 class="mb-5"><i class="text-primary">{{.label}}</i> is already <span class="text-danger">registered</span>!</h1>
+ {{end}}
<form action="/name/{{.label}}/renew" method="get" class="align-items-center">
- <div class="input-group mb-2 w-75">
- <span class="input-group-text" id="reg-prefix"><i class="text-primary">{{.label}}</i>.<i class="text-secondary">{{.suffixHint}}</i>: </span>
- <input name="label" disabled="{{.modificationAllowed}}" class="form-control form-control-lg" maxlength="63" type="text" placeholder="Enter your zone key here!" value="{{.currentValue}}" required autofocus>
- {{if .modificationAllowed}}
- <input class="btn btn-primary" disabled="{{.modificationAllowed}}" type="submit" value="Update">
- {{end}}
+ <div class="row">
+ <div class="col-lg-12">
+ <div class="input-group mb-2 w-75">
+ <span class="input-group-text" id="reg-prefix"><i class="text-primary">{{.label}}</i>.<i class="text-secondary">{{.suffixHint}}</i>: </span>
+ <input name="label" disabled="{{.modificationAllowed}}" class="form-control form-control-lg" maxlength="63" type="text" placeholder="Enter your zone key here!" value="{{.currentValue}}" required autofocus>
+ </div>
+ </div>
</div>
</form>
- <form action="/renew" method="get" class="align-items-center">
- <span>Registration valid until: <i>placeholder</i></span><br/>
- {{if .modificationAllowed}}
- <span>Registration extension cost: <i>23 EUR</i></span><br/>
- <input class="btn btn-primary" type="submit" value="Extend registration">
- {{end}}
- </form>
+ {{if ne .registeredUntil ""}}
+ <span>Registration valid until: <i>{{.registeredUntil}}</i></span><br/>
+ {{end}}
{{end}}
<a class="btn btn-primary mt-5" href="/">Back</a>
</div>
diff --git a/web/templates/names.html b/web/templates/names.html
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
- <head>
- <!-- Required meta tags -->
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
- <link href="/css/style.css" rel="stylesheet">
- <title>All Names Overview</title>
- </head>
- <body>
- <div class="names">
- <!-- FIXME: Probably make this a table -->
- <ul>
- {{range $val := .}}
- <li>{{$val.RecordName}}: {{(index $val.Records 0).Value}}</li>
- {{end}}
- </ul>
- </div>
- </body>
-</html>