Add user page
This commit is contained in:
parent
e6aec21aff
commit
d987934ba4
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
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
|
||||||
"FROM orders o "+
|
Confirmed bool
|
||||||
"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) "+
|
func (db *DB) FetchOrders(where *FetchOrdersWhere, orders *[]Order) error {
|
||||||
"AND i.confirmed_at IS NOT NULL "+
|
query := "" +
|
||||||
"ORDER BY price DESC", marketId)
|
"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 {
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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