delphi.market/router.go

175 lines
5.3 KiB
Go

package main
import (
"database/sql"
"encoding/base64"
"fmt"
"html/template"
"io"
"net/http"
"os"
"time"
"github.com/labstack/echo/v4"
"github.com/skip2/go-qrcode"
)
type Template struct {
templates *template.Template
}
func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
return t.templates.ExecuteTemplate(w, name, data)
}
func index(c echo.Context) error {
return c.Render(http.StatusOK, "index.html", map[string]any{"session": c.Get("session"), "VERSION": VERSION, "COMMIT_LONG_SHA": COMMIT_LONG_SHA})
}
func login(c echo.Context) error {
lnauth, err := lnAuth()
if err != nil {
return err
}
var sessionId string
err = db.QueryRow("INSERT INTO lnauth(k1, lnurl) VALUES($1, $2) RETURNING session_id", lnauth.k1, lnauth.lnurl).Scan(&sessionId)
if err != nil {
return err
}
expires := time.Now().Add(60 * 60 * 24 * 365 * time.Second)
c.SetCookie(&http.Cookie{Name: "session", HttpOnly: true, Path: "/", Value: sessionId, Secure: true, Expires: expires})
png, err := qrcode.Encode(lnauth.lnurl, qrcode.Medium, 256)
if err != nil {
return err
}
qr := base64.StdEncoding.EncodeToString([]byte(png))
return c.Render(http.StatusOK, "login.html", map[string]any{"session": c.Get("session"), "lnurl": lnauth.lnurl, "qr": qr})
}
func verifyLogin(c echo.Context) error {
var query LnAuthResponse
if err := c.Bind(&query); err != nil {
c.Logger().Error(err)
return c.JSON(http.StatusBadRequest, map[string]string{"status": "ERROR", "reason": "bad request"})
}
var sessionId string
err := db.QueryRow("SELECT session_id FROM lnauth WHERE k1 = $1", query.K1).Scan(&sessionId)
if err == sql.ErrNoRows {
return c.JSON(http.StatusBadRequest, map[string]string{"status": "ERROR", "reason": "unknown k1"})
} else if err != nil {
c.Logger().Error(err)
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
}
ok, err := lnAuthVerify(&query)
if err != nil {
c.Logger().Error(err)
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
}
if !ok {
c.Logger().Error("bad signature")
return c.JSON(http.StatusUnauthorized, map[string]string{"status": "ERROR", "reason": "bad signature"})
}
_, err = db.Exec("INSERT INTO sessions(pubkey, session_id) VALUES($1, $2)", query.Key, sessionId)
if err != nil {
c.Logger().Error(err)
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
}
_, err = db.Exec("DELETE FROM lnauth WHERE k1 = $1", query.K1)
if err != nil {
c.Logger().Error(err)
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
}
return c.JSON(http.StatusOK, map[string]string{"status": "OK"})
}
func checkSession(c echo.Context) error {
cookie, err := c.Cookie("session")
if err != nil {
c.Logger().Error(err)
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
}
sessionId := cookie.Value
var pubkey string
err = db.QueryRow("SELECT pubkey FROM sessions WHERE session_id = $1", sessionId).Scan(&pubkey)
if err == sql.ErrNoRows {
return c.JSON(http.StatusNotFound, map[string]string{"status": "Not Found", "message": "session not found"})
} else if err != nil {
c.Logger().Error(err)
return c.JSON(http.StatusInternalServerError, map[string]string{"status": "ERROR", "reason": "internal server error"})
}
return c.JSON(http.StatusOK, map[string]string{"pubkey": pubkey})
}
func sessionHandler(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
cookie, err := c.Cookie("session")
if err != nil {
// cookie not found
return next(c)
}
sessionId := cookie.Value
var pubkey string
err = db.QueryRow("SELECT pubkey FROM sessions WHERE session_id = $1", sessionId).Scan(&pubkey)
if err == nil {
// session found
c.Set("session", Session{pubkey})
} else if err != sql.ErrNoRows {
c.Logger().Error(err)
}
// session not found
return next(c)
}
}
func logout(c echo.Context) error {
cookie, err := c.Cookie("session")
if err != nil {
// cookie not found
return c.Redirect(http.StatusSeeOther, "/")
}
sessionId := cookie.Value
_, err = db.Exec("DELETE FROM sessions where session_id = $1", sessionId)
if err != nil {
c.Logger().Error(err)
return err
}
// tell browser that cookie is expired and thus can be deleted
c.SetCookie(&http.Cookie{Name: "session", HttpOnly: true, Path: "/", Value: sessionId, Secure: true, Expires: time.Now()})
return c.Redirect(http.StatusSeeOther, "/")
}
func serve500(c echo.Context) {
f, err := os.Open("public/500.html")
if err != nil {
c.Logger().Error(err)
return
}
err = c.Stream(500, "text/html", f)
if err != nil {
c.Logger().Error(err)
return
}
}
func httpErrorHandler(err error, c echo.Context) {
c.Logger().Error(err)
code := http.StatusInternalServerError
httpError, ok := err.(*echo.HTTPError)
if ok {
code = httpError.Code
}
filePath := fmt.Sprintf("public/%d.html", code)
f, err := os.Open(filePath)
if err != nil {
c.Logger().Error(err)
serve500(c)
return
}
err = c.Stream(code, "text/html", f)
if err != nil {
c.Logger().Error(err)
serve500(c)
return
}
}