Add user page

This commit is contained in:
ekzyis 2023-09-09 22:52:51 +02:00
parent e6aec21aff
commit d987934ba4
10 changed files with 186 additions and 26 deletions

View File

@ -21,9 +21,7 @@
<nav> <nav>
<a href="/">home</a> <a href="/">home</a>
{{ if .session }} {{ if .session }}
<form action='/logout' method='post'> <a href='/user'>user</a>
<button type='submit'>logout</button>
</form>
{{ else }} <a href="/login">login</a> {{ end }} {{ else }} <a href="/login">login</a> {{ end }}
</nav> </nav>
</header> </header>

View File

@ -22,9 +22,7 @@
<nav> <nav>
<a href="/">home</a> <a href="/">home</a>
{{ if .session }} {{ if .session }}
<form action='/logout' method='post'> <a href='/user'>user</a>
<button type='submit'>logout</button>
</form>
{{ else }} <a href="/login">login</a> {{ end }} {{ else }} <a href="/login">login</a> {{ end }}
</nav> </nav>
</header> </header>

View File

@ -21,9 +21,7 @@
<nav> <nav>
<a href="/">home</a> <a href="/">home</a>
{{ if .session }} {{ if .session }}
<form action='/logout' method='post'> <a href='/user'>user</a>
<button type='submit'>logout</button>
</form>
{{ else }} <a href="/login">login</a> {{ end }} {{ else }} <a href="/login">login</a> {{ end }}
</nav> </nav>
</header> </header>

View File

@ -22,9 +22,7 @@
<nav> <nav>
<a href="/">home</a> <a href="/">home</a>
{{ if .session }} {{ if .session }}
<form action='/logout' method='post'> <a href='/user'>user</a>
<button type='submit'>logout</button>
</form>
{{ else }} <a href="/login">login</a> {{ end }} {{ else }} <a href="/login">login</a> {{ end }}
</nav> </nav>
</header> </header>

102
pages/user.html Normal file
View File

@ -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>

View File

@ -69,22 +69,40 @@ func (db *DB) FetchShares(marketId int, shares *[]Share) error {
return nil return nil
} }
func (db *DB) FetchOrders(marketId int, orders *[]Order) error { type FetchOrdersWhere struct {
rows, err := db.Query(""+ MarketId int
"SELECT o.id, share_id, o.pubkey, o.side, o.quantity, o.price, s.description, o.order_id "+ 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 " + "FROM orders o " +
"JOIN invoices i ON o.invoice_id = i.id " + "JOIN invoices i ON o.invoice_id = i.id " +
"JOIN shares s ON o.share_id = s.id " + "JOIN shares s ON o.share_id = s.id " +
"WHERE share_id = ANY(SELECT id FROM shares WHERE market_id = $1) "+ "WHERE "
"AND i.confirmed_at IS NOT NULL "+ var args []any
"ORDER BY price DESC", marketId) 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 { if err != nil {
return err return err
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
var order Order 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) *orders = append(*orders, order)
} }
return nil return nil
@ -125,3 +143,7 @@ func (db *DB) ConfirmInvoice(hash string, confirmedAt time.Time, msatsReceived i
} }
return nil 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)
}

View File

@ -26,6 +26,8 @@ type Share struct {
type Order struct { type Order struct {
Session Session
Share Share
Market
Invoice
Id string Id string
ShareId string `form:"share_id"` ShareId string `form:"share_id"`
Side string `form:"side"` Side string `form:"side"`
@ -131,7 +133,7 @@ func market(c echo.Context) error {
return err return err
} }
var orders []Order 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 return err
} }
data := map[string]any{ data := map[string]any{

View File

@ -26,11 +26,23 @@ func div(arg1 int, arg2 int) int {
return arg1 / arg2 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 ( var (
FuncMap template.FuncMap = template.FuncMap{ FuncMap template.FuncMap = template.FuncMap{
"add": add, "add": add,
"sub": sub, "sub": sub,
"div": div, "div": div,
"substr": substr,
} }
) )

View File

@ -63,6 +63,7 @@ func main() {
e.GET("/api/login", verifyLogin) e.GET("/api/login", verifyLogin)
e.GET("/api/session", checkSession) e.GET("/api/session", checkSession)
e.POST("/logout", logout) e.POST("/logout", logout)
e.GET("/user", sessionGuard(user))
e.GET("/market/:id", sessionGuard(market)) e.GET("/market/:id", sessionGuard(market))
e.POST("/market/:id/order", sessionGuard(order)) e.POST("/market/:id/order", sessionGuard(order))
e.GET("/invoice/:id", sessionGuard(invoice)) e.GET("/invoice/:id", sessionGuard(invoice))

29
src/user.go Normal file
View File

@ -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)
}