You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
memos/store/attachment.go

167 lines
4.2 KiB
Go

package store
import (
"context"
"log/slog"
"os"
"path/filepath"
"github.com/pkg/errors"
"github.com/usememos/memos/internal/base"
"github.com/usememos/memos/plugin/storage/s3"
storepb "github.com/usememos/memos/proto/gen/store"
)
type Attachment struct {
// ID is the system generated unique identifier for the attachment.
ID int32
// UID is the user defined unique identifier for the attachment.
UID string
// Standard fields
CreatorID int32
CreatedTs int64
UpdatedTs int64
// Domain specific fields
Filename string
Blob []byte
Type string
Size int64
StorageType storepb.AttachmentStorageType
Reference string
Payload *storepb.AttachmentPayload
// The related memo ID.
MemoID *int32
}
type FindAttachment struct {
GetBlob bool
ID *int32
UID *string
CreatorID *int32
Filename *string
FilenameSearch *string
MemoID *int32
HasRelatedMemo bool
StorageType *storepb.AttachmentStorageType
Limit *int
Offset *int
}
type UpdateAttachment struct {
ID int32
UID *string
UpdatedTs *int64
Filename *string
MemoID *int32
Reference *string
Payload *storepb.AttachmentPayload
}
type DeleteAttachment struct {
ID int32
MemoID *int32
}
func (s *Store) CreateAttachment(ctx context.Context, create *Attachment) (*Attachment, error) {
if !base.UIDMatcher.MatchString(create.UID) {
return nil, errors.New("invalid uid")
}
return s.driver.CreateAttachment(ctx, create)
}
func (s *Store) ListAttachments(ctx context.Context, find *FindAttachment) ([]*Attachment, error) {
// Set default limits to prevent loading too many attachments at once
if find.Limit == nil && find.GetBlob {
// When fetching blobs, we should be especially careful with limits
defaultLimit := 10
find.Limit = &defaultLimit
} else if find.Limit == nil {
// Even without blobs, let's default to a reasonable limit
defaultLimit := 100
find.Limit = &defaultLimit
}
return s.driver.ListAttachments(ctx, find)
}
func (s *Store) GetAttachment(ctx context.Context, find *FindAttachment) (*Attachment, error) {
attachments, err := s.ListAttachments(ctx, find)
if err != nil {
return nil, err
}
if len(attachments) == 0 {
return nil, nil
}
return attachments[0], nil
}
func (s *Store) UpdateAttachment(ctx context.Context, update *UpdateAttachment) error {
if update.UID != nil && !base.UIDMatcher.MatchString(*update.UID) {
return errors.New("invalid uid")
}
return s.driver.UpdateAttachment(ctx, update)
}
func (s *Store) DeleteAttachment(ctx context.Context, delete *DeleteAttachment) error {
attachment, err := s.GetAttachment(ctx, &FindAttachment{ID: &delete.ID})
if err != nil {
return errors.Wrap(err, "failed to get attachment")
}
if attachment == nil {
return errors.New("attachment not found")
}
if attachment.StorageType == storepb.AttachmentStorageType_LOCAL {
if err := func() error {
p := filepath.FromSlash(attachment.Reference)
if !filepath.IsAbs(p) {
p = filepath.Join(s.profile.Data, p)
}
err := os.Remove(p)
if err != nil {
return errors.Wrap(err, "failed to delete local file")
}
return nil
}(); err != nil {
return errors.Wrap(err, "failed to delete local file")
}
} else if attachment.StorageType == storepb.AttachmentStorageType_S3 {
if err := func() error {
s3ObjectPayload := attachment.Payload.GetS3Object()
if s3ObjectPayload == nil {
return errors.Errorf("No s3 object found")
}
workspaceStorageSetting, err := s.GetWorkspaceStorageSetting(ctx)
if err != nil {
return errors.Wrap(err, "failed to get workspace storage setting")
}
s3Config := s3ObjectPayload.S3Config
if s3Config == nil {
if workspaceStorageSetting.S3Config == nil {
return errors.Errorf("S3 config is not found")
}
s3Config = workspaceStorageSetting.S3Config
}
s3Client, err := s3.NewClient(ctx, s3Config)
if err != nil {
return errors.Wrap(err, "Failed to create s3 client")
}
if err := s3Client.DeleteObject(ctx, s3ObjectPayload.Key); err != nil {
return errors.Wrap(err, "Failed to delete s3 object")
}
return nil
}(); err != nil {
slog.Warn("Failed to delete s3 object", slog.Any("err", err))
}
}
return s.driver.DeleteAttachment(ctx, delete)
}