Test login + callback
This commit is contained in:
parent
c66e96bf3f
commit
fc6fb9914e
2
Makefile
2
Makefile
@ -10,3 +10,5 @@ delphi.market: $(SOURCE)
|
|||||||
run:
|
run:
|
||||||
go run .
|
go run .
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test ./server/... ./db/... ./lnd/...
|
||||||
|
42
db/db.go
42
db/db.go
@ -2,6 +2,8 @@ package db
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
)
|
)
|
||||||
@ -10,6 +12,10 @@ type DB struct {
|
|||||||
*sql.DB
|
*sql.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
initSqlPath = "./db/init.sql"
|
||||||
|
)
|
||||||
|
|
||||||
func New(dbUrl string) (*DB, error) {
|
func New(dbUrl string) (*DB, error) {
|
||||||
var (
|
var (
|
||||||
db_ *sql.DB
|
db_ *sql.DB
|
||||||
@ -27,3 +33,39 @@ func New(dbUrl string) (*DB, error) {
|
|||||||
db = &DB{DB: db_}
|
db = &DB{DB: db_}
|
||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) Reset(dbName string) error {
|
||||||
|
var (
|
||||||
|
f []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if err = db.Clear(dbName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if f, err = ioutil.ReadFile(initSqlPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err = db.Exec(string(f)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) Clear(dbName string) error {
|
||||||
|
var (
|
||||||
|
tables = []string{"lnauth", "users", "sessions", "markets", "shares", "invoices", "order_side", "orders", "matches"}
|
||||||
|
sql []string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
for _, t := range tables {
|
||||||
|
sql = append(sql, fmt.Sprintf("DROP TABLE IF EXISTS %s CASCADE", t))
|
||||||
|
}
|
||||||
|
sql = append(sql, "DROP EXTENSION IF EXISTS \"uuid-ossp\"")
|
||||||
|
sql = append(sql, "DROP TYPE IF EXISTS order_side")
|
||||||
|
for _, s := range sql {
|
||||||
|
if _, err = db.Exec(s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -12,7 +12,7 @@ type (
|
|||||||
LNAuth struct {
|
LNAuth struct {
|
||||||
K1 string
|
K1 string
|
||||||
LNURL string
|
LNURL string
|
||||||
CreatdAt time.Time
|
CreatedAt time.Time
|
||||||
SessionId string
|
SessionId string
|
||||||
}
|
}
|
||||||
User struct {
|
User struct {
|
||||||
|
@ -7,6 +7,10 @@ func (db *DB) CreateUser(u *User) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) FetchUser(pubkey string, u *User) error {
|
||||||
|
return db.QueryRow("SELECT pubkey, last_seen FROM users WHERE pubkey = $1", pubkey).Scan(&u.Pubkey, &u.LastSeen)
|
||||||
|
}
|
||||||
|
|
||||||
func (db *DB) UpdateUser(u *User) error {
|
func (db *DB) UpdateUser(u *User) error {
|
||||||
_, err := db.Exec("UPDATE users SET last_seen = $1 WHERE pubkey = $2", u.LastSeen, u.Pubkey)
|
_, err := db.Exec("UPDATE users SET last_seen = $1 WHERE pubkey = $2", u.LastSeen, u.Pubkey)
|
||||||
return err
|
return err
|
||||||
|
3
go.mod
3
go.mod
@ -14,6 +14,7 @@ require (
|
|||||||
github.com/lightningnetwork/lnd v0.10.0-beta.rc6.0.20200615174244-103c59a4889f
|
github.com/lightningnetwork/lnd v0.10.0-beta.rc6.0.20200615174244-103c59a4889f
|
||||||
github.com/namsral/flag v1.7.4-pre
|
github.com/namsral/flag v1.7.4-pre
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||||
|
github.com/stretchr/testify v1.8.1
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
|
||||||
gopkg.in/guregu/null.v4 v4.0.0
|
gopkg.in/guregu/null.v4 v4.0.0
|
||||||
)
|
)
|
||||||
@ -72,6 +73,7 @@ require (
|
|||||||
github.com/miekg/dns v0.0.0-20171125082028-79bfde677fa8 // indirect
|
github.com/miekg/dns v0.0.0-20171125082028-79bfde677fa8 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_golang v0.9.3 // indirect
|
github.com/prometheus/client_golang v0.9.3 // indirect
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect
|
||||||
github.com/prometheus/common v0.4.0 // indirect
|
github.com/prometheus/common v0.4.0 // indirect
|
||||||
@ -98,5 +100,6 @@ require (
|
|||||||
gopkg.in/macaroon-bakery.v2 v2.0.1 // indirect
|
gopkg.in/macaroon-bakery.v2 v2.0.1 // indirect
|
||||||
gopkg.in/macaroon.v2 v2.1.0 // indirect
|
gopkg.in/macaroon.v2 v2.1.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.2.3 // indirect
|
gopkg.in/yaml.v2 v2.2.3 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
sigs.k8s.io/yaml v1.1.0 // indirect
|
sigs.k8s.io/yaml v1.1.0 // indirect
|
||||||
)
|
)
|
||||||
|
6
go.sum
6
go.sum
@ -253,12 +253,17 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO
|
|||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
@ -396,6 +401,7 @@ gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
182
server/router/handler/login_test.go
Normal file
182
server/router/handler/login_test.go
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
package handler_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
db_ "git.ekzyis.com/ekzyis/delphi.market/db"
|
||||||
|
"git.ekzyis.com/ekzyis/delphi.market/server/auth"
|
||||||
|
"git.ekzyis.com/ekzyis/delphi.market/server/router"
|
||||||
|
"git.ekzyis.com/ekzyis/delphi.market/server/router/context"
|
||||||
|
"git.ekzyis.com/ekzyis/delphi.market/server/router/handler"
|
||||||
|
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
dbName string = "delphi_test"
|
||||||
|
dbUrl string = fmt.Sprintf("postgres://delphi:delphi@localhost:5432/%s?sslmode=disable", dbName)
|
||||||
|
db *db_.DB
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// for ParseTemplates to work, cwd needs to be project root
|
||||||
|
_, filename, _, _ := runtime.Caller(0)
|
||||||
|
dir := path.Join(path.Dir(filename), "../../..")
|
||||||
|
err := os.Chdir(dir)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if db, err = db_.New(dbUrl); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
if err := db.Reset(dbName); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
retCode := m.Run()
|
||||||
|
if err := db.Clear(dbName); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
os.Exit(retCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mocks(method string, target string, body io.Reader) (*echo.Echo, context.ServerContext, *http.Request, *httptest.ResponseRecorder) {
|
||||||
|
var (
|
||||||
|
e *echo.Echo
|
||||||
|
sc context.ServerContext
|
||||||
|
req *http.Request
|
||||||
|
rec *httptest.ResponseRecorder
|
||||||
|
)
|
||||||
|
e = echo.New()
|
||||||
|
e.Renderer = router.ParseTemplates("pages/**.html")
|
||||||
|
sc = context.ServerContext{
|
||||||
|
Db: db,
|
||||||
|
}
|
||||||
|
req = httptest.NewRequest(method, target, body)
|
||||||
|
rec = httptest.NewRecorder()
|
||||||
|
return e, sc, req, rec
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogin(t *testing.T) {
|
||||||
|
var (
|
||||||
|
assert = assert.New(t)
|
||||||
|
e *echo.Echo
|
||||||
|
c echo.Context
|
||||||
|
sc context.ServerContext
|
||||||
|
req *http.Request
|
||||||
|
rec *httptest.ResponseRecorder
|
||||||
|
cookies []*http.Cookie
|
||||||
|
sessionId string
|
||||||
|
dbSessionId string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
e, sc, req, rec = mocks("GET", "/login", nil)
|
||||||
|
c = e.NewContext(req, rec)
|
||||||
|
|
||||||
|
err = handler.HandleLogin(sc)(c)
|
||||||
|
assert.NoErrorf(err, "handler returned error")
|
||||||
|
|
||||||
|
// Set-Cookie header present
|
||||||
|
cookies = rec.Result().Cookies()
|
||||||
|
assert.Equalf(len(cookies), 1, "wrong number of Set-Cookie headers")
|
||||||
|
assert.Equalf(cookies[0].Name, "session", "wrong cookie name")
|
||||||
|
|
||||||
|
// new challenge inserted
|
||||||
|
sessionId = cookies[0].Value
|
||||||
|
err = db.QueryRow("SELECT session_id FROM lnauth WHERE session_id = $1", sessionId).Scan(&dbSessionId)
|
||||||
|
if !assert.NoError(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// inserted challenge matches cookie value
|
||||||
|
assert.Equalf(sessionId, dbSessionId, "wrong session id")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoginCallback(t *testing.T) {
|
||||||
|
var (
|
||||||
|
assert = assert.New(t)
|
||||||
|
e *echo.Echo
|
||||||
|
c echo.Context
|
||||||
|
sc context.ServerContext
|
||||||
|
req *http.Request
|
||||||
|
rec *httptest.ResponseRecorder
|
||||||
|
lnAuth *auth.LNAuth
|
||||||
|
dbLnAuth *db_.LNAuth
|
||||||
|
u *db_.User
|
||||||
|
s *db_.Session
|
||||||
|
key string
|
||||||
|
sig string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
lnAuth, err = auth.NewLNAuth()
|
||||||
|
if !assert.NoErrorf(err, "error creating challenge") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dbLnAuth = &db_.LNAuth{K1: lnAuth.K1, LNURL: lnAuth.LNURL}
|
||||||
|
err = db.CreateLNAuth(dbLnAuth)
|
||||||
|
if !assert.NoErrorf(err, "error inserting challenge") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
key, sig, err = sign(lnAuth.K1)
|
||||||
|
if !assert.NoErrorf(err, "error signing k1") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e, sc, req, rec = mocks("GET", fmt.Sprintf("/api/login?k1=%s&key=%s&sig=%s", lnAuth.K1, key, sig), nil)
|
||||||
|
c = e.NewContext(req, rec)
|
||||||
|
|
||||||
|
err = handler.HandleLoginCallback(sc)(c)
|
||||||
|
assert.NoErrorf(err, "handler returned error")
|
||||||
|
|
||||||
|
// user created
|
||||||
|
u = new(db_.User)
|
||||||
|
err = db.FetchUser(key, u)
|
||||||
|
if assert.NoErrorf(err, "error fetching user") {
|
||||||
|
assert.Equalf(u.Pubkey, key, "pubkeys do not match")
|
||||||
|
}
|
||||||
|
|
||||||
|
// session created
|
||||||
|
s = &db_.Session{SessionId: dbLnAuth.SessionId}
|
||||||
|
err = db.FetchSession(s)
|
||||||
|
if assert.NoErrorf(err, "error fetching session") {
|
||||||
|
assert.Equalf(s.Pubkey, u.Pubkey, "session pubkey does not match user pubkey")
|
||||||
|
}
|
||||||
|
|
||||||
|
// challenge deleted
|
||||||
|
err = db.FetchSessionId(u.Pubkey, &dbLnAuth.SessionId)
|
||||||
|
assert.ErrorIs(err, sql.ErrNoRows, "challenge not deleted")
|
||||||
|
}
|
||||||
|
|
||||||
|
func sign(k1_ string) (string, string, error) {
|
||||||
|
var (
|
||||||
|
sk *secp256k1.PrivateKey
|
||||||
|
k1 []byte
|
||||||
|
sig []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if k1, err = hex.DecodeString(k1_); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
if sk, err = secp256k1.GeneratePrivateKey(); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
if sig, err = ecdsa.SignASN1(rand.Reader, sk.ToECDSA(), k1); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
return hex.EncodeToString(sk.PubKey().SerializeCompressed()), hex.EncodeToString(sig), nil
|
||||||
|
}
|
@ -20,12 +20,8 @@ func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Con
|
|||||||
return t.templates.ExecuteTemplate(w, name, data)
|
return t.templates.ExecuteTemplate(w, name, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
func ParseTemplates(pattern string) *Template {
|
||||||
T *Template
|
return &Template{
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
T = &Template{
|
|
||||||
templates: template.Must(template.New("").Funcs(template.FuncMap{
|
templates: template.Must(template.New("").Funcs(template.FuncMap{
|
||||||
"add": add[int64],
|
"add": add[int64],
|
||||||
"sub": sub[int64],
|
"sub": sub[int64],
|
||||||
|
@ -20,7 +20,7 @@ func New(ctx ServerContext) *Server {
|
|||||||
)
|
)
|
||||||
e = echo.New()
|
e = echo.New()
|
||||||
e.Static("/", "public")
|
e.Static("/", "public")
|
||||||
e.Renderer = router.T
|
e.Renderer = router.ParseTemplates("pages/**.html")
|
||||||
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
|
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
|
||||||
Format: "${time_custom} ${method} ${uri} ${status}\n",
|
Format: "${time_custom} ${method} ${uri} ${status}\n",
|
||||||
CustomTimeFormat: "2006-01-02 15:04:05.00000-0700",
|
CustomTimeFormat: "2006-01-02 15:04:05.00000-0700",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user