Compare commits

..

5 Commits

Author SHA1 Message Date
ekzyis 4727f18958 wip: create market 2024-08-08 01:39:32 -05:00
ekzyis e540b78994 Fix duplicate build steps 2024-08-08 01:39:32 -05:00
ekzyis 65e7adc7fa Add remote port forwarding command to hotreload.sh 2024-08-08 01:39:32 -05:00
ekzyis a2b717b336 Add shell.nix 2024-07-21 20:39:30 -05:00
ekzyis 139bb2e5b1 Remove unnecessary argument 2024-07-21 20:39:12 -05:00
9 changed files with 152 additions and 8 deletions

View File

@ -4,16 +4,16 @@ PID=$(pidof delphi.market)
set -e
echo ":: remote port forwarding for dev1.delphi.market ::"
ssh -fnNR 4322:localhost:4321 dev1.delphi.market
echo
function restart_server() {
set +e
[[ -z "$PID" ]] || kill -15 $PID
ENV=development make build -B
set -e
tailwindcss -i public/css/_tw-input.css -o public/css/tailwind.css
templ generate -path server/router/pages
go build -o delphi.market .
./delphi.market 2>&1 &
templ generate -path server/router/pages
PID=$(pidof delphi.market)
}

View File

@ -152,7 +152,7 @@ func HandleLnAuthCallback(sc context.Context) echo.HandlerFunc {
}
func NostrAuth(sc context.Context, c echo.Context, action string) error {
return echo.NewHTTPError(http.StatusNotImplemented, nil)
return echo.NewHTTPError(http.StatusNotImplemented)
}
func HandleSessionCheck(sc context.Context) echo.HandlerFunc {

View File

@ -0,0 +1,52 @@
package handler
import (
"database/sql"
"net/http"
"time"
"git.ekzyis.com/ekzyis/delphi.market/server/router/context"
"git.ekzyis.com/ekzyis/delphi.market/server/router/pages/components"
"git.ekzyis.com/ekzyis/delphi.market/types"
"github.com/a-h/templ"
"github.com/labstack/echo/v4"
)
func HandleInvoice(sc context.Context) echo.HandlerFunc {
return func(c echo.Context) error {
var (
db = sc.Db
ctx = c.Request().Context()
hash = c.Param("hash")
u = c.Get("session").(types.User)
inv = types.Invoice{}
expiresIn int
paid bool
qr templ.Component
err error
)
if err = db.QueryRowContext(ctx, ""+
"SELECT user_id, msats, COALESCE(msats_received, 0), expires_at, confirmed_at, bolt11 "+
"FROM invoices "+
"WHERE hash = $1", hash).
Scan(&inv.UserId, &inv.Msats, &inv.MsatsReceived, &inv.ExpiresAt, &inv.ConfirmedAt, &inv.Bolt11); err != nil {
if err == sql.ErrNoRows {
return echo.NewHTTPError(http.StatusNotFound)
}
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusInternalServerError)
}
if u.Id != inv.UserId {
return echo.NewHTTPError(http.StatusNotFound)
}
expiresIn = int(time.Until(inv.ExpiresAt).Seconds())
paid = inv.MsatsReceived >= inv.Msats
qr = components.Invoice(hash, inv.Bolt11, int(inv.Msats), expiresIn, paid)
return components.Modal(qr).Render(context.RenderContext(sc, c), c.Response().Writer)
}
}

View File

@ -67,7 +67,7 @@ func HandleCreate(sc context.Context) echo.HandlerFunc {
return err
}
qr = components.Qr(paymentRequest, "lightning:"+paymentRequest)
qr = components.Invoice(hash.String(), paymentRequest, int(amount), int(expiry), false)
return components.Modal(qr).Render(context.RenderContext(sc, c), c.Response().Writer)
}

View File

@ -0,0 +1,49 @@
package components
import (
"strconv"
)
templ Invoice(hash string, bolt11 string, msats int, expiresIn int, paid bool) {
<div class="p-5 border border-muted bg-background text-center ">
@Qr(bolt11, "lightning:"+bolt11)
<small class="mx-auto w-[256px] my-1 break-words">{ bolt11 }</small>
<div class="my-1">{ strconv.Itoa(msats/1000) } sats</div>
if !paid {
<div class="font-mono" id="countdown" countdown-data={ templ.JSONString(expiresIn)} hx-preserve></div>
<script type="text/javascript">
function countdown() {
// a script that was included in a previous response might already be running the countdown
if (typeof interval !== "undefined") return
var $ = selector => document.querySelector(selector)
var expiresIn = JSON.parse($("#countdown").getAttribute("countdown-data"))
function pad(num, places) {
return String(num).padStart(places, "0")
}
function _countdown() {
var minutes = Math.floor(expiresIn / 60)
var seconds = expiresIn % 60
var text = `${pad(minutes, 2)}:${pad(seconds, 2)}`
$("#countdown").innerText = text
expiresIn--
}
_countdown()
interval = setInterval(_countdown, 1000)
}
countdown()
</script>
<div
hx-get={ string(templ.SafeURL("/invoice/" + hash)) }
hx-trigger="load delay:1s"
hx-target="#modal"
hx-swap="outerHTML"
hx-select="#modal" />
} else {
<div class="font-mono text-green">PAID</div>
}
</div>
}

View File

@ -27,4 +27,6 @@ func Init(e *echo.Echo, sc Context) {
e.GET("/user", handler.HandleUser(sc), middleware.SessionGuard(sc))
e.POST("/logout", handler.HandleLogout(sc), middleware.SessionGuard(sc))
e.GET("/invoice/:hash", handler.HandleInvoice(sc), middleware.SessionGuard(sc))
}

18
shell.nix Normal file
View File

@ -0,0 +1,18 @@
let
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-23.11";
pkgs = import nixpkgs { config = {}; overlays = []; };
in
pkgs.mkShell {
packages = with pkgs; [
go
tailwindcss
gnumake
inotify-tools
figlet
];
shellHook = ''
# install templ if not already installed
command -v templ > /dev/null || go install github.com/a-h/templ/cmd/templ@latest
'';
}

View File

@ -6,7 +6,12 @@ module.exports = {
center: true,
padding: '1rem'
},
extend: {},
extend: {
colors: {
'background': '191d21',
'muted': '#6c757d',
},
},
},
plugins: [
function ({ addComponents }) {

View File

@ -1,6 +1,10 @@
package types
import "time"
import (
"time"
"gopkg.in/guregu/null.v4"
)
type User struct {
Id int
@ -9,3 +13,17 @@ type User struct {
NostrPubkey string
Msats int64
}
type Invoice struct {
Id int
UserId int
Msats int64
MsatsReceived int64
Hash string
Bolt11 string
CreatedAt time.Time
ExpiresAt time.Time
ConfirmedAt null.Time
HeldSince bool
Description string
}