You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
bit4sat/api/download.go

328 lines
7.9 KiB
Go

package api
import (
"encoding/gob"
"fmt"
"log"
"net/http"
"time"
"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"
"github.com/mediocregopher/radix/v3"
)
func download(c *gin.Context) {
var ok bool
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 files metadata for upload
var upFilesMeta []storage.FileUpload
upFilesMeta, err = storage.GetUploadFilesMeta(upId)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
// Get the download 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
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
}
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
}
}
// Test if we are alread in a download session
// If the stored dlId is different than the
// current dl id it means we're downloading a different
// file
if dlSessVal == nil || (sessDlId != dlId) {
// This is a new download session
log.Println("new download session")
session.Values["dlId"] = dlId
// 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)
//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
}
invoiceOpts := ln.InvoiceOpts{
Amount: up.AskAmount,
Curr: ln.CurrencyID[up.AskCurrency],
Memo: fmt.Sprintf("bit4sat download: %s", sessId),
}
log.Printf("invoice opts %#v", invoiceOpts)
invoice, err := ln.NewInvoice(invoiceOpts)
if err != nil {
utils.JSONErrPriv(c, http.StatusInternalServerError, err)
return
}
// 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))
session.Values["session-id"] = sessId
session.Values["invoice-id"] = invoice.RHash
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
}
c.JSON(http.StatusPaymentRequired, gin.H{
"invoice": invoice,
"files": upFilesMeta,
})
// This is a returning download session to pay the invoice
} else {
log.Printf("continue download session id: %s", dlSess)
// Get invoice-id linked to sessionId
invIdVal := session.Values["invoice-id"]
if invIdVal == nil {
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")
return
}
var invoiceId string
if invoiceId, ok = invIdVal.(string); !ok {
log.Printf("Could not find invoice in session %s", dlSess)
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")
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
}
// 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
if invoice.Settled {
log.Println("dl invoice paid, sending")
downKey, err := storage.GetShortId()
if err != nil {
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
}
c.JSON(http.StatusOK, gin.H{
"download_key": downKey,
"invoice": invoice,
})
return
}
c.JSON(http.StatusPaymentRequired, gin.H{
"invoice": invoice,
"files": upFilesMeta,
})
}
return
}
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
}
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
}
func init() {
gob.Register(&ln.Invoice{})
gob.Register(&[]storage.FileUpload{})
}