140 lines
3.4 KiB
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
|
|
}
|