2022-09-24 19:33:38 +00:00
|
|
|
package services
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/google/uuid"
|
2022-09-27 23:52:13 +00:00
|
|
|
"github.com/hay-kot/homebox/backend/ent"
|
2022-09-24 19:33:38 +00:00
|
|
|
"github.com/hay-kot/homebox/backend/ent/attachment"
|
2022-09-27 23:52:13 +00:00
|
|
|
"github.com/hay-kot/homebox/backend/internal/repo"
|
2022-09-24 19:33:38 +00:00
|
|
|
"github.com/hay-kot/homebox/backend/pkgs/hasher"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
)
|
|
|
|
|
|
|
|
// TODO: this isn't a scalable solution, tokens should be stored in the database
|
|
|
|
type attachmentTokens map[string]uuid.UUID
|
|
|
|
|
|
|
|
func (at attachmentTokens) Add(token string, id uuid.UUID) {
|
|
|
|
at[token] = id
|
|
|
|
|
|
|
|
log.Debug().Str("token", token).Str("uuid", id.String()).Msg("added token")
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
ch := time.After(1 * time.Minute)
|
|
|
|
<-ch
|
|
|
|
at.Delete(token)
|
|
|
|
log.Debug().Str("token", token).Msg("deleted token")
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (at attachmentTokens) Get(token string) (uuid.UUID, bool) {
|
|
|
|
id, ok := at[token]
|
|
|
|
return id, ok
|
|
|
|
}
|
|
|
|
|
|
|
|
func (at attachmentTokens) Delete(token string) {
|
|
|
|
delete(at, token)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (svc *ItemService) AttachmentToken(ctx Context, itemId, attachmentId uuid.UUID) (string, error) {
|
2022-09-27 23:52:13 +00:00
|
|
|
_, err := svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemId)
|
2022-09-24 19:33:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
token := hasher.GenerateToken()
|
|
|
|
|
|
|
|
// Ensure that the file exists
|
|
|
|
attachment, err := svc.repo.Attachments.Get(ctx, attachmentId)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := os.Stat(attachment.Edges.Document.Path); os.IsNotExist(err) {
|
|
|
|
_ = svc.AttachmentDelete(ctx, ctx.GID, itemId, attachmentId)
|
|
|
|
return "", ErrNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
svc.at.Add(token.Raw, attachmentId)
|
|
|
|
|
|
|
|
return token.Raw, nil
|
|
|
|
}
|
|
|
|
|
2022-09-27 23:52:13 +00:00
|
|
|
func (svc *ItemService) AttachmentPath(ctx context.Context, token string) (*ent.Document, error) {
|
2022-09-24 19:33:38 +00:00
|
|
|
attachmentId, ok := svc.at.Get(token)
|
|
|
|
if !ok {
|
2022-09-27 23:52:13 +00:00
|
|
|
return nil, ErrNotFound
|
2022-09-24 19:33:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
attachment, err := svc.repo.Attachments.Get(ctx, attachmentId)
|
|
|
|
if err != nil {
|
2022-09-27 23:52:13 +00:00
|
|
|
return nil, err
|
2022-09-24 19:33:38 +00:00
|
|
|
}
|
|
|
|
|
2022-09-27 23:52:13 +00:00
|
|
|
return attachment.Edges.Document, nil
|
2022-09-24 19:33:38 +00:00
|
|
|
}
|
|
|
|
|
2022-09-27 23:52:13 +00:00
|
|
|
func (svc *ItemService) AttachmentUpdate(ctx Context, itemId uuid.UUID, data *repo.ItemAttachmentUpdate) (repo.ItemOut, error) {
|
|
|
|
// Update Attachment
|
2022-09-24 19:33:38 +00:00
|
|
|
attachment, err := svc.repo.Attachments.Update(ctx, data.ID, attachment.Type(data.Type))
|
|
|
|
if err != nil {
|
2022-09-27 23:52:13 +00:00
|
|
|
return repo.ItemOut{}, err
|
2022-09-24 19:33:38 +00:00
|
|
|
}
|
|
|
|
|
2022-09-27 23:52:13 +00:00
|
|
|
// Update Document
|
2022-09-24 19:33:38 +00:00
|
|
|
attDoc := attachment.Edges.Document
|
2022-09-27 23:52:13 +00:00
|
|
|
_, err = svc.repo.Docs.Rename(ctx, attDoc.ID, data.Title)
|
|
|
|
if err != nil {
|
|
|
|
return repo.ItemOut{}, err
|
2022-09-24 19:33:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return svc.GetOne(ctx, ctx.GID, itemId)
|
|
|
|
}
|
|
|
|
|
|
|
|
// AttachmentAdd adds an attachment to an item by creating an entry in the Documents table and linking it to the Attachment
|
|
|
|
// Table and Items table. The file provided via the reader is stored on the file system based on the provided
|
|
|
|
// relative path during construction of the service.
|
2022-09-27 23:52:13 +00:00
|
|
|
func (svc *ItemService) AttachmentAdd(ctx Context, itemId uuid.UUID, filename string, attachmentType attachment.Type, file io.Reader) (repo.ItemOut, error) {
|
2022-09-24 19:33:38 +00:00
|
|
|
// Get the Item
|
2022-09-27 23:52:13 +00:00
|
|
|
_, err := svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemId)
|
2022-09-24 19:33:38 +00:00
|
|
|
if err != nil {
|
2022-09-27 23:52:13 +00:00
|
|
|
return repo.ItemOut{}, err
|
2022-09-24 19:33:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create the document
|
2022-09-27 23:52:13 +00:00
|
|
|
doc, err := svc.repo.Docs.Create(ctx, ctx.GID, repo.DocumentCreate{Title: filename, Content: file})
|
2022-09-24 19:33:38 +00:00
|
|
|
if err != nil {
|
2022-09-27 23:52:13 +00:00
|
|
|
log.Err(err).Msg("failed to create document")
|
|
|
|
return repo.ItemOut{}, err
|
2022-09-24 19:33:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create the attachment
|
|
|
|
_, err = svc.repo.Attachments.Create(ctx, itemId, doc.ID, attachmentType)
|
|
|
|
if err != nil {
|
2022-09-27 23:52:13 +00:00
|
|
|
log.Err(err).Msg("failed to create attachment")
|
|
|
|
return repo.ItemOut{}, err
|
2022-09-24 19:33:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return svc.GetOne(ctx, ctx.GID, itemId)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (svc *ItemService) AttachmentDelete(ctx context.Context, gid, itemId, attachmentId uuid.UUID) error {
|
|
|
|
// Get the Item
|
2022-09-27 23:52:13 +00:00
|
|
|
_, err := svc.repo.Items.GetOneByGroup(ctx, gid, itemId)
|
2022-09-24 19:33:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
attachment, err := svc.repo.Attachments.Get(ctx, attachmentId)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete the attachment
|
|
|
|
err = svc.repo.Attachments.Delete(ctx, attachmentId)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove File
|
|
|
|
err = os.Remove(attachment.Edges.Document.Path)
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|