Better session handling, wip download invoice

- Improved API endpoints logic
- Use gorilla sessions for more granularity
master
Chakib Benziane 5 years ago
parent 24fec00864
commit 53f37ff619

@ -0,0 +1,112 @@
package api
import (
"fmt"
"log"
"net/http"
"git.sp4ke.com/sp4ke/bit4sat/ln"
"git.sp4ke.com/sp4ke/bit4sat/storage"
"git.sp4ke.com/sp4ke/bit4sat/utils"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
)
func DownloadHandler(c *gin.Context) {
dlId := c.Param("dlId")
// Get upload id for dl id
upId, err := storage.GetUploadIdForDlId(dlId)
if err != nil {
utils.JSONErr(c, http.StatusNotFound, "this file does not exist anymore")
return
}
// Get the upload session
session, err := SessionStore.Get(c.Request, DlSessionKey)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
dlSess, exists := session.Values["session-id"]
log.Printf("%#v", dlSess)
// Test if we are alread in a download session
if !exists {
// This is a new download session
log.Println("new download session")
// Create a unique session id for this download session
sessId, err := storage.GetShortId()
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
log.Printf("going to generate invoice for %s", upId)
session.AddFlash(sessId, "session-id")
session.Save(c.Request, c.Writer)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
//TODO: use fee asked by uploader
//
// Get upload fee asked by uploader
//
invoiceOpts := ln.InvoiceOpts{
Amount: 100,
Curr: ln.CurSat,
Memo: fmt.Sprintf("bit4sat download: %s", sessId),
}
invoice, err := ln.NewInvoice(invoiceOpts)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
// This is a returning download session to pay the invoice
//
c.JSON(http.StatusPaymentRequired, gin.H{
"invoice_rhash": invoice.RHash,
})
} else {
log.Printf("continue download session id: %s", dlSess)
session.Flashes("session-id")
err = session.Save(c.Request, c.Writer)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
c.Status(http.StatusOK)
}
return
}
func TestDownHandler(c *gin.Context) {
sess := sessions.Default(c)
test := sess.Get("test")
log.Printf("%#v", test)
if test != nil {
sess.Clear()
sess.Save()
c.String(http.StatusOK, "I remember you")
} else {
sess.Set("test", 1)
sess.Save()
c.String(http.StatusOK, "i dont remember you")
}
return
}

@ -9,22 +9,33 @@ import (
"git.sp4ke.com/sp4ke/bit4sat/ln"
"git.sp4ke.com/sp4ke/bit4sat/storage"
"git.sp4ke.com/sp4ke/bit4sat/utils"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"github.com/mediocregopher/radix/v3"
)
func sessionHandler(c *gin.Context) {
session := sessions.Default(c)
func upSessionHandler(c *gin.Context) {
lastUp := session.Get(storage.SessLastUploadName)
session.Clear()
session.Save()
upSession, err := SessionStore.Get(c.Request, UpSessionKey)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
lastUp, exists := upSession.Values["lastUp"]
if lastUp != nil {
if exists {
err = upSession.Save(c.Request, c.Writer)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
// Get upload status
key := fmt.Sprintf("upload_status_%s", lastUp.(string))
uId := lastUp.([]string)[0]
key := fmt.Sprintf("upload_status_%s", uId)
var upStatus storage.UpStatus
err := db.DB.Redis.Do(radix.FlatCmd(&upStatus, "GET", key))
@ -102,21 +113,3 @@ func invoiceCbHandler(c *gin.Context) {
return
}
func downHandler(c *gin.Context) {
sess := sessions.Default(c)
test := sess.Get("test")
log.Printf("%#v", test)
if test != nil {
sess.Clear()
sess.Save()
c.String(http.StatusOK, "I remember you")
} else {
sess.Set("test", 1)
sess.Save()
c.String(http.StatusOK, "i dont remember you")
}
return
}

@ -2,15 +2,13 @@ package api
import (
"git.sp4ke.com/sp4ke/bit4sat/config"
"git.sp4ke.com/sp4ke/bit4sat/storage"
"github.com/boj/redistore"
"github.com/gin-contrib/cors"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/redis"
"github.com/gin-gonic/gin"
)
var (
UploadCtrl = storage.UploadCtrl{}
upCtrl = UploadCtrl{}
)
type API struct {
@ -20,28 +18,35 @@ type API struct {
func (api *API) Run() {
// Get last session if it exists
api.router.GET("/a/session", sessionHandler)
uploadRoute := api.router.Group("/a/u")
v1 := api.router.Group("/api/v1")
v1.GET("/session", upSessionHandler)
uploadRoute := v1.Group("/u")
{
uploadRoute.POST("", UploadCtrl.New)
uploadRoute.PUT(":id", UploadCtrl.Upload)
uploadRoute.POST("", upCtrl.New)
uploadRoute.PUT("/:id", upCtrl.Upload)
//TODO: make a poll version + this one
uploadRoute.GET("/check/:id", UploadCtrl.CheckStatus)
uploadRoute.GET("/poll/:id", UploadCtrl.PollStatus)
uploadRoute.GET("/check/:id", upCtrl.CheckStatus)
uploadRoute.GET("/poll/:id", upCtrl.PollStatus)
}
// Download route
downRoute := api.router.Group("/a/d")
downRoute := v1.Group("/d")
{
downRoute.GET("t", downHandler)
// Download
downRoute.GET(":dlId", DownloadHandler)
}
// test rout
v1.GET("/t", TestDownHandler)
// Websocket server
//api.router.GET("/ws", ws.Serve)
// LN charge callback
api.router.POST("/"+config.ChargeCallbackEndpoint, invoiceCbHandler)
//api.router.POST("/"+config.ChargeCallbackEndpoint, invoiceCbHandler)
api.router.Run(config.ApiListen)
}
@ -51,14 +56,13 @@ func NewAPI() *API {
router.Use(cors.Default())
// Setup Session
sessionStore, err := redis.NewStore(10, "tcp", "redis:6379",
var err error
SessionStore, err = redistore.NewRediStore(10, "tcp", "redis:6379",
"", []byte(config.SessionSecret))
if err != nil {
panic(err)
}
router.Use(sessions.Sessions("bit4sat", sessionStore))
//
//
//router.Use(secure.New(secure.Config{

@ -0,0 +1,12 @@
package api
import "github.com/boj/redistore"
const (
UpSessionKey = "bit4sat-up"
DlSessionKey = "bit4sat-dl"
)
var (
SessionStore *redistore.RediStore
)

@ -1,4 +1,4 @@
package storage
package api
import (
"crypto/sha256"
@ -11,15 +11,11 @@ import (
"git.sp4ke.com/sp4ke/bit4sat/db"
"git.sp4ke.com/sp4ke/bit4sat/ln"
"git.sp4ke.com/sp4ke/bit4sat/storage"
"git.sp4ke.com/sp4ke/bit4sat/utils"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
)
const (
SessLastUploadName = "last-upload"
)
type UploadCtrl struct{}
//TODO: tell client to avoid sending duplicate if we already have hash
@ -34,7 +30,7 @@ func (ctrl UploadCtrl) New(c *gin.Context) {
}
// Create unique id
uploadId, err := GetShortId()
uploadId, err := storage.GetShortId()
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
@ -56,7 +52,7 @@ func (ctrl UploadCtrl) New(c *gin.Context) {
}
// Create new upload
up := &Upload{}
up := &storage.Upload{}
up.UploadId = uploadId
up.AskFee = uploadForm.RequestPayment
up.AskCurrency = uploadForm.PaymentCurrency
@ -64,7 +60,7 @@ func (ctrl UploadCtrl) New(c *gin.Context) {
// Generate unique download id
//
dlId, err := GetShortId()
dlId, err := storage.GetShortId()
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
@ -80,7 +76,7 @@ func (ctrl UploadCtrl) New(c *gin.Context) {
}
for _, file := range uploadForm.Files {
fup := &FileUpload{}
fup := &storage.FileUpload{}
fup.UploadId = uploadId
fup.FileName, fup.FileExt = utils.CleanFileName(file.Name)
@ -104,34 +100,47 @@ func (ctrl UploadCtrl) New(c *gin.Context) {
}
// Set upload status to new
err = SetUploadStatus(uploadId, UpNew)
err = storage.SetUploadStatus(uploadId, storage.UpNew)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
// Upload seems valid, handle new upload procedure
session := sessions.Default(c)
//
// Get the upload session
upSession, err := SessionStore.Get(c.Request, UpSessionKey)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
// Set the uploadId as last upload for session
session.AddFlash(SessLastUploadName, uploadId)
//session.AddFlash(SessLastUploadName, uploadId)
session.Save()
upSession.AddFlash(uploadId)
err = upSession.Save(c.Request, c.Writer)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
// If not free upload generate a an invoice
// TODO: check if upload fee is free
if uploadForm.RequestPayment {
invoice, err := ln.NewInvoiceForUpload(uploadForm.RequestPaymentAmount,
ln.CurrencyID[uploadForm.PaymentCurrency],
uploadId)
invoiceOpts := ln.InvoiceOpts{
Amount: uploadForm.RequestPaymentAmount,
Curr: ln.CurrencyID[uploadForm.PaymentCurrency],
Memo: fmt.Sprintf("bit4sat upload: %s", uploadId),
}
invoice, err := ln.NewInvoice(invoiceOpts)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
// Update Upload Invoice
err = SetUploadInvoice(uploadId, invoice)
err = storage.SetUploadInvoice(uploadId, invoice)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
@ -148,7 +157,7 @@ func (ctrl UploadCtrl) New(c *gin.Context) {
//}
c.JSON(http.StatusOK, gin.H{
"status": UpNew,
"status": storage.UpNew,
"invoice": invoice,
"upload_id": uploadId,
})
@ -172,7 +181,7 @@ func (ctrl UploadCtrl) New(c *gin.Context) {
func (ctrl UploadCtrl) CheckStatus(c *gin.Context) {
uploadId := c.Param("id")
exists, err := IdExists(uploadId)
exists, err := storage.IdExists(uploadId)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
@ -184,7 +193,7 @@ func (ctrl UploadCtrl) CheckStatus(c *gin.Context) {
}
// Get upload status
uploadStatus, err := GetUploadStatus(uploadId)
uploadStatus, err := storage.GetUploadStatus(uploadId)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
@ -198,7 +207,7 @@ func (ctrl UploadCtrl) CheckStatus(c *gin.Context) {
}
// Get invoice id for upload
invoiceId, err := GetUploadIdInvoiceId(uploadId)
invoiceId, err := storage.GetUploadIdInvoiceId(uploadId)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
@ -217,7 +226,7 @@ func (ctrl UploadCtrl) CheckStatus(c *gin.Context) {
return
}
c.JSON(InvoiceHttpCode(invoice), gin.H{
c.JSON(ln.InvoiceHttpCode(invoice), gin.H{
"upload_id": uploadId,
"invoice": invoice,
"status": uploadStatus,
@ -228,7 +237,7 @@ func (ctrl UploadCtrl) CheckStatus(c *gin.Context) {
func (ctrl UploadCtrl) PollStatus(c *gin.Context) {
uploadId := c.Param("id")
exists, err := IdExists(uploadId)
exists, err := storage.IdExists(uploadId)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
@ -239,13 +248,13 @@ func (ctrl UploadCtrl) PollStatus(c *gin.Context) {
return
}
uploadStatus, err := GetUploadStatus(uploadId)
uploadStatus, err := storage.GetUploadStatus(uploadId)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
if uploadStatus&UpNew != 0 {
if uploadStatus&storage.UpNew != 0 {
c.JSON(http.StatusCreated, gin.H{
"status": uploadStatus,
})
@ -253,7 +262,7 @@ func (ctrl UploadCtrl) PollStatus(c *gin.Context) {
}
// we are not in UpNew state so we should have an invoice
invoice, err := GetUploadInvoice(uploadId)
invoice, err := storage.GetUploadInvoice(uploadId)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
@ -282,7 +291,7 @@ func (ctrl UploadCtrl) PollStatus(c *gin.Context) {
// if expired return with expired response
if invoice.Expired {
err := SetUploadStatus(uploadId, UpPayExpired)
err := storage.SetUploadStatus(uploadId, storage.UpPayExpired)
if err != nil {
log.Printf("Redis error: %s", err)
}
@ -301,43 +310,51 @@ func (ctrl UploadCtrl) PollStatus(c *gin.Context) {
//
log.Println("POLL: invoice was paid")
uploadStatus |= UpPaid
err := SetUploadStatus(uploadId, uploadStatus)
uploadStatus |= storage.UpPaid
err := storage.SetUploadStatus(uploadId, uploadStatus)
if err != nil {
log.Printf("Redis error: %s", err)
}
// Update Upload Invoice
err = SetUploadInvoice(uploadId, invoice)
err = storage.SetUploadInvoice(uploadId, invoice)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
// Generate admin token and save it
adminToken, err := GetLongId()
adminToken, err := storage.GetLongId()
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
err = SetUploadAdminToken(uploadId, adminToken)
err = storage.SetUploadAdminToken(uploadId, adminToken)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
// Get the download id
dlId, err := GetDownloadId(uploadId)
dlId, err := storage.GetDownloadId(uploadId)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
// Clear the session
session := sessions.Default(c)
session.Clear()
session.Save()
upSession, err := SessionStore.Get(c.Request, UpSessionKey)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
upSession.Flashes()
err = upSession.Save(c.Request, c.Writer)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
// invoice was paid
c.JSON(http.StatusAccepted, gin.H{
@ -368,7 +385,7 @@ func (ctrl UploadCtrl) Upload(c *gin.Context) {
// Check that upload ID exists
uploadId := c.Param("id")
exists, err := IdExists(uploadId)
exists, err := storage.IdExists(uploadId)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
@ -438,7 +455,7 @@ func (ctrl UploadCtrl) Upload(c *gin.Context) {
sum256 := hex.EncodeToString(hasher.Sum(nil))
// Get the file's metadata from upload table
up, err := GetByHashID(sum256, uploadId)
up, err := storage.GetByHashID(sum256, uploadId)
if err != nil {
utils.JSONErrPriv(c, http.StatusNotFound, err)
db.RollbackTx(tx, nil)
@ -456,7 +473,7 @@ func (ctrl UploadCtrl) Upload(c *gin.Context) {
//
//
log.Println("Storing file")
err = StoreFormFile(formFd, up.SHA256+up.FileExt)
err = storage.StoreFormFile(formFd, up.SHA256+up.FileExt)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
db.RollbackTx(tx, nil)
@ -464,7 +481,7 @@ func (ctrl UploadCtrl) Upload(c *gin.Context) {
}
log.Printf("%s stored at %s", up.FileName,
GetStoreDestination(up.SHA256+up.FileExt))
storage.GetStoreDestination(up.SHA256+up.FileExt))
// Setting status to stored
log.Println("Updating file upload stored status")
@ -475,7 +492,7 @@ func (ctrl UploadCtrl) Upload(c *gin.Context) {
// Rollback and remove the file
db.RollbackTx(tx, func() {
err := os.Remove(GetStoreDestination(up.SHA256 + up.FileExt))
err := os.Remove(storage.GetStoreDestination(up.SHA256 + up.FileExt))
if err != nil {
log.Println(err)
}
@ -491,7 +508,7 @@ func (ctrl UploadCtrl) Upload(c *gin.Context) {
}
// Set the whole transaction as stored
err = SetUploadStatus(uploadId, UpStored)
err = storage.SetUploadStatus(uploadId, storage.UpStored)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
@ -499,7 +516,7 @@ func (ctrl UploadCtrl) Upload(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": http.StatusOK,
"store_status": UpStored,
"store_status": storage.UpStored,
"upload_id": uploadId,
})

@ -1,4 +1,4 @@
package storage
package api
import "time"

@ -21,7 +21,7 @@ var FixedRates = map[string]float64{
"BTC": 1,
}
func ToMsat(currency string, amount float64) (int64, error) {
func FiatToMsat(currency string, amount float64) (int64, error) {
rate, err := getRate(currency)
if err != nil {
return -1, err

@ -3,6 +3,8 @@ module git.sp4ke.com/sp4ke/bit4sat
go 1.12
require (
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff
github.com/garyburd/redigo v1.6.0 // indirect
github.com/gin-contrib/cors v0.0.0-20190301062745-f9e10995c85a
github.com/gin-contrib/sessions v0.0.0-20190226023029-1532893d996f
github.com/gin-gonic/gin v1.3.0
@ -26,5 +28,6 @@ require (
google.golang.org/appengine v1.5.0 // indirect
google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922 // indirect
google.golang.org/grpc v1.19.1
gopkg.in/boj/redistore.v1 v1.0.0-20160128113310-fc113767cd6b // indirect
gopkg.in/macaroon.v2 v2.1.0
)

@ -52,6 +52,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/frankban/quicktest v1.0.0 h1:QgmxFbprE29UG4oL88tGiiL/7VuiBl5xCcz+wJcJhc0=
github.com/frankban/quicktest v1.0.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/garyburd/redigo v1.6.0 h1:0VruCpn7yAIIu7pWVClQC8wxCJEcG3nyzpMSHKi1PQc=
github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/gin-contrib/cors v0.0.0-20190301062745-f9e10995c85a h1:zBycVvXa03SIX+jdMv8wGu9TMDMWdN8EhaR1FoeKHNo=
github.com/gin-contrib/cors v0.0.0-20190301062745-f9e10995c85a/go.mod h1:pL2kNE+DgDU+eQ+dary5bX0Z6LPP8nR6Mqs1iejILw4=
github.com/gin-contrib/sessions v0.0.0-20190226023029-1532893d996f h1:f8TGHcU6cxOhMwW6YQhpRe+zlr05qNjVmdcK1qigr5I=
@ -241,6 +243,8 @@ google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.19.1 h1:TrBcJ1yqAl1G++wO39nD/qtgpsW9/1+QGrluyMGEYgM=
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
gopkg.in/boj/redistore.v1 v1.0.0-20160128113310-fc113767cd6b h1:U/Uqd1232+wrnHOvWNaxrNqn/kFnr4yu4blgPtQt0N8=
gopkg.in/boj/redistore.v1 v1.0.0-20160128113310-fc113767cd6b/go.mod h1:fgfIZMlsafAHpspcks2Bul+MWUNw/2dyQmjC2faKjtg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

@ -1,22 +1,17 @@
package ln
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"net/url"
"strconv"
"strings"
"git.sp4ke.com/sp4ke/bit4sat/btc"
"git.sp4ke.com/sp4ke/bit4sat/bus"
"git.sp4ke.com/sp4ke/bit4sat/config"
"git.sp4ke.com/sp4ke/bit4sat/db"
"git.sp4ke.com/sp4ke/bit4sat/lndrpc"
"git.sp4ke.com/sp4ke/bit4sat/utils"
"github.com/gin-gonic/gin"
"github.com/mediocregopher/radix/v3"
)
@ -24,20 +19,6 @@ var (
Client *http.Client
)
func Info() (gin.H, error) {
result := make(gin.H)
resp, err := Client.Get(getUrl(InfoEndpoint))
if err != nil {
return nil, err
}
jsonDec := json.NewDecoder(resp.Body)
jsonDec.Decode(&result)
return result, nil
}
// Quick check if invoice was paid
func CheckInvoice(id string) (*Invoice, error) {
@ -97,160 +78,50 @@ func PollPaidInvoice(invoiceId string, invoicePaidChan chan<- *Invoice, errorCha
}
}
func PollPaidInvoiceTMP(invoiceId string, invoiceChan chan<- *Invoice, errorChan chan<- error) {
invoice := Invoice{}
reqParams := url.Values{}
// Timeout in seconds
reqParams.Set("timeout", strconv.Itoa(60))
reqURI := fmt.Sprintf("%s/%s/wait?%s",
getUrl(InvoiceEndpoint),
invoiceId,
reqParams.Encode())
log.Printf("polling to %s", reqURI)
var err error
var jsonDec *json.Decoder
resp := &http.Response{}
for {
//log.Println("new poll")
resp, err = http.Get(reqURI)
if err != nil {
log.Printf("Error: ", err)
}
jsonDec = json.NewDecoder(resp.Body)
if resp.StatusCode == http.StatusPaymentRequired {
log.Printf("invoice %s not yet paid", invoiceId)
continue
}
if resp.StatusCode == http.StatusGone {
log.Printf("invoice expired %s", invoiceId)
invoice.Expired = true
break
}
if resp.StatusCode == http.StatusOK {
log.Printf("Invoice paid %s", invoice)
break
} else {
log.Println("else !")
// This section handles unknown answer from ln-charge
log.Printf("InvoicePoll Error: unknown resopnse %s", resp.Status)
jsonDec.Decode(&invoice)
close(invoiceChan)
errorChan <- fmt.Errorf("%s: %s", resp.Status, invoice)
return
}
}
jsonDec.Decode(&invoice)
invoiceChan <- &invoice
log.Printf("quit polling %s", invoiceId)
type InvoiceOpts struct {
Amount float64
Curr Currency
Memo string
}
func NewInvoiceForUpload(amount float64, curr Currency, uploadId string) (*Invoice, error) {
func NewInvoice(opts InvoiceOpts) (*Invoice, error) {
var err error
var satValue int64
description := fmt.Sprintf("bit4sat upload: %s", uploadId)
if curr == CurMSat {
if opts.Curr == CurMSat {
log.Println("cur is msat")
// Convert to MSAT, meaning values under 1000 MSAT are ignored
satValue = int64(amount / 1000)
satValue = int64(opts.Amount / 1000)
// Other supported currecies
}
if curr != CurSat {
if opts.Curr != CurSat {
log.Println("cur is custom")
// Get Sat satValue for this invoice
msatVal, err := btc.ToMsat(CurrencyString[curr], amount)
msatVal, err := btc.FiatToMsat(CurrencyString[opts.Curr], opts.Amount)
if err != nil {
return nil, err
}
satValue = msatVal / 1000
} else {
satValue = int64(amount)
satValue = int64(opts.Amount)
}
lndInvoice, err := lndrpc.AddInvoiceSat(description, satValue)
lndInvoice, err := lndrpc.AddInvoiceSat(opts.Memo, satValue)
if err != nil {
return nil, err
}
invoice := InvoiceFromLndIn(lndInvoice)
invoice.QuotedCurrency = CurrencyString[curr]
invoice.QuotedAmount = amount
invoice.QuotedCurrency = CurrencyString[opts.Curr]
invoice.QuotedAmount = opts.Amount
return invoice, nil
}
func NewInvoiceForUploadTMP(amount float32, curr Currency, uploadId string) (*Invoice, error) {
webhookUrl := fmt.Sprintf("http://%s:%s/%s", config.ApiHost, config.ApiPort,
config.ChargeCallbackEndpoint)
reqData := gin.H{
"description": fmt.Sprintf("bit4sat upload: %s", uploadId),
"webhook": webhookUrl,
}
// If Satoshi convert to millisat
if curr == CurSat {
reqData["msatoshi"] = amount * 1000
} else {
reqData["currency"] = CurrencyString[curr]
reqData["amount"] = amount
}
jsonEnc, err := json.Marshal(reqData)
if err != nil {
return nil, err
}
log.Printf("Asking for invoice with: %s", jsonEnc)
resp, err := Client.Post(getUrl(InvoiceEndpoint),
"application/json",
bytes.NewReader(jsonEnc))
if err != nil {
return nil, err
}
invoice := Invoice{}
jsonDec := json.NewDecoder(resp.Body)
jsonDec.Decode(&invoice)
return &invoice, nil
}
func getUrl(endpoint string) string {
url, err := url.Parse(fmt.Sprintf("http://api-token:%s@%s/%s",
config.LnChargeToken,
config.LnChargeApi,
endpoint))
if err != nil {
log.Fatal(err)
}
return url.String()
}
func init() {
Client = utils.NewHttpClient()
}

@ -1,12 +1,10 @@
package storage
package ln
import (
"net/http"
"git.sp4ke.com/sp4ke/bit4sat/ln"
)
func InvoiceHttpCode(invoice *ln.Invoice) int {
func InvoiceHttpCode(invoice *Invoice) int {
if invoice.Expired {
return http.StatusGone
}

@ -12,6 +12,8 @@ func main() {
defer db.DB.Sql.Close()
api := api.NewAPI()
api.Run()
v1 := api.NewAPI()
defer api.SessionStore.Close()
v1.Run()
}

@ -255,7 +255,7 @@ func GetDownloadId(uploadId string) (string, error) {
//if !exists get from sql and store in redis
if !exists {
log.Printf("download id not found for %s, trying form db", uploadId)
log.Printf("redis: dlId %s not found for, trying form db", uploadId)
query := `SELECT download_id from uploads WHERE upload_id = $1`
err = DB.Sql.Get(&downloadId, query, uploadId)
if err != nil {
@ -268,12 +268,49 @@ func GetDownloadId(uploadId string) (string, error) {
// Store it back on redis cache
err = DB.Redis.Do(radix.FlatCmd(nil, "SET", key, downloadId))
} else {
err = DB.Redis.Do(radix.FlatCmd(&downloadId, "GET", key))
}
return downloadId, err
}
func GetUploadIdForDlId(dlId string) (string, error) {
key := fmt.Sprintf("upload_for_download_%s", dlId)
// Try redis
var upId string
var exists bool
// check exists on redis
err := DB.Redis.Do(radix.FlatCmd(&exists, "EXISTS", key))
if err != nil {
return "", err
}
// if not get from sql and store on redis
if !exists {
log.Printf("redis: upId %s not found , trying form db", dlId)
query := `SELECT upload_id from uploads WHERE download_id = $1`
err = DB.Sql.Get(&upId, query, dlId)
if err != nil {
return "", err
}
if len(upId) == 0 {
return "", fmt.Errorf("upload id missing in %s", dlId)
}
// Store it back on redis cache
err = DB.Redis.Do(radix.FlatCmd(nil, "SET", key, upId))
} else {
err = DB.Redis.Do(radix.FlatCmd(&upId, "GET", key))
}
return upId, err
}
func GetUploadInvoice(uploadId string) (*ln.Invoice, error) {
var invoice ln.Invoice

@ -11,7 +11,12 @@ header / {
## API
proxy /a localhost:8880 {
proxy /api localhost:8880 {
transparent
}
## test
proxy /t localhost:8880 {
transparent
}

@ -10,11 +10,8 @@ header / {
}
proxy /d bit4sat-api:8880
proxy /api bit4sat-api:8880
proxy /a bit4sat-api:8880 {
transparent
}
#log /api stdout
log / stdout

@ -7,7 +7,7 @@
If choose to setup a fee for your link, every download will
require a payment.
</p>
<p>You can redeem your payments at any time.</p>
<p>You can redeem your payments at any time using your admin token.</p>
</div>

@ -5,10 +5,10 @@ import 'babel-polyfill';
const endPoints = {
upload: '/a/u',
session: '/a/session',
pollstatus: '/a/u/poll',
checkstatus: '/a/u/check'
upload: '/api/v1/u',
session: '/api/v1/session',
pollstatus: '/api/v1/u/poll',
checkstatus: '/api/v1/u/check'
}

Loading…
Cancel
Save