Refactor router code
This commit is contained in:
parent
04ce96069b
commit
c2a8968120
138
src/auth.go
138
src/auth.go
|
@ -3,11 +3,17 @@ package main
|
|||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcutil/bech32"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/skip2/go-qrcode"
|
||||
)
|
||||
|
||||
type LnAuth struct {
|
||||
|
@ -65,3 +71,135 @@ func lnAuthVerify(r *LnAuthResponse) (bool, error) {
|
|||
ecdsaKey := ecdsa.PublicKey{Curve: btcec.S256(), X: key.X(), Y: key.Y()}
|
||||
return ecdsa.VerifyASN1(&ecdsaKey, k1Bytes, sigBytes), nil
|
||||
}
|
||||
|
||||
func login(c echo.Context) error {
|
||||
lnauth, err := lnAuth()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var sessionId string
|
||||
err = db.QueryRow("INSERT INTO lnauth(k1, lnurl) VALUES($1, $2) RETURNING session_id", lnauth.k1, lnauth.lnurl).Scan(&sessionId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
expires := time.Now().Add(60 * 60 * 24 * 365 * time.Second)
|
||||
c.SetCookie(&http.Cookie{Name: "session", HttpOnly: true, Path: "/", Value: sessionId, Secure: true, Expires: expires})
|
||||
png, err := qrcode.Encode(lnauth.lnurl, qrcode.Medium, 256)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
qr := base64.StdEncoding.EncodeToString([]byte(png))
|
||||
return c.Render(http.StatusOK, "login.html", map[string]any{"session": c.Get("session"), "PUBLIC_URL": PUBLIC_URL, "lnurl": lnauth.lnurl, "qr": qr})
|
||||
}
|
||||
|
||||
func verifyLogin(c echo.Context) error {
|
||||
var query LnAuthResponse
|
||||
if err := c.Bind(&query); err != nil {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusBadRequest, map[string]string{"status": "ERROR", "reason": "bad request"})
|
||||
}
|
||||
var sessionId string
|
||||
err := db.QueryRow("SELECT session_id FROM lnauth WHERE k1 = $1", query.K1).Scan(&sessionId)
|
||||
if err == sql.ErrNoRows {
|
||||
return c.JSON(http.StatusBadRequest, map[string]string{"status": "ERROR", "reason": "unknown k1"})
|
||||
} else if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
|
||||
}
|
||||
ok, err := lnAuthVerify(&query)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
|
||||
}
|
||||
if !ok {
|
||||
c.Logger().Error("bad signature")
|
||||
return c.JSON(http.StatusUnauthorized, map[string]string{"status": "ERROR", "reason": "bad signature"})
|
||||
}
|
||||
_, err = db.Exec("INSERT INTO users(pubkey) VALUES ($1) ON CONFLICT(pubkey) DO UPDATE SET last_seen = CURRENT_TIMESTAMP", query.Key)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
|
||||
}
|
||||
_, err = db.Exec("INSERT INTO sessions(pubkey, session_id) VALUES($1, $2)", query.Key, sessionId)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
|
||||
}
|
||||
_, err = db.Exec("DELETE FROM lnauth WHERE k1 = $1", query.K1)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
|
||||
}
|
||||
return c.JSON(http.StatusOK, map[string]string{"status": "OK"})
|
||||
}
|
||||
|
||||
func checkSession(c echo.Context) error {
|
||||
cookie, err := c.Cookie("session")
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
|
||||
}
|
||||
sessionId := cookie.Value
|
||||
var pubkey string
|
||||
err = db.QueryRow("SELECT pubkey FROM sessions WHERE session_id = $1", sessionId).Scan(&pubkey)
|
||||
if err == sql.ErrNoRows {
|
||||
return c.JSON(http.StatusNotFound, map[string]string{"status": "Not Found", "message": "session not found"})
|
||||
} else if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
|
||||
}
|
||||
return c.JSON(http.StatusOK, map[string]string{"pubkey": pubkey})
|
||||
}
|
||||
|
||||
func sessionHandler(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
cookie, err := c.Cookie("session")
|
||||
if err != nil {
|
||||
// cookie not found
|
||||
return next(c)
|
||||
}
|
||||
sessionId := cookie.Value
|
||||
var pubkey string
|
||||
err = db.QueryRow("SELECT pubkey FROM sessions WHERE session_id = $1", sessionId).Scan(&pubkey)
|
||||
if err == nil {
|
||||
// session found
|
||||
_, err = db.Exec("UPDATE users SET last_seen = CURRENT_TIMESTAMP WHERE pubkey = $1", pubkey)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
|
||||
}
|
||||
c.Set("session", Session{pubkey})
|
||||
} else if err != sql.ErrNoRows {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
|
||||
}
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
|
||||
func sessionGuard(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
session := c.Get("session")
|
||||
if session == nil {
|
||||
return c.Redirect(http.StatusTemporaryRedirect, "/login")
|
||||
}
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
|
||||
func logout(c echo.Context) error {
|
||||
cookie, err := c.Cookie("session")
|
||||
if err != nil {
|
||||
// cookie not found
|
||||
return c.Redirect(http.StatusSeeOther, "/")
|
||||
}
|
||||
sessionId := cookie.Value
|
||||
_, err = db.Exec("DELETE FROM sessions where session_id = $1", sessionId)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return err
|
||||
}
|
||||
// tell browser that cookie is expired and thus can be deleted
|
||||
c.SetCookie(&http.Cookie{Name: "session", HttpOnly: true, Path: "/", Value: sessionId, Secure: true, Expires: time.Now()})
|
||||
return c.Redirect(http.StatusSeeOther, "/")
|
||||
}
|
||||
|
|
79
src/db.go
79
src/db.go
|
@ -11,9 +11,13 @@ import (
|
|||
|
||||
var (
|
||||
DbUrl string
|
||||
db *sql.DB
|
||||
db *DB
|
||||
)
|
||||
|
||||
type DB struct {
|
||||
*sql.DB
|
||||
}
|
||||
|
||||
func init() {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
|
@ -29,12 +33,12 @@ func init() {
|
|||
}
|
||||
}
|
||||
|
||||
func initDb() *sql.DB {
|
||||
func initDb() *DB {
|
||||
db, err := sql.Open("postgres", DbUrl)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return db
|
||||
return &DB{DB: db}
|
||||
}
|
||||
|
||||
func validateFlags() {
|
||||
|
@ -42,3 +46,72 @@ func validateFlags() {
|
|||
log.Fatal("DATABASE_URL not set")
|
||||
}
|
||||
}
|
||||
|
||||
func (db *DB) FetchMarket(marketId int, market *Market) error {
|
||||
if err := db.QueryRow("SELECT id, description FROM markets WHERE id = $1", marketId).Scan(&market.Id, &market.Description); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) FetchShares(marketId int, shares *[]Share) error {
|
||||
rows, err := db.Query("SELECT id, market_id, description FROM shares WHERE market_id = $1 ORDER BY description DESC", marketId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var share Share
|
||||
rows.Scan(&share.Id, &share.MarketId, &share.Description)
|
||||
*shares = append(*shares, share)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) FetchOrderBook(shareId string, orderBook *[]OrderBookEntry) error {
|
||||
rows, err := db.Query(""+
|
||||
"SELECT share_id, side, price, SUM(quantity)"+
|
||||
"FROM orders WHERE share_id = $1"+
|
||||
"GROUP BY (share_id, side, price)"+
|
||||
"ORDER BY share_id DESC, side DESC, price DESC", shareId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
buyOrders := []Order{}
|
||||
sellOrders := []Order{}
|
||||
for rows.Next() {
|
||||
var order Order
|
||||
rows.Scan(&order.ShareId, &order.Side, &order.Price, &order.Quantity)
|
||||
if order.Side == "BUY" {
|
||||
buyOrders = append(buyOrders, Order{Price: order.Price, Quantity: order.Quantity})
|
||||
} else {
|
||||
sellOrders = append(sellOrders, Order{Price: order.Price, Quantity: order.Quantity})
|
||||
}
|
||||
}
|
||||
buySum := 0
|
||||
sellSum := 0
|
||||
for i := 0; i < Max(len(buyOrders), len(sellOrders)); i++ {
|
||||
buyPrice, buyQuantity, sellQuantity, sellPrice := 0, 0, 0, 0
|
||||
if i < len(buyOrders) {
|
||||
buyPrice = buyOrders[i].Price
|
||||
buyQuantity = buySum + buyOrders[i].Quantity
|
||||
}
|
||||
if i < len(sellOrders) {
|
||||
sellPrice = sellOrders[i].Price
|
||||
sellQuantity = sellSum + sellOrders[i].Quantity
|
||||
}
|
||||
buySum += buyQuantity
|
||||
sellSum += sellQuantity
|
||||
*orderBook = append(
|
||||
*orderBook,
|
||||
OrderBookEntry{
|
||||
BuyQuantity: buyQuantity,
|
||||
BuyPrice: buyPrice,
|
||||
SellPrice: sellPrice,
|
||||
SellQuantity: sellQuantity,
|
||||
},
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
20
src/funcs.go
20
src/funcs.go
|
@ -1,20 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
)
|
||||
|
||||
func add(arg1 int, arg2 int) int {
|
||||
return arg1 + arg2
|
||||
}
|
||||
|
||||
func sub(arg1 int, arg2 int) int {
|
||||
return arg1 - arg2
|
||||
}
|
||||
|
||||
var (
|
||||
FuncMap template.FuncMap = template.FuncMap{
|
||||
"add": add,
|
||||
"sub": sub,
|
||||
}
|
||||
)
|
101
src/market.go
101
src/market.go
|
@ -1,9 +1,48 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type Market struct {
|
||||
Id int
|
||||
Description string
|
||||
Funding int
|
||||
Active bool
|
||||
}
|
||||
|
||||
type Share struct {
|
||||
Id string
|
||||
MarketId int
|
||||
Description string
|
||||
}
|
||||
|
||||
type Order struct {
|
||||
ShareId string
|
||||
Side string
|
||||
Price int
|
||||
Quantity int
|
||||
}
|
||||
|
||||
type OrderBookEntry struct {
|
||||
BuyQuantity int
|
||||
BuyPrice int
|
||||
SellPrice int
|
||||
SellQuantity int
|
||||
}
|
||||
|
||||
type MarketDataRequest struct {
|
||||
ShareId string `json:"share_id"`
|
||||
OrderSide string `json:"side"`
|
||||
Quantity int `json:"quantity"`
|
||||
}
|
||||
|
||||
func costFunction(b float64, q1 float64, q2 float64) float64 {
|
||||
// reference: http://blog.oddhead.com/2006/10/30/implementing-hansons-market-maker/
|
||||
return b * math.Log(math.Pow(math.E, q1/b)+math.Pow(math.E, q2/b))
|
||||
|
@ -18,3 +57,65 @@ func BinaryLMSR(invariant int, funding int, q1 int, q2 int, dq1 int) float64 {
|
|||
fdq1 := float64(dq1)
|
||||
return costFunction(b, fq1+fdq1, fq2) - costFunction(b, fq1, fq2)
|
||||
}
|
||||
|
||||
func trades(c echo.Context) error {
|
||||
marketId, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Bad Request")
|
||||
}
|
||||
var market Market
|
||||
if err = db.FetchMarket(int(marketId), &market); err == sql.ErrNoRows {
|
||||
return echo.NewHTTPError(http.StatusNotFound, "Not Found")
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
var shares []Share
|
||||
if err = db.FetchShares(market.Id, &shares); err != nil {
|
||||
return err
|
||||
}
|
||||
data := map[string]any{
|
||||
"session": c.Get("session"),
|
||||
"ENV": ENV,
|
||||
"Id": market.Id,
|
||||
"Description": market.Description,
|
||||
"Shares": shares,
|
||||
}
|
||||
return c.Render(http.StatusOK, "bmarket_trade.html", data)
|
||||
}
|
||||
|
||||
func orders(c echo.Context) error {
|
||||
marketId, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Bad Request")
|
||||
}
|
||||
shareId := c.Param("sid")
|
||||
|
||||
var market Market
|
||||
if err = db.FetchMarket(int(marketId), &market); err == sql.ErrNoRows {
|
||||
return echo.NewHTTPError(http.StatusNotFound, "Not Found")
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var shares []Share
|
||||
if err = db.FetchShares(market.Id, &shares); err != nil {
|
||||
return err
|
||||
}
|
||||
if shareId == "" {
|
||||
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("/market/%d/%s", market.Id, shares[0].Id))
|
||||
}
|
||||
var orderBook []OrderBookEntry
|
||||
if err = db.FetchOrderBook(shareId, &orderBook); err != nil {
|
||||
return err
|
||||
}
|
||||
data := map[string]any{
|
||||
"session": c.Get("session"),
|
||||
"ENV": ENV,
|
||||
"Id": market.Id,
|
||||
"Description": market.Description,
|
||||
"ShareId": shareId,
|
||||
"Shares": shares,
|
||||
"OrderBook": orderBook,
|
||||
}
|
||||
return c.Render(http.StatusOK, "bmarket_order.html", data)
|
||||
}
|
||||
|
|
285
src/router.go
285
src/router.go
|
@ -1,55 +1,33 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/skip2/go-qrcode"
|
||||
)
|
||||
|
||||
type Template struct {
|
||||
templates *template.Template
|
||||
}
|
||||
|
||||
type Market struct {
|
||||
Id int
|
||||
Description string
|
||||
Funding int
|
||||
Active bool
|
||||
func add(arg1 int, arg2 int) int {
|
||||
return arg1 + arg2
|
||||
}
|
||||
|
||||
type Share struct {
|
||||
Id string
|
||||
MarketId int
|
||||
Description string
|
||||
func sub(arg1 int, arg2 int) int {
|
||||
return arg1 - arg2
|
||||
}
|
||||
|
||||
type Order struct {
|
||||
ShareId string
|
||||
Side string
|
||||
Price int
|
||||
Quantity int
|
||||
}
|
||||
|
||||
type OrderBookEntry struct {
|
||||
BuyQuantity int
|
||||
BuyPrice int
|
||||
SellPrice int
|
||||
SellQuantity int
|
||||
}
|
||||
|
||||
type MarketDataRequest struct {
|
||||
ShareId string `json:"share_id"`
|
||||
OrderSide string `json:"side"`
|
||||
Quantity int `json:"quantity"`
|
||||
}
|
||||
var (
|
||||
FuncMap template.FuncMap = template.FuncMap{
|
||||
"add": add,
|
||||
"sub": sub,
|
||||
}
|
||||
)
|
||||
|
||||
func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
|
||||
return t.templates.ExecuteTemplate(w, name, data)
|
||||
|
@ -76,249 +54,6 @@ func index(c echo.Context) error {
|
|||
return c.Render(http.StatusOK, "index.html", data)
|
||||
}
|
||||
|
||||
func login(c echo.Context) error {
|
||||
lnauth, err := lnAuth()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var sessionId string
|
||||
err = db.QueryRow("INSERT INTO lnauth(k1, lnurl) VALUES($1, $2) RETURNING session_id", lnauth.k1, lnauth.lnurl).Scan(&sessionId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
expires := time.Now().Add(60 * 60 * 24 * 365 * time.Second)
|
||||
c.SetCookie(&http.Cookie{Name: "session", HttpOnly: true, Path: "/", Value: sessionId, Secure: true, Expires: expires})
|
||||
png, err := qrcode.Encode(lnauth.lnurl, qrcode.Medium, 256)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
qr := base64.StdEncoding.EncodeToString([]byte(png))
|
||||
return c.Render(http.StatusOK, "login.html", map[string]any{"session": c.Get("session"), "PUBLIC_URL": PUBLIC_URL, "lnurl": lnauth.lnurl, "qr": qr})
|
||||
}
|
||||
|
||||
func verifyLogin(c echo.Context) error {
|
||||
var query LnAuthResponse
|
||||
if err := c.Bind(&query); err != nil {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusBadRequest, map[string]string{"status": "ERROR", "reason": "bad request"})
|
||||
}
|
||||
var sessionId string
|
||||
err := db.QueryRow("SELECT session_id FROM lnauth WHERE k1 = $1", query.K1).Scan(&sessionId)
|
||||
if err == sql.ErrNoRows {
|
||||
return c.JSON(http.StatusBadRequest, map[string]string{"status": "ERROR", "reason": "unknown k1"})
|
||||
} else if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
|
||||
}
|
||||
ok, err := lnAuthVerify(&query)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
|
||||
}
|
||||
if !ok {
|
||||
c.Logger().Error("bad signature")
|
||||
return c.JSON(http.StatusUnauthorized, map[string]string{"status": "ERROR", "reason": "bad signature"})
|
||||
}
|
||||
_, err = db.Exec("INSERT INTO users(pubkey) VALUES ($1) ON CONFLICT(pubkey) DO UPDATE SET last_seen = CURRENT_TIMESTAMP", query.Key)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
|
||||
}
|
||||
_, err = db.Exec("INSERT INTO sessions(pubkey, session_id) VALUES($1, $2)", query.Key, sessionId)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
|
||||
}
|
||||
_, err = db.Exec("DELETE FROM lnauth WHERE k1 = $1", query.K1)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
|
||||
}
|
||||
return c.JSON(http.StatusOK, map[string]string{"status": "OK"})
|
||||
}
|
||||
|
||||
func checkSession(c echo.Context) error {
|
||||
cookie, err := c.Cookie("session")
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
|
||||
}
|
||||
sessionId := cookie.Value
|
||||
var pubkey string
|
||||
err = db.QueryRow("SELECT pubkey FROM sessions WHERE session_id = $1", sessionId).Scan(&pubkey)
|
||||
if err == sql.ErrNoRows {
|
||||
return c.JSON(http.StatusNotFound, map[string]string{"status": "Not Found", "message": "session not found"})
|
||||
} else if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
|
||||
}
|
||||
return c.JSON(http.StatusOK, map[string]string{"pubkey": pubkey})
|
||||
}
|
||||
|
||||
func sessionHandler(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
cookie, err := c.Cookie("session")
|
||||
if err != nil {
|
||||
// cookie not found
|
||||
return next(c)
|
||||
}
|
||||
sessionId := cookie.Value
|
||||
var pubkey string
|
||||
err = db.QueryRow("SELECT pubkey FROM sessions WHERE session_id = $1", sessionId).Scan(&pubkey)
|
||||
if err == nil {
|
||||
// session found
|
||||
_, err = db.Exec("UPDATE users SET last_seen = CURRENT_TIMESTAMP WHERE pubkey = $1", pubkey)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
|
||||
}
|
||||
c.Set("session", Session{pubkey})
|
||||
} else if err != sql.ErrNoRows {
|
||||
c.Logger().Error(err)
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
|
||||
}
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
|
||||
func sessionGuard(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
session := c.Get("session")
|
||||
if session == nil {
|
||||
return c.Redirect(http.StatusTemporaryRedirect, "/login")
|
||||
}
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
|
||||
func logout(c echo.Context) error {
|
||||
cookie, err := c.Cookie("session")
|
||||
if err != nil {
|
||||
// cookie not found
|
||||
return c.Redirect(http.StatusSeeOther, "/")
|
||||
}
|
||||
sessionId := cookie.Value
|
||||
_, err = db.Exec("DELETE FROM sessions where session_id = $1", sessionId)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
return err
|
||||
}
|
||||
// tell browser that cookie is expired and thus can be deleted
|
||||
c.SetCookie(&http.Cookie{Name: "session", HttpOnly: true, Path: "/", Value: sessionId, Secure: true, Expires: time.Now()})
|
||||
return c.Redirect(http.StatusSeeOther, "/")
|
||||
}
|
||||
|
||||
func trades(c echo.Context) error {
|
||||
marketId := c.Param("id")
|
||||
var market Market
|
||||
err := db.QueryRow("SELECT id, description FROM markets WHERE id = $1 AND active = true", marketId).Scan(&market.Id, &market.Description)
|
||||
if err == sql.ErrNoRows {
|
||||
return echo.NewHTTPError(http.StatusNotFound, "Not Found")
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
rows, err := db.Query("SELECT id, market_id, description FROM shares WHERE market_id = $1 ORDER BY description DESC", marketId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
var shares []Share
|
||||
for rows.Next() {
|
||||
var share Share
|
||||
rows.Scan(&share.Id, &share.MarketId, &share.Description)
|
||||
shares = append(shares, share)
|
||||
}
|
||||
data := map[string]any{
|
||||
"session": c.Get("session"),
|
||||
"ENV": ENV,
|
||||
"Id": market.Id,
|
||||
"Description": market.Description,
|
||||
"Shares": shares,
|
||||
}
|
||||
return c.Render(http.StatusOK, "bmarket_trade.html", data)
|
||||
}
|
||||
|
||||
func orders(c echo.Context) error {
|
||||
marketId := c.Param("id")
|
||||
shareId := c.Param("sid")
|
||||
var market Market
|
||||
err := db.QueryRow("SELECT id, description FROM markets WHERE id = $1 AND active = true", marketId).Scan(&market.Id, &market.Description)
|
||||
if err == sql.ErrNoRows {
|
||||
return echo.NewHTTPError(http.StatusNotFound, "Not Found")
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
rows, err := db.Query("SELECT id, market_id, description FROM shares WHERE market_id = $1 ORDER BY description DESC", marketId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
var shares []Share
|
||||
for rows.Next() {
|
||||
var share Share
|
||||
rows.Scan(&share.Id, &share.MarketId, &share.Description)
|
||||
shares = append(shares, share)
|
||||
}
|
||||
if shareId == "" {
|
||||
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("/market/%s/%s", marketId, shares[0].Id))
|
||||
}
|
||||
rows, err = db.Query(""+
|
||||
"SELECT share_id, side, price, SUM(quantity)"+
|
||||
"FROM orders WHERE share_id = $1"+
|
||||
"GROUP BY (share_id, side, price)"+
|
||||
"ORDER BY share_id DESC, side DESC, price DESC", shareId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
buyOrders := []Order{}
|
||||
sellOrders := []Order{}
|
||||
for rows.Next() {
|
||||
var order Order
|
||||
rows.Scan(&order.ShareId, &order.Side, &order.Price, &order.Quantity)
|
||||
if order.Side == "BUY" {
|
||||
buyOrders = append(buyOrders, Order{Price: order.Price, Quantity: order.Quantity})
|
||||
} else {
|
||||
sellOrders = append(sellOrders, Order{Price: order.Price, Quantity: order.Quantity})
|
||||
}
|
||||
}
|
||||
orderBook := []OrderBookEntry{}
|
||||
buySum := 0
|
||||
sellSum := 0
|
||||
for i := 0; i < Max(len(buyOrders), len(sellOrders)); i++ {
|
||||
buyPrice, buyQuantity, sellQuantity, sellPrice := 0, 0, 0, 0
|
||||
if i < len(buyOrders) {
|
||||
buyPrice = buyOrders[i].Price
|
||||
buyQuantity = buySum + buyOrders[i].Quantity
|
||||
}
|
||||
if i < len(sellOrders) {
|
||||
sellPrice = sellOrders[i].Price
|
||||
sellQuantity = sellSum + sellOrders[i].Quantity
|
||||
}
|
||||
buySum += buyQuantity
|
||||
sellSum += sellQuantity
|
||||
orderBook = append(
|
||||
orderBook,
|
||||
OrderBookEntry{
|
||||
BuyQuantity: buyQuantity,
|
||||
BuyPrice: buyPrice,
|
||||
SellPrice: sellPrice,
|
||||
SellQuantity: sellQuantity,
|
||||
},
|
||||
)
|
||||
}
|
||||
data := map[string]any{
|
||||
"session": c.Get("session"),
|
||||
"ENV": ENV,
|
||||
"Id": market.Id,
|
||||
"Description": market.Description,
|
||||
"ShareId": shareId,
|
||||
"Shares": shares,
|
||||
"OrderBook": orderBook,
|
||||
}
|
||||
return c.Render(http.StatusOK, "bmarket_order.html", data)
|
||||
}
|
||||
|
||||
func serve500(c echo.Context) {
|
||||
f, err := os.Open("public/500.html")
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in New Issue