diff --git a/apis/collection.go b/apis/collection.go index af7b1dd4..68d8bcc2 100644 --- a/apis/collection.go +++ b/apis/collection.go @@ -1,8 +1,6 @@ package apis import ( - "errors" - "log" "net/http" "github.com/labstack/echo/v5" @@ -160,13 +158,6 @@ func (api *collectionApi) delete(c echo.Context) error { return rest.NewBadRequestError("Failed to delete collection. Make sure that the collection is not referenced by other collections.", err) } - // try to delete the collection files - if err := api.deleteCollectionFiles(e.Collection); err != nil && api.app.IsDebug() { - // non critical error - only log for debug - // (usually could happen because of S3 api limits) - log.Println(err) - } - return e.HttpContext.NoContent(http.StatusNoContent) }) @@ -176,18 +167,3 @@ func (api *collectionApi) delete(c echo.Context) error { return handlerErr } - -func (api *collectionApi) deleteCollectionFiles(collection *models.Collection) error { - fs, err := api.app.NewFilesystem() - if err != nil { - return err - } - defer fs.Close() - - failed := fs.DeletePrefix(collection.BaseFilesPath()) - if len(failed) > 0 { - return errors.New("Failed to delete all record files.") - } - - return nil -} diff --git a/apis/record.go b/apis/record.go index b69c301d..141eb779 100644 --- a/apis/record.go +++ b/apis/record.go @@ -356,13 +356,6 @@ func (api *recordApi) delete(c echo.Context) error { return rest.NewBadRequestError("Failed to delete record. Make sure that the record is not part of a required relation reference.", err) } - // try to delete the record files - if err := api.deleteRecordFiles(e.Record); err != nil && api.app.IsDebug() { - // non critical error - only log for debug - // (usually could happen due to S3 api limits) - log.Println(err) - } - return e.HttpContext.NoContent(http.StatusNoContent) }) @@ -373,21 +366,6 @@ func (api *recordApi) delete(c echo.Context) error { return handlerErr } -func (api *recordApi) deleteRecordFiles(record *models.Record) error { - fs, err := api.app.NewFilesystem() - if err != nil { - return err - } - defer fs.Close() - - failed := fs.DeletePrefix(record.BaseFilesPath()) - if len(failed) > 0 { - return fmt.Errorf("Failed to delete %d record files.", len(failed)) - } - - return nil -} - func (api *recordApi) exportRequestData(c echo.Context) map[string]any { result := map[string]any{} queryParams := map[string]any{} diff --git a/core/base.go b/core/base.go index 3fef5588..43539fdb 100644 --- a/core/base.go +++ b/core/base.go @@ -5,6 +5,7 @@ import ( "database/sql" "encoding/json" "errors" + "log" "os" "path/filepath" "time" @@ -123,7 +124,7 @@ type BaseApp struct { // // To initialize the app, you need to call `app.Bootsrap()`. func NewBaseApp(dataDir string, encryptionEnv string, isDebug bool) *BaseApp { - return &BaseApp{ + app := &BaseApp{ dataDir: dataDir, isDebug: isDebug, encryptionEnv: encryptionEnv, @@ -209,6 +210,10 @@ func NewBaseApp(dataDir string, encryptionEnv string, isDebug bool) *BaseApp { onCollectionBeforeDeleteRequest: &hook.Hook[*CollectionDeleteEvent]{}, onCollectionAfterDeleteRequest: &hook.Hook[*CollectionDeleteEvent]{}, } + + app.registerDefaultHooks() + + return app } // Bootstrap initializes the application @@ -750,3 +755,33 @@ func (app *BaseApp) createDao(db dbx.Builder) *daos.Dao { return dao } + +func (app *BaseApp) registerDefaultHooks() { + deletePrefix := func(prefix string) error { + fs, err := app.NewFilesystem() + if err != nil { + return err + } + defer fs.Close() + + failed := fs.DeletePrefix(prefix) + if len(failed) > 0 { + return errors.New("Failed to delete the files at " + prefix) + } + + return nil + } + + // delete storage files from deleted Collection, Records, etc. + app.OnModelAfterDelete().Add(func(e *ModelEvent) error { + if m, ok := e.Model.(models.FilesManager); ok && m.BaseFilesPath() != "" { + if err := deletePrefix(m.BaseFilesPath()); err != nil && app.IsDebug() { + // non critical error - only log for debug + // (usually could happen because of S3 api limits) + log.Println(err) + } + } + + return nil + }) +} diff --git a/models/base.go b/models/base.go index e85a55dd..1c9871fa 100644 --- a/models/base.go +++ b/models/base.go @@ -15,6 +15,12 @@ type ColumnValueMapper interface { ColumnValueMap() map[string]any } +// FilesManager defines an interface with common methods that files manager models should implement. +type FilesManager interface { + // BaseFilesPath returns the storage dir path used by the interface instance. + BaseFilesPath() string +} + // Model defines an interface with common methods that all db models should have. type Model interface { TableName() string diff --git a/models/collection.go b/models/collection.go index 8a008567..b90cb4b6 100644 --- a/models/collection.go +++ b/models/collection.go @@ -3,6 +3,7 @@ package models import "github.com/pocketbase/pocketbase/models/schema" var _ Model = (*Collection)(nil) +var _ FilesManager = (*Collection)(nil) type Collection struct { BaseModel diff --git a/models/record.go b/models/record.go index e2698849..787fde9e 100644 --- a/models/record.go +++ b/models/record.go @@ -16,6 +16,7 @@ import ( var _ Model = (*Record)(nil) var _ ColumnValueMapper = (*Record)(nil) +var _ FilesManager = (*Record)(nil) type Record struct { BaseModel