df/bff/db/sqlc/tx_create_document.go

140 lines
3.4 KiB
Go

package db
import (
"context"
"crypto/sha256"
"database/sql"
"encoding/hex"
"errors"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"path"
"path/filepath"
)
type CreateDocumentTxParams struct {
AccountID uint64 `json:"account_id"`
PersonID uint64 `json:"person_id"`
MailID uint64 `json:"mail_id"`
File *multipart.FileHeader `json:"file"`
Creator string `json:"creator"`
}
type CreateDocumentTxResult struct {
Document Document `json:"document"`
}
func (store *SQLStore) CreateDocumentTx(ctx context.Context, arg CreateDocumentTxParams) (doc Document, code int, err error) {
var result CreateDocumentTxResult
if arg.MailID > 0 && arg.PersonID > 0 {
return Document{}, http.StatusBadRequest, errors.New("document can't be assigned to both person_id AND mail_id")
}
if arg.MailID < 1 && arg.PersonID < 1 {
return Document{}, http.StatusBadRequest, errors.New("document has to be assigned to either a person_id or a mail_id")
}
req := CreateDocumentParams{
Creator: arg.Creator,
}
targetDir := filepath.Join("./files", fmt.Sprintf("%d", arg.AccountID))
fileData, err := arg.File.Open()
if err != nil {
return Document{}, http.StatusBadRequest, errors.New("failed to read file")
}
h := sha256.New()
_, err = io.Copy(h, fileData)
if err != nil {
return Document{}, http.StatusInternalServerError, errors.New("could not create file hash")
}
fileData.Seek(0, io.SeekStart)
req.Hash = hex.EncodeToString(h.Sum(nil))
if arg.MailID > 0 {
_, err := store.GetMail(ctx, arg.MailID)
if err != nil {
return Document{}, http.StatusNotFound, errors.New("mail not found")
}
targetDir = filepath.Join(targetDir, "mail", fmt.Sprintf("%d", arg.MailID))
req.MailID = sql.NullInt64{
Valid: true,
Int64: int64(arg.MailID),
}
}
if arg.PersonID > 0 {
_, err := store.GetPerson(ctx, arg.PersonID)
if err != nil {
return Document{}, http.StatusNotFound, errors.New("person not found")
}
docs, err := store.GetDocumentByHash(ctx, GetDocumentByHashParams{
AccountID: arg.AccountID,
Hash: req.Hash,
})
if err != nil {
return Document{}, http.StatusInternalServerError, fmt.Errorf("could not check file hash in db: %v", err.Error())
}
if len(docs) > 0 {
return Document{
ID: docs[0],
}, http.StatusConflict, errors.New("file already exists in database")
}
targetDir = filepath.Join(targetDir, "person", fmt.Sprintf("%d", arg.PersonID))
req.PersonID = sql.NullInt64{
Valid: true,
Int64: int64(arg.PersonID),
}
}
req.Type = path.Ext(arg.File.Filename)
req.Name = arg.File.Filename
p := filepath.Join(targetDir, req.Hash+path.Ext(arg.File.Filename))
req.Path = p
if _, err := os.Stat(p); err == nil {
return Document{}, http.StatusConflict, errors.New("file already exists")
}
err = store.execTx(ctx, func(q *Queries) error {
var err error
if _, err := os.Stat(targetDir); err != nil {
err = os.MkdirAll(targetDir, 0755)
if err != nil {
return errors.New("could not create directory structure")
}
}
f, err := os.Create(p)
if err != nil {
return fmt.Errorf("could not create file: %v", err)
}
_, err = io.Copy(f, fileData)
if err != nil {
return errors.New("could not write file")
}
result.Document, err = q.CreateDocument(ctx, req)
if err != nil {
return err
}
return err
})
return result.Document, http.StatusInternalServerError, err
}