2019-04-06 01:45:50 +00:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
2019-04-07 10:30:43 +00:00
|
|
|
"encoding/gob"
|
2019-04-06 01:45:50 +00:00
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
2019-04-07 10:30:43 +00:00
|
|
|
"time"
|
2019-04-06 01:45:50 +00:00
|
|
|
|
2019-04-07 20:31:35 +00:00
|
|
|
"git.sp4ke.com/sp4ke/bit4sat/db"
|
2019-04-06 01:45:50 +00:00
|
|
|
"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"
|
2019-04-07 20:31:35 +00:00
|
|
|
"github.com/mediocregopher/radix/v3"
|
2019-04-06 01:45:50 +00:00
|
|
|
)
|
|
|
|
|
2019-04-07 11:54:41 +00:00
|
|
|
func download(c *gin.Context) {
|
|
|
|
var ok bool
|
2019-04-06 01:45:50 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-04-07 20:31:35 +00:00
|
|
|
// Get files metadata for upload
|
|
|
|
var upFilesMeta []storage.FileUpload
|
|
|
|
upFilesMeta, err = storage.GetUploadFilesMeta(upId)
|
|
|
|
if err != nil {
|
|
|
|
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-04-07 10:30:43 +00:00
|
|
|
// Get the download session
|
2019-04-06 01:45:50 +00:00
|
|
|
session, err := SessionStore.Get(c.Request, DlSessionKey)
|
|
|
|
if err != nil {
|
|
|
|
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-04-09 07:20:46 +00:00
|
|
|
dlSessVal := session.Values["session-id"]
|
|
|
|
var dlSess string
|
|
|
|
|
|
|
|
if dlSess, ok = dlSessVal.(string); dlSessVal != nil && !ok {
|
|
|
|
log.Printf("error parsing session dlSessVal")
|
|
|
|
utils.JSONErr(c, http.StatusExpectationFailed,
|
|
|
|
"start a new download session session")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-04-07 11:54:41 +00:00
|
|
|
dlIdVal := session.Values["dlId"]
|
|
|
|
|
|
|
|
var sessDlId string
|
|
|
|
|
|
|
|
// If no dlId was stored before than this is a new download
|
|
|
|
if dlIdVal == nil {
|
|
|
|
sessDlId = dlId
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if sessDlId, ok = dlIdVal.(string); !ok {
|
|
|
|
log.Printf("cannot cast sessDlId value")
|
|
|
|
utils.JSONErr(c, http.StatusExpectationFailed,
|
|
|
|
"start a new download session session")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2019-04-06 01:45:50 +00:00
|
|
|
|
|
|
|
// Test if we are alread in a download session
|
2019-04-07 11:54:41 +00:00
|
|
|
// If the stored dlId is different than the
|
|
|
|
// current dl id it means we're downloading a different
|
|
|
|
// file
|
2019-04-09 07:20:46 +00:00
|
|
|
if dlSessVal == nil || (sessDlId != dlId) {
|
2019-04-06 01:45:50 +00:00
|
|
|
// This is a new download session
|
|
|
|
log.Println("new download session")
|
|
|
|
|
2019-04-07 11:54:41 +00:00
|
|
|
session.Values["dlId"] = dlId
|
|
|
|
|
2019-04-06 01:45:50 +00:00
|
|
|
// 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)
|
|
|
|
|
2019-04-07 10:30:43 +00:00
|
|
|
//TODO: use fee asked by uploader
|
|
|
|
//
|
|
|
|
// Get upload data asked by uploader
|
|
|
|
up, err := storage.GetUploadById(upId)
|
|
|
|
if err != nil {
|
|
|
|
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
|
|
|
|
return
|
|
|
|
}
|
2019-04-06 01:45:50 +00:00
|
|
|
|
|
|
|
invoiceOpts := ln.InvoiceOpts{
|
2019-04-07 10:30:43 +00:00
|
|
|
Amount: up.AskAmount,
|
|
|
|
Curr: ln.CurrencyID[up.AskCurrency],
|
2019-04-06 01:45:50 +00:00
|
|
|
Memo: fmt.Sprintf("bit4sat download: %s", sessId),
|
|
|
|
}
|
|
|
|
|
2019-04-07 11:54:41 +00:00
|
|
|
log.Printf("invoice opts %#v", invoiceOpts)
|
|
|
|
|
2019-04-06 01:45:50 +00:00
|
|
|
invoice, err := ln.NewInvoice(invoiceOpts)
|
|
|
|
if err != nil {
|
|
|
|
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-04-07 20:31:35 +00:00
|
|
|
// Store invoice_id ---> upload_id
|
|
|
|
key := fmt.Sprintf("invoice_id_%s_upload_id", invoice.RHash)
|
|
|
|
err = db.DB.Redis.Do(radix.FlatCmd(nil, "SET", key, upId))
|
|
|
|
if err != nil {
|
|
|
|
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store invoice_<invoice_id>
|
|
|
|
invoiceKey := fmt.Sprintf("invoice_%s", invoice.RHash)
|
|
|
|
err = db.DB.Redis.Do(radix.FlatCmd(nil, "SET", invoiceKey, invoice))
|
2019-04-07 11:54:41 +00:00
|
|
|
|
2019-04-07 10:30:43 +00:00
|
|
|
session.Values["session-id"] = sessId
|
2019-04-07 20:31:35 +00:00
|
|
|
session.Values["invoice-id"] = invoice.RHash
|
2019-04-07 10:30:43 +00:00
|
|
|
|
|
|
|
invoiceExpires := time.Time(invoice.ExpiresAt).Sub(time.Now()).Seconds()
|
|
|
|
|
|
|
|
//TODO: this session can be reused during this hour
|
|
|
|
session.Options.MaxAge = int(invoiceExpires)
|
|
|
|
|
|
|
|
//log.Printf("session: %#v", session.Values)
|
|
|
|
|
|
|
|
err = session.Save(c.Request, c.Writer)
|
|
|
|
if err != nil {
|
|
|
|
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-04-06 01:45:50 +00:00
|
|
|
c.JSON(http.StatusPaymentRequired, gin.H{
|
2019-04-07 10:30:43 +00:00
|
|
|
"invoice": invoice,
|
|
|
|
"files": upFilesMeta,
|
2019-04-06 01:45:50 +00:00
|
|
|
})
|
|
|
|
|
2019-04-07 10:30:43 +00:00
|
|
|
// This is a returning download session to pay the invoice
|
2019-04-06 01:45:50 +00:00
|
|
|
} else {
|
|
|
|
log.Printf("continue download session id: %s", dlSess)
|
|
|
|
|
2019-04-07 20:31:35 +00:00
|
|
|
// Get invoice-id linked to sessionId
|
|
|
|
invIdVal := session.Values["invoice-id"]
|
|
|
|
if invIdVal == nil {
|
2019-04-07 10:30:43 +00:00
|
|
|
session.Options.MaxAge = -1
|
|
|
|
|
|
|
|
err = session.Save(c.Request, c.Writer)
|
|
|
|
if err != nil {
|
|
|
|
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
utils.JSONErr(c, http.StatusExpectationFailed,
|
|
|
|
"invoice not found, start a new download session session")
|
2019-04-06 01:45:50 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-04-07 20:31:35 +00:00
|
|
|
var invoiceId string
|
|
|
|
if invoiceId, ok = invIdVal.(string); !ok {
|
|
|
|
log.Printf("Could not find invoice in session %s", dlSess)
|
2019-04-07 10:30:43 +00:00
|
|
|
session.Options.MaxAge = -1
|
|
|
|
|
|
|
|
err = session.Save(c.Request, c.Writer)
|
|
|
|
if err != nil {
|
|
|
|
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
utils.JSONErr(c, http.StatusExpectationFailed,
|
2019-04-07 20:31:35 +00:00
|
|
|
"invoice not found, start a new download session session")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get invoice from invoice_id
|
|
|
|
invoiceKey := fmt.Sprintf("invoice_%s", invoiceId)
|
|
|
|
invoice := ln.Invoice{}
|
|
|
|
err = db.DB.Redis.Do(radix.FlatCmd(&invoice, "GET", invoiceKey))
|
|
|
|
if err != nil {
|
|
|
|
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-04-09 07:20:46 +00:00
|
|
|
// If invoice paid create an authorized download link linked to session
|
|
|
|
// The link will expire after after one hour
|
|
|
|
// Also bump up the expirey of the DlSession to one hour
|
2019-04-07 20:31:35 +00:00
|
|
|
if invoice.Settled {
|
|
|
|
log.Println("dl invoice paid, sending")
|
|
|
|
|
2019-04-09 07:20:46 +00:00
|
|
|
downKey, err := storage.GetShortId()
|
2019-04-07 20:31:35 +00:00
|
|
|
if err != nil {
|
2019-04-09 07:20:46 +00:00
|
|
|
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
key := fmt.Sprintf("session_%s_download_key_%s", dlSess, downKey)
|
|
|
|
// We store the upload id in the value to use it when retreiving
|
|
|
|
// files
|
|
|
|
db.SetKeyVal(key, upId)
|
|
|
|
db.ExpireKey(key, MaxAgeDlKeySession)
|
|
|
|
|
|
|
|
// Session will also expire in 1 hour
|
|
|
|
session.Options.MaxAge = MaxAgeDlKeySession
|
|
|
|
err = session.Save(c.Request, c.Writer)
|
|
|
|
if err != nil {
|
|
|
|
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
|
|
|
|
return
|
2019-04-07 20:31:35 +00:00
|
|
|
}
|
|
|
|
|
2019-04-09 07:20:46 +00:00
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
"download_key": downKey,
|
|
|
|
"invoice": invoice,
|
|
|
|
})
|
2019-04-07 10:30:43 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
c.JSON(http.StatusPaymentRequired, gin.H{
|
|
|
|
"invoice": invoice,
|
2019-04-07 20:31:35 +00:00
|
|
|
"files": upFilesMeta,
|
2019-04-07 10:30:43 +00:00
|
|
|
})
|
2019-04-06 01:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-04-09 07:20:46 +00:00
|
|
|
func getFiles(c *gin.Context) {
|
|
|
|
downKey := c.Param("dlKey")
|
|
|
|
|
|
|
|
// Get the session
|
|
|
|
session, err := SessionStore.Get(c.Request, DlSessionKey)
|
|
|
|
if err != nil {
|
|
|
|
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
dlSessVal := session.Values["session-id"]
|
|
|
|
|
|
|
|
var dlSess string
|
|
|
|
var ok bool
|
|
|
|
|
|
|
|
if dlSess, ok = dlSessVal.(string); dlSessVal != nil && !ok {
|
|
|
|
log.Printf("error parsing session dlSessVal")
|
|
|
|
utils.JSONErr(c, http.StatusExpectationFailed,
|
|
|
|
"user session lost, start a new download session session")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if dlSessVal == nil {
|
|
|
|
utils.JSONErr(c, http.StatusGone, "the download session has expired")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
key := fmt.Sprintf("session_%s_download_key_%s", dlSess, downKey)
|
|
|
|
|
|
|
|
// Check if download key exists in db
|
|
|
|
exists, err := db.Exists(key)
|
|
|
|
if err != nil {
|
|
|
|
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if !exists {
|
|
|
|
utils.JSONErr(c, http.StatusGone, "the download session has expired")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// We should now allow the user to get the files
|
|
|
|
|
|
|
|
// We retreive the files from the upId stored on the session download key
|
|
|
|
var upId string
|
|
|
|
err = db.GetFromKey(key, &upId)
|
|
|
|
if err != nil {
|
|
|
|
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get files metadata for upload
|
|
|
|
var upFilesMeta []storage.FileUpload
|
|
|
|
upFilesMeta, err = storage.GetUploadFilesMeta(upId)
|
|
|
|
if err != nil {
|
|
|
|
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
c.Header("Content-Type", "application/zip")
|
|
|
|
c.Header("Content-Disposition", "attachment; filename=download.zip")
|
|
|
|
|
|
|
|
err = storage.ZipFiles(upFilesMeta, c.Writer)
|
|
|
|
if err != nil {
|
|
|
|
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-04-06 01:45:50 +00:00
|
|
|
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
|
|
|
|
}
|
2019-04-07 10:30:43 +00:00
|
|
|
|
|
|
|
func init() {
|
|
|
|
gob.Register(&ln.Invoice{})
|
|
|
|
gob.Register(&[]storage.FileUpload{})
|
|
|
|
}
|