init commit
This commit is contained in:
166
store/attachment.go
Normal file
166
store/attachment.go
Normal file
@@ -0,0 +1,166 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user