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>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										102
									
								
								pages/user.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								pages/user.html
									
									
									
									
									
										Normal 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>
 | 
			
		||||
							
								
								
									
										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))
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										29
									
								
								src/user.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/user.go
									
									
									
									
									
										Normal 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)
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user