You've already forked pocketbase
							
							
				mirror of
				https://github.com/pocketbase/pocketbase.git
				synced 2025-10-31 08:37:38 +02:00 
			
		
		
		
	[#151] remove files on cascade deletion
This commit is contained in:
		| @@ -1,8 +1,6 @@ | |||||||
| package apis | package apis | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"errors" |  | ||||||
| 	"log" |  | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  |  | ||||||
| 	"github.com/labstack/echo/v5" | 	"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) | 			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) | 		return e.HttpContext.NoContent(http.StatusNoContent) | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
| @@ -176,18 +167,3 @@ func (api *collectionApi) delete(c echo.Context) error { | |||||||
|  |  | ||||||
| 	return handlerErr | 	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 |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -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) | 			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) | 		return e.HttpContext.NoContent(http.StatusNoContent) | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
| @@ -373,21 +366,6 @@ func (api *recordApi) delete(c echo.Context) error { | |||||||
| 	return handlerErr | 	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 { | func (api *recordApi) exportRequestData(c echo.Context) map[string]any { | ||||||
| 	result := map[string]any{} | 	result := map[string]any{} | ||||||
| 	queryParams := map[string]any{} | 	queryParams := map[string]any{} | ||||||
|   | |||||||
							
								
								
									
										37
									
								
								core/base.go
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								core/base.go
									
									
									
									
									
								
							| @@ -5,6 +5,7 @@ import ( | |||||||
| 	"database/sql" | 	"database/sql" | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 	"log" | ||||||
| 	"os" | 	"os" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"time" | 	"time" | ||||||
| @@ -123,7 +124,7 @@ type BaseApp struct { | |||||||
| // | // | ||||||
| // To initialize the app, you need to call `app.Bootsrap()`. | // To initialize the app, you need to call `app.Bootsrap()`. | ||||||
| func NewBaseApp(dataDir string, encryptionEnv string, isDebug bool) *BaseApp { | func NewBaseApp(dataDir string, encryptionEnv string, isDebug bool) *BaseApp { | ||||||
| 	return &BaseApp{ | 	app := &BaseApp{ | ||||||
| 		dataDir:             dataDir, | 		dataDir:             dataDir, | ||||||
| 		isDebug:             isDebug, | 		isDebug:             isDebug, | ||||||
| 		encryptionEnv:       encryptionEnv, | 		encryptionEnv:       encryptionEnv, | ||||||
| @@ -209,6 +210,10 @@ func NewBaseApp(dataDir string, encryptionEnv string, isDebug bool) *BaseApp { | |||||||
| 		onCollectionBeforeDeleteRequest: &hook.Hook[*CollectionDeleteEvent]{}, | 		onCollectionBeforeDeleteRequest: &hook.Hook[*CollectionDeleteEvent]{}, | ||||||
| 		onCollectionAfterDeleteRequest:  &hook.Hook[*CollectionDeleteEvent]{}, | 		onCollectionAfterDeleteRequest:  &hook.Hook[*CollectionDeleteEvent]{}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	app.registerDefaultHooks() | ||||||
|  |  | ||||||
|  | 	return app | ||||||
| } | } | ||||||
|  |  | ||||||
| // Bootstrap initializes the application | // Bootstrap initializes the application | ||||||
| @@ -750,3 +755,33 @@ func (app *BaseApp) createDao(db dbx.Builder) *daos.Dao { | |||||||
|  |  | ||||||
| 	return 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 | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -15,6 +15,12 @@ type ColumnValueMapper interface { | |||||||
| 	ColumnValueMap() map[string]any | 	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. | // Model defines an interface with common methods that all db models should have. | ||||||
| type Model interface { | type Model interface { | ||||||
| 	TableName() string | 	TableName() string | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ package models | |||||||
| import "github.com/pocketbase/pocketbase/models/schema" | import "github.com/pocketbase/pocketbase/models/schema" | ||||||
|  |  | ||||||
| var _ Model = (*Collection)(nil) | var _ Model = (*Collection)(nil) | ||||||
|  | var _ FilesManager = (*Collection)(nil) | ||||||
|  |  | ||||||
| type Collection struct { | type Collection struct { | ||||||
| 	BaseModel | 	BaseModel | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ import ( | |||||||
|  |  | ||||||
| var _ Model = (*Record)(nil) | var _ Model = (*Record)(nil) | ||||||
| var _ ColumnValueMapper = (*Record)(nil) | var _ ColumnValueMapper = (*Record)(nil) | ||||||
|  | var _ FilesManager = (*Record)(nil) | ||||||
|  |  | ||||||
| type Record struct { | type Record struct { | ||||||
| 	BaseModel | 	BaseModel | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user