bit4sat/storage/upload_model.go

249 lines
4.9 KiB
Go
Raw Normal View History

2019-03-15 18:00:35 +00:00
package storage
import (
2019-03-21 17:44:40 +00:00
"database/sql"
2019-03-29 18:50:11 +00:00
"encoding/json"
2019-03-15 18:00:35 +00:00
"errors"
2019-03-26 16:03:13 +00:00
"fmt"
2019-03-15 18:00:35 +00:00
"log"
2019-03-30 18:42:41 +00:00
"git.sp4ke.com/sp4ke/bit4sat/bus"
2019-03-23 18:29:48 +00:00
"git.sp4ke.com/sp4ke/bit4sat/db"
2019-03-29 18:50:11 +00:00
"git.sp4ke.com/sp4ke/bit4sat/ln"
2019-03-21 17:44:40 +00:00
"github.com/jmoiron/sqlx"
2019-03-29 18:50:11 +00:00
"github.com/lib/pq"
2019-03-26 16:03:13 +00:00
"github.com/mediocregopher/radix/v3"
2019-03-15 18:00:35 +00:00
)
var DB = db.DB
const (
2019-03-29 18:50:11 +00:00
//TODO: sync upload status from redis
2019-03-30 18:42:41 +00:00
// TODO: status is currently handled in cache not here
2019-03-15 18:00:35 +00:00
DBUploadSchema = `
2019-03-24 21:36:28 +00:00
CREATE TABLE IF NOT EXISTS upload (
2019-03-27 15:35:36 +00:00
id serial PRIMARY KEY,
upload_id varchar(9) NOT NULL,
2019-03-27 15:35:36 +00:00
sha256 varchar(64) NOT NULL,
file_name varchar(255) NOT NULL,
file_type varchar(255) DEFAULT '',
file_size integer NOT NULL,
file_ext varchar(255) DEFAULT '',
2019-03-29 18:50:11 +00:00
fee integer NOT NULL DEFAULT 0,
2019-03-27 15:35:36 +00:00
status integer DEFAULT 0 REFERENCES upload_status(type),
2019-03-21 17:44:40 +00:00
UNIQUE (upload_id, sha256)
2019-03-15 18:00:35 +00:00
);
`
2019-03-21 17:44:40 +00:00
DBUploadView = `
2019-03-27 15:35:36 +00:00
CREATE OR REPLACE VIEW upload_with_status
2019-03-24 21:36:28 +00:00
AS SELECT
upload.upload_id,
upload.sha256,
upload_status.status,
upload.file_name,
upload.file_type,
upload.file_size,
2019-03-29 18:50:11 +00:00
upload.file_ext,
upload.fee
2019-03-24 21:36:28 +00:00
FROM upload
JOIN upload_status ON
2019-03-21 17:44:40 +00:00
upload.status = upload_status.type
`
DBUploadStatusSchema = `CREATE TABLE IF NOT EXISTS upload_status
(
2019-03-24 21:36:28 +00:00
type INT NOT NULL PRIMARY KEY,
status CHAR(255) NOT NULL UNIQUE
2019-03-21 17:44:40 +00:00
)
`
QNewUpload = `INSERT INTO upload
2019-03-29 18:50:11 +00:00
(upload_id, sha256, file_name, file_type, file_size, file_ext, status, fee)
VALUES
2019-03-29 18:50:11 +00:00
(:upload_id, :sha256, :file_name, :file_type, :file_size, :file_ext, :status, :fee)`
2019-03-21 17:44:40 +00:00
QSetStatus = `UPDATE upload SET status = :status WHERE upload_id = :upload_id `
2019-03-26 16:03:13 +00:00
QGetByHashID = `SELECT upload_id,
sha256,
file_name,
file_type,
file_size,
file_ext,
2019-03-29 18:50:11 +00:00
fee,
2019-03-26 16:03:13 +00:00
status
2019-03-27 15:35:36 +00:00
FROM upload WHERE sha256 = $1 AND upload_id = $2`
2019-03-15 18:00:35 +00:00
)
2019-03-17 19:25:44 +00:00
const (
UpNew = iota
UpWaitingPayment
2019-03-29 18:50:11 +00:00
UpWaitingStorage
UpStored
2019-03-17 19:25:44 +00:00
UpReady
)
2019-03-29 18:50:11 +00:00
// new --> waiting payment (optional) --> waiting storage (client upload)
// --> Sotred --> Ready (sharing url sent to client and acked)
2019-03-21 17:44:40 +00:00
var UploadStatus = map[int]string{
UpNew: "new upload",
UpWaitingPayment: "waiting payment",
2019-03-29 18:50:11 +00:00
UpWaitingStorage: "waiting storage",
UpStored: "stored",
2019-03-21 17:44:40 +00:00
UpReady: "ready",
}
2019-03-15 18:00:35 +00:00
var (
ErrDoesNotExist = errors.New("does not exist")
ErrAlreadyExists = errors.New("already exists")
)
type Upload struct {
ID string `db:"upload_id"`
SHA256 string `db:"sha256"`
FileName string `db:"file_name"`
FileType string `db:"file_type"`
FileSize int64 `db:"file_size"`
FileExt string `db:"file_ext"`
Status int `db:"status"`
2019-03-29 18:50:11 +00:00
Fee int `db:"fee"`
2019-03-26 16:03:13 +00:00
}
2019-03-29 18:50:11 +00:00
// TODO: sync from redis to db
//func SyncUploadStatusToDB(){
//}
2019-03-26 16:03:13 +00:00
2019-03-29 18:50:11 +00:00
func SetUploadStatus(id string, status int) error {
key := fmt.Sprintf("upload_status_%s", id)
2019-03-26 16:03:13 +00:00
return DB.Redis.Do(radix.FlatCmd(nil, "SET", key, status))
2019-03-15 18:00:35 +00:00
}
2019-03-29 18:50:11 +00:00
func SetUploadInvoice(uploadId string, invoice *ln.Invoice) error {
key := fmt.Sprintf("upload_%s_invoice", uploadId)
invoiceJson, err := json.Marshal(invoice)
if err != nil {
return err
}
return DB.Redis.Do(radix.FlatCmd(nil, "SET", key, invoiceJson))
}
2019-03-31 20:53:53 +00:00
func WatchUploadPayment(invoice *ln.Invoice) error {
log.Println("watching invoice ", invoice)
2019-03-29 18:50:11 +00:00
invoiceJson, err := json.Marshal(invoice)
if err != nil {
return err
}
2019-03-30 18:42:41 +00:00
return DB.Redis.Do(radix.FlatCmd(nil, "PUBLISH", bus.WatchInvoicesChannelName, invoiceJson))
2019-03-29 18:50:11 +00:00
//TODO: Notify client on websocket
return nil
}
2019-03-21 17:44:40 +00:00
// Returns true if id exists in DB
func IdExists(id string) (exists bool, err error) {
2019-03-26 16:03:13 +00:00
key := fmt.Sprintf("upload_status_%s", id)
2019-03-21 17:44:40 +00:00
2019-03-26 16:03:13 +00:00
err = DB.Redis.Do(radix.Cmd(&exists, "EXISTS", key))
2019-03-21 17:44:40 +00:00
return
}
// Get a file by upload id and hash
func GetByHashID(sha256 string, id string) (*Upload, error) {
var up Upload
2019-03-26 16:03:13 +00:00
2019-03-23 18:29:48 +00:00
err := DB.Sql.Get(&up, QGetByHashID, sha256, id)
2019-03-21 17:44:40 +00:00
if err == sql.ErrNoRows {
return nil, ErrDoesNotExist
}
if err != nil {
return nil, err
}
return &up, nil
}
func (u *Upload) TxSetState(tx *sqlx.Tx, status int) error {
_, err := tx.NamedExec(QSetStatus, u)
if err != nil {
return err
}
return nil
}
func (u *Upload) TxWrite(tx *sqlx.Tx) error {
_, err := tx.NamedExec(QNewUpload, u)
2019-03-23 18:29:48 +00:00
2019-03-29 18:50:11 +00:00
if pqError, ok := err.(*pq.Error); ok {
// unique constraint
if pqError.Code == "23505" {
return ErrAlreadyExists
}
2019-03-21 17:44:40 +00:00
}
if err != nil {
return err
}
return nil
}
2019-03-15 18:00:35 +00:00
func (u *Upload) Write() error {
2019-03-23 18:29:48 +00:00
_, err := DB.Sql.NamedExec(QNewUpload, u)
2019-03-29 18:50:11 +00:00
if pqError, ok := err.(*pq.Error); ok {
// unique constraint
if pqError.Code == "23505" {
return ErrAlreadyExists
}
2019-03-15 18:00:35 +00:00
}
if err != nil {
return err
}
return nil
}
func init() {
2019-03-24 21:36:28 +00:00
_, err := DB.Sql.Exec(DBUploadStatusSchema)
2019-03-15 18:00:35 +00:00
if err != nil {
log.Fatal(err)
}
2019-03-24 21:36:28 +00:00
_, err = DB.Sql.Exec(DBUploadSchema)
2019-03-21 17:44:40 +00:00
if err != nil {
log.Fatal(err)
}
2019-03-24 21:36:28 +00:00
_, err = DB.Sql.Exec(DBUploadView)
2019-03-21 17:44:40 +00:00
if err != nil {
log.Fatal(err)
}
// Populate status types
2019-03-27 15:35:36 +00:00
query := `INSERT INTO upload_status (type, status)
VALUES($1,$2) ON CONFLICT (type) DO UPDATE
SET status = $3`
2019-03-21 17:44:40 +00:00
for k, v := range UploadStatus {
2019-03-26 13:42:07 +00:00
_, err := DB.Sql.Exec(query, k, v, v)
2019-03-21 17:44:40 +00:00
if err != nil {
2019-03-29 18:50:11 +00:00
log.Panic(err)
2019-03-21 17:44:40 +00:00
}
}
2019-03-15 18:00:35 +00:00
}