Add user page
This commit is contained in:
parent
e6aec21aff
commit
d987934ba4
|
@ -21,9 +21,7 @@
|
|||
<nav>
|
||||
<a href="/">home</a>
|
||||
{{ if .session }}
|
||||
<form action='/logout' method='post'>
|
||||
<button type='submit'>logout</button>
|
||||
</form>
|
||||
<a href='/user'>user</a>
|
||||
{{ else }} <a href="/login">login</a> {{ end }}
|
||||
</nav>
|
||||
</header>
|
||||
|
|
|
@ -22,9 +22,7 @@
|
|||
<nav>
|
||||
<a href="/">home</a>
|
||||
{{ if .session }}
|
||||
<form action='/logout' method='post'>
|
||||
<button type='submit'>logout</button>
|
||||
</form>
|
||||
<a href='/user'>user</a>
|
||||
{{ else }} <a href="/login">login</a> {{ end }}
|
||||
</nav>
|
||||
</header>
|
||||
|
|
|
@ -21,9 +21,7 @@
|
|||
<nav>
|
||||
<a href="/">home</a>
|
||||
{{ if .session }}
|
||||
<form action='/logout' method='post'>
|
||||
<button type='submit'>logout</button>
|
||||
</form>
|
||||
<a href='/user'>user</a>
|
||||
{{ else }} <a href="/login">login</a> {{ end }}
|
||||
</nav>
|
||||
</header>
|
||||
|
|
|
@ -22,9 +22,7 @@
|
|||
<nav>
|
||||
<a href="/">home</a>
|
||||
{{ if .session }}
|
||||
<form action='/logout' method='post'>
|
||||
<button type='submit'>logout</button>
|
||||
</form>
|
||||
<a href='/user'>user</a>
|
||||
{{ else }} <a href="/login">login</a> {{ end }}
|
||||
</nav>
|
||||
</header>
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>delphi.market</title>
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<link rel="stylesheet" href="/index.css" />
|
||||
<link rel="stylesheet" href="/market.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#091833" />
|
||||
{{ if eq .ENV "development" }}
|
||||
<script defer src="/hotreload.js"></script>
|
||||
{{ end }}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header class="flex flex-row text-center justify-center pt-1">
|
||||
<nav>
|
||||
<a href="/">home</a>
|
||||
{{ if .session }}
|
||||
<a href='/user'>user</a>
|
||||
{{ else }} <a href="/login">login</a> {{ end }}
|
||||
</nav>
|
||||
</header>
|
||||
<div class="container flex flex-column text-center">
|
||||
<code>
|
||||
<strong>
|
||||
<pre>
|
||||
_ _ ___ ___ _ __
|
||||
| | | / __|/ _ \ '__|
|
||||
| |_| \__ \ __/ |
|
||||
\__,_|___/\___|_| </pre>
|
||||
</strong>
|
||||
</code>
|
||||
<div class="font-mono mb-1 align-left word-wrap">
|
||||
You are: {{substr .session.Pubkey 0 8}}
|
||||
</div>
|
||||
<details open class="align-left mb-1">
|
||||
<summary><span class="font-mono mb-1"><strong>Open Orders</strong></span></summary>
|
||||
<table class="w-100p mb-1">
|
||||
<tr>
|
||||
<th class="align-left">Market</th>
|
||||
<th class="align-center">Side</th>
|
||||
<th class="align-center">Share</th>
|
||||
<th class="align-right">Quantity @ Price</th>
|
||||
</tr>
|
||||
{{ range .Orders }}
|
||||
{{ if .Invoice.ConfirmedAt.Valid }}
|
||||
<tr class='{{ if eq .Side "BUY" }}yes{{ else }}no{{ end }}'>
|
||||
<td class="align-left">
|
||||
<a href="/market/{{.Share.MarketId}}">{{.Share.MarketId}}</a>
|
||||
</td>
|
||||
<td class="align-center">{{.Side}}</td>
|
||||
<td class="align-center">{{.Share.Description}}</td>
|
||||
<td class="align-right">{{.Quantity}} @ {{.Price}}</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</table>
|
||||
</details>
|
||||
<details open class="align-left mb-1">
|
||||
<summary><span class="font-mono mb-1"><strong>Unpaid Orders</strong></span></summary>
|
||||
<table class="w-100p mb-1">
|
||||
<tr>
|
||||
<th class="align-left">Market</th>
|
||||
<th class="align-center">Side</th>
|
||||
<th class="align-center">Share</th>
|
||||
<th class="align-right">Quantity @ Price</th>
|
||||
<th class="align-right">Invoice</th>
|
||||
</tr>
|
||||
{{ range .Orders }}
|
||||
{{ if not .Invoice.ConfirmedAt.Valid }}
|
||||
<tr class='{{ if eq .Side "BUY" }}yes{{ else }}no{{ end }}'>
|
||||
<td class="align-left">
|
||||
<a href="/market/{{.Share.MarketId}}">{{.Share.MarketId}}</a>
|
||||
</td>
|
||||
<td class="align-center">{{.Side}}</td>
|
||||
<td class="align-center">{{.Share.Description}}</td>
|
||||
<td class="align-right">{{.Quantity}} @ {{.Price}}</td>
|
||||
<td class="align-right"><a href="/invoice/{{.InvoiceId}}">invoice</a></td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</table>
|
||||
</details>
|
||||
<form class="align-left" action="/logout" method="post">
|
||||
<button type="submit">logout</button>
|
||||
</form>
|
||||
</div>
|
||||
<footer class="flex justify-center">
|
||||
<div>
|
||||
<hr />
|
||||
<code><a href="https://github.com/ekzyis/delphi.market/commit/{{.COMMIT_LONG_SHA}}">{{.VERSION}}</a></code>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
</html>
|
42
src/db.go
42
src/db.go
|
@ -69,22 +69,40 @@ func (db *DB) FetchShares(marketId int, shares *[]Share) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) FetchOrders(marketId int, orders *[]Order) error {
|
||||
rows, err := db.Query(""+
|
||||
"SELECT o.id, share_id, o.pubkey, o.side, o.quantity, o.price, s.description, o.order_id "+
|
||||
"FROM orders o "+
|
||||
"JOIN invoices i ON o.invoice_id = i.id "+
|
||||
"JOIN shares s ON o.share_id = s.id "+
|
||||
"WHERE share_id = ANY(SELECT id FROM shares WHERE market_id = $1) "+
|
||||
"AND i.confirmed_at IS NOT NULL "+
|
||||
"ORDER BY price DESC", marketId)
|
||||
type FetchOrdersWhere struct {
|
||||
MarketId int
|
||||
Pubkey string
|
||||
Confirmed bool
|
||||
}
|
||||
|
||||
func (db *DB) FetchOrders(where *FetchOrdersWhere, orders *[]Order) error {
|
||||
query := "" +
|
||||
"SELECT o.id, share_id, o.pubkey, o.side, o.quantity, o.price, o.invoice_id, s.description, s.market_id, i.confirmed_at, o.order_id " +
|
||||
"FROM orders o " +
|
||||
"JOIN invoices i ON o.invoice_id = i.id " +
|
||||
"JOIN shares s ON o.share_id = s.id " +
|
||||
"WHERE "
|
||||
var args []any
|
||||
if where.MarketId > 0 {
|
||||
query += "share_id = ANY(SELECT id FROM shares WHERE market_id = $1) "
|
||||
args = append(args, where.MarketId)
|
||||
} else if where.Pubkey != "" {
|
||||
query += "o.pubkey = $1 "
|
||||
args = append(args, where.Pubkey)
|
||||
}
|
||||
if where.Confirmed {
|
||||
query += "AND i.confirmed_at IS NOT NULL "
|
||||
}
|
||||
query += "AND (i.confirmed_at IS NOT NULL OR i.expires_at > CURRENT_TIMESTAMP) "
|
||||
query += "ORDER BY price DESC"
|
||||
rows, err := db.Query(query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var order Order
|
||||
rows.Scan(&order.Id, &order.ShareId, &order.Pubkey, &order.Side, &order.Quantity, &order.Price, &order.Share.Description, &order.OrderId)
|
||||
rows.Scan(&order.Id, &order.ShareId, &order.Pubkey, &order.Side, &order.Quantity, &order.Price, &order.InvoiceId, &order.Share.Description, &order.Share.MarketId, &order.Invoice.ConfirmedAt, &order.OrderId)
|
||||
*orders = append(*orders, order)
|
||||
}
|
||||
return nil
|
||||
|
@ -125,3 +143,7 @@ func (db *DB) ConfirmInvoice(hash string, confirmedAt time.Time, msatsReceived i
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) FetchUser(pubkey string, user *User) error {
|
||||
return db.QueryRow("SELECT pubkey FROM users WHERE pubkey = $1", pubkey).Scan(&user.Pubkey)
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ type Share struct {
|
|||
type Order struct {
|
||||
Session
|
||||
Share
|
||||
Market
|
||||
Invoice
|
||||
Id string
|
||||
ShareId string `form:"share_id"`
|
||||
Side string `form:"side"`
|
||||
|
@ -131,7 +133,7 @@ func market(c echo.Context) error {
|
|||
return err
|
||||
}
|
||||
var orders []Order
|
||||
if err = db.FetchOrders(market.Id, &orders); err != nil {
|
||||
if err = db.FetchOrders(&FetchOrdersWhere{MarketId: market.Id, Confirmed: true}, &orders); err != nil {
|
||||
return err
|
||||
}
|
||||
data := map[string]any{
|
||||
|
|
|
@ -26,11 +26,23 @@ func div(arg1 int, arg2 int) int {
|
|||
return arg1 / arg2
|
||||
}
|
||||
|
||||
func substr(s string, start, length int) string {
|
||||
if start < 0 || start >= len(s) {
|
||||
return ""
|
||||
}
|
||||
end := start + length
|
||||
if end > len(s) {
|
||||
end = len(s)
|
||||
}
|
||||
return s[start:end]
|
||||
}
|
||||
|
||||
var (
|
||||
FuncMap template.FuncMap = template.FuncMap{
|
||||
"add": add,
|
||||
"sub": sub,
|
||||
"div": div,
|
||||
"add": add,
|
||||
"sub": sub,
|
||||
"div": div,
|
||||
"substr": substr,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ func main() {
|
|||
e.GET("/api/login", verifyLogin)
|
||||
e.GET("/api/session", checkSession)
|
||||
e.POST("/logout", logout)
|
||||
e.GET("/user", sessionGuard(user))
|
||||
e.GET("/market/:id", sessionGuard(market))
|
||||
e.POST("/market/:id/order", sessionGuard(order))
|
||||
e.GET("/invoice/:id", sessionGuard(invoice))
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Session
|
||||
}
|
||||
|
||||
func user(c echo.Context) error {
|
||||
session := c.Get("session").(Session)
|
||||
u := User{}
|
||||
if err := db.FetchUser(session.Pubkey, &u); err != nil {
|
||||
return err
|
||||
}
|
||||
var orders []Order
|
||||
if err := db.FetchOrders(&FetchOrdersWhere{Pubkey: session.Pubkey}, &orders); err != nil {
|
||||
return err
|
||||
}
|
||||
data := map[string]any{
|
||||
"session": c.Get("session"),
|
||||
"user": u,
|
||||
"Orders": orders,
|
||||
}
|
||||
return c.Render(http.StatusOK, "user.html", data)
|
||||
}
|
Loading…
Reference in New Issue