diff --git a/api/download.go b/api/download.go new file mode 100644 index 0000000..b02f555 --- /dev/null +++ b/api/download.go @@ -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 +} diff --git a/api/handlers.go b/api/handlers.go index f130ad4..bdb9373 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -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 -} diff --git a/api/routes.go b/api/routes.go index e9e8088..4a6177d 100644 --- a/api/routes.go +++ b/api/routes.go @@ -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{ diff --git a/api/session.go b/api/session.go new file mode 100644 index 0000000..1041e18 --- /dev/null +++ b/api/session.go @@ -0,0 +1,12 @@ +package api + +import "github.com/boj/redistore" + +const ( + UpSessionKey = "bit4sat-up" + DlSessionKey = "bit4sat-dl" +) + +var ( + SessionStore *redistore.RediStore +) diff --git a/storage/upload_ctrl.go b/api/upload_ctrl.go similarity index 79% rename from storage/upload_ctrl.go rename to api/upload_ctrl.go index 43e12db..ff56204 100644 --- a/storage/upload_ctrl.go +++ b/api/upload_ctrl.go @@ -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, }) diff --git a/storage/upload_form.go b/api/upload_form.go similarity index 97% rename from storage/upload_form.go rename to api/upload_form.go index e626ff2..f1c0f01 100644 --- a/storage/upload_form.go +++ b/api/upload_form.go @@ -1,4 +1,4 @@ -package storage +package api import "time" diff --git a/btc/price.go b/btc/price.go index bd2cb97..331ea11 100644 --- a/btc/price.go +++ b/btc/price.go @@ -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 diff --git a/go.mod b/go.mod index fea71e5..78cd6e9 100644 --- a/go.mod +++ b/go.mod @@ -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 ) diff --git a/go.sum b/go.sum index dec2c12..3614f72 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/ln/api.go b/ln/api.go index e73d846..7fefb71 100644 --- a/ln/api.go +++ b/ln/api.go @@ -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() } diff --git a/storage/utils.go b/ln/utils.go similarity index 63% rename from storage/utils.go rename to ln/utils.go index 0debc71..83fd624 100644 --- a/storage/utils.go +++ b/ln/utils.go @@ -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 } diff --git a/main.go b/main.go index bcaa26e..eb32b00 100644 --- a/main.go +++ b/main.go @@ -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() } diff --git a/storage/upload_model.go b/storage/upload_model.go index a0418ad..d96d33e 100644 --- a/storage/upload_model.go +++ b/storage/upload_model.go @@ -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 diff --git a/web/Caddyfile b/web/Caddyfile index 643a8d6..5526047 100644 --- a/web/Caddyfile +++ b/web/Caddyfile @@ -11,7 +11,12 @@ header / { ## API -proxy /a localhost:8880 { +proxy /api localhost:8880 { + transparent +} + +## test +proxy /t localhost:8880 { transparent } diff --git a/web/Caddyfile-prod b/web/Caddyfile-prod index 831ab8c..a43b6e0 100644 --- a/web/Caddyfile-prod +++ b/web/Caddyfile-prod @@ -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 diff --git a/web/src/Home.vue b/web/src/Home.vue index f2a0827..eaa78fb 100644 --- a/web/src/Home.vue +++ b/web/src/Home.vue @@ -7,7 +7,7 @@ If choose to setup a fee for your link, every download will require a payment.

-

You can redeem your payments at any time.

+

You can redeem your payments at any time using your admin token.

diff --git a/web/src/api.js b/web/src/api.js index b6e07a6..94ade41 100644 --- a/web/src/api.js +++ b/web/src/api.js @@ -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' }