|
|
|
@ -1,58 +1,15 @@
|
|
|
|
|
package storage
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"database/sql"
|
|
|
|
|
"errors"
|
|
|
|
|
"log"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
|
|
"git.sp4ke.com/sp4ke/bit4sat-server.git/db"
|
|
|
|
|
"github.com/jmoiron/sqlx"
|
|
|
|
|
"github.com/mattn/go-sqlite3"
|
|
|
|
|
"git.sp4ke.com/sp4ke/bit4sat/db"
|
|
|
|
|
"github.com/mediocregopher/radix/v3"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var DB = db.DB
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
DBUploadSchema = `
|
|
|
|
|
CREATE TABLE IF NOT EXISTS "upload" (
|
|
|
|
|
upload_id TEXT NOT NULL,
|
|
|
|
|
sha256 TEXT NOT NULL,
|
|
|
|
|
file_name TEXT NOT NULL,
|
|
|
|
|
file_type TEXT DEFAULT '',
|
|
|
|
|
file_size INTEGER NOT NULL,
|
|
|
|
|
file_ext TEXT DEFAULT '',
|
|
|
|
|
status INTEGER DEFAULT 0,
|
|
|
|
|
FOREIGN KEY (status) REFERENCES upload_status(type),
|
|
|
|
|
UNIQUE (upload_id, sha256)
|
|
|
|
|
);
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
DBUploadView = `
|
|
|
|
|
CREATE VIEW IF NOT EXISTS "upload_with_status" AS SELECT * FROM upload JOIN upload_status ON
|
|
|
|
|
upload.status = upload_status.type
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
DBUploadStatusSchema = `CREATE TABLE IF NOT EXISTS upload_status
|
|
|
|
|
(
|
|
|
|
|
type INTEGER PRIMARY KEY,
|
|
|
|
|
status TEXT NOT NULL UNIQUE
|
|
|
|
|
)
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
QNewUpload = `INSERT INTO upload
|
|
|
|
|
(upload_id, sha256, file_name, file_type, file_size, file_ext, status)
|
|
|
|
|
VALUES
|
|
|
|
|
(:upload_id, :sha256, :file_name, :file_type, :file_size, :file_ext, :status)`
|
|
|
|
|
|
|
|
|
|
QSetStatus = `UPDATE upload SET status = :status WHERE upload_id = :upload_id `
|
|
|
|
|
|
|
|
|
|
QGetByHashID = `SELECT * FROM upload
|
|
|
|
|
WHERE
|
|
|
|
|
sha256 = ?
|
|
|
|
|
AND
|
|
|
|
|
upload_id = ?`
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
UpNew = iota
|
|
|
|
|
UpStored
|
|
|
|
@ -61,127 +18,71 @@ const (
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var UploadStatus = map[int]string{
|
|
|
|
|
UpNew: "new upload",
|
|
|
|
|
UpNew: "new",
|
|
|
|
|
UpStored: "stored",
|
|
|
|
|
UpWaitingPayment: "waiting payment",
|
|
|
|
|
UpWaitingPayment: "waiting-payment",
|
|
|
|
|
UpReady: "ready",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
ErrDoesNotExist = errors.New("does not exist")
|
|
|
|
|
ErrAlreadyExists = errors.New("already exists")
|
|
|
|
|
)
|
|
|
|
|
type SHA256 string
|
|
|
|
|
|
|
|
|
|
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"`
|
|
|
|
|
func (sha SHA256) MarshalText() ([]byte, error) {
|
|
|
|
|
return []byte(sha), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns true if id exists in DB
|
|
|
|
|
func IdExists(id string) (exists bool, err error) {
|
|
|
|
|
qUploadExists := `
|
|
|
|
|
SELECT EXISTS (SELECT upload_id FROM upload where upload_id = ?)
|
|
|
|
|
`
|
|
|
|
|
err = DB.Handle.Get(&exists, qUploadExists, id)
|
|
|
|
|
|
|
|
|
|
// No result found is also no result
|
|
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
|
err = nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
type Upload struct {
|
|
|
|
|
ID string
|
|
|
|
|
Status uint8
|
|
|
|
|
Files map[SHA256]*UpFile
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get a file by upload id and hash
|
|
|
|
|
func GetByHashID(sha256 string, id string) (*Upload, error) {
|
|
|
|
|
var up Upload
|
|
|
|
|
err := DB.Handle.Get(&up, QGetByHashID, sha256, id)
|
|
|
|
|
|
|
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
|
return nil, ErrDoesNotExist
|
|
|
|
|
type UpFile struct {
|
|
|
|
|
FileName string
|
|
|
|
|
FileType string
|
|
|
|
|
FileSize int64
|
|
|
|
|
FileExt string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
func NewUpload() *Upload {
|
|
|
|
|
files := make(map[SHA256]*UpFile)
|
|
|
|
|
return &Upload{
|
|
|
|
|
Files: files,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &up, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (u *Upload) TxSetState(tx *sqlx.Tx, status int) error {
|
|
|
|
|
_, err := tx.NamedExec(QSetStatus, u)
|
|
|
|
|
// Returns true if id exists in DB
|
|
|
|
|
func IdExists(id string) (exists bool, err error) {
|
|
|
|
|
key := fmt.Sprintf("upload:%s", id)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
err = DB.Do(radix.Cmd(&exists, "EXISTS", key))
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (u *Upload) TxWrite(tx *sqlx.Tx) error {
|
|
|
|
|
|
|
|
|
|
_, err := tx.NamedExec(QNewUpload, u)
|
|
|
|
|
sqlErr, isSqlErr := err.(sqlite3.Error)
|
|
|
|
|
if isSqlErr && sqlErr.Code == sqlite3.ErrConstraint {
|
|
|
|
|
return ErrAlreadyExists
|
|
|
|
|
}
|
|
|
|
|
// Get a file by upload id and hash
|
|
|
|
|
func GetByID(id string) (*Upload, error) {
|
|
|
|
|
var buf []byte
|
|
|
|
|
key := fmt.Sprintf("upload:%s", id)
|
|
|
|
|
|
|
|
|
|
err := DB.Do(radix.Cmd(&buf, "GET", key))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
var up Upload
|
|
|
|
|
err = json.Unmarshal(buf, &up)
|
|
|
|
|
|
|
|
|
|
return &up, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (u *Upload) Write() error {
|
|
|
|
|
_, err := DB.Handle.NamedExec(QNewUpload, u)
|
|
|
|
|
sqlErr, isSqlErr := err.(sqlite3.Error)
|
|
|
|
|
if isSqlErr && sqlErr.Code == sqlite3.ErrConstraint {
|
|
|
|
|
return ErrAlreadyExists
|
|
|
|
|
}
|
|
|
|
|
// Create a new upload hash upload:id --> [Files]
|
|
|
|
|
key := fmt.Sprintf("upload:%s", u.ID)
|
|
|
|
|
|
|
|
|
|
enc, err := json.Marshal(u)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
_, err := DB.Handle.Exec(DBUploadSchema)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = DB.Handle.Exec(DBUploadView)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = DB.Handle.Exec(DBUploadStatusSchema)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Populate status types
|
|
|
|
|
query := `INSERT INTO upload_status (type, status) VALUES(?,?)`
|
|
|
|
|
for k, v := range UploadStatus {
|
|
|
|
|
_, err := DB.Handle.Exec(query, k, v)
|
|
|
|
|
if err != nil {
|
|
|
|
|
sqlErr, ok := err.(sqlite3.Error)
|
|
|
|
|
if ok && sqlErr.ExtendedCode == sqlite3.ErrConstraintUnique {
|
|
|
|
|
log.Panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
|
log.Panic(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return DB.Do(radix.FlatCmd(nil, "SET", key, enc))
|
|
|
|
|
}
|
|
|
|
|