s3_object_presigner.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. package s3objectpresigner
  2. import (
  3. "context"
  4. "log/slog"
  5. "time"
  6. "google.golang.org/protobuf/types/known/timestamppb"
  7. "github.com/usememos/memos/plugin/storage/s3"
  8. storepb "github.com/usememos/memos/proto/gen/store"
  9. "github.com/usememos/memos/store"
  10. )
  11. // nolint
  12. type S3ObjectPresigner struct {
  13. Store *store.Store
  14. }
  15. func NewS3ObjectPresigner(store *store.Store) *S3ObjectPresigner {
  16. return &S3ObjectPresigner{
  17. Store: store,
  18. }
  19. }
  20. func (p *S3ObjectPresigner) CheckAndPresign(ctx context.Context) {
  21. workspaceStorageSetting, err := p.Store.GetWorkspaceStorageSetting(ctx)
  22. if err != nil {
  23. return
  24. }
  25. s3StorageType := storepb.ResourceStorageType_S3
  26. resources, err := p.Store.ListResources(ctx, &store.FindResource{
  27. GetBlob: false,
  28. StorageType: &s3StorageType,
  29. })
  30. if err != nil {
  31. return
  32. }
  33. for _, resource := range resources {
  34. s3ObjectPayload := resource.Payload.GetS3Object()
  35. if s3ObjectPayload == nil {
  36. continue
  37. }
  38. if s3ObjectPayload.LastPresignedTime != nil {
  39. // Skip if the presigned URL is still valid for the next 4 days.
  40. // The expiration time is set to 5 days.
  41. if time.Now().Before(s3ObjectPayload.LastPresignedTime.AsTime().Add(4 * 24 * time.Hour)) {
  42. continue
  43. }
  44. }
  45. s3Config := workspaceStorageSetting.GetS3Config()
  46. if s3ObjectPayload.S3Config != nil {
  47. s3Config = s3ObjectPayload.S3Config
  48. }
  49. if s3Config == nil {
  50. slog.Error("S3 config is not found")
  51. continue
  52. }
  53. s3Client, err := s3.NewClient(ctx, s3Config)
  54. if err != nil {
  55. slog.Error("Failed to create S3 client", slog.Any("err", err))
  56. continue
  57. }
  58. presignURL, err := s3Client.PresignGetObject(ctx, s3ObjectPayload.Key)
  59. if err != nil {
  60. return
  61. }
  62. s3ObjectPayload.S3Config = s3Config
  63. s3ObjectPayload.LastPresignedTime = timestamppb.New(time.Now())
  64. if err := p.Store.UpdateResource(ctx, &store.UpdateResource{
  65. ID: resource.ID,
  66. Reference: &presignURL,
  67. Payload: &storepb.ResourcePayload{
  68. Payload: &storepb.ResourcePayload_S3Object_{
  69. S3Object: s3ObjectPayload,
  70. },
  71. },
  72. }); err != nil {
  73. return
  74. }
  75. }
  76. }
  77. func (p *S3ObjectPresigner) Start(ctx context.Context) {
  78. p.CheckAndPresign(ctx)
  79. // Schedule runner every 24 hours.
  80. ticker := time.NewTicker(24 * time.Hour)
  81. defer ticker.Stop()
  82. for {
  83. select {
  84. case <-ctx.Done():
  85. return
  86. case <-ticker.C:
  87. }
  88. p.CheckAndPresign(ctx)
  89. }
  90. }