1
0
mirror of https://github.com/pocketbase/pocketbase.git synced 2024-12-12 05:24:27 +02:00
pocketbase/apis/file.go

122 lines
3.6 KiB
Go
Raw Normal View History

2022-07-06 23:19:05 +02:00
package apis
import (
2023-02-18 19:33:42 +02:00
"fmt"
2022-07-06 23:19:05 +02:00
"github.com/labstack/echo/v5"
"github.com/pocketbase/pocketbase/core"
"github.com/pocketbase/pocketbase/models"
"github.com/pocketbase/pocketbase/models/schema"
"github.com/pocketbase/pocketbase/tools/list"
)
2022-10-30 10:28:14 +02:00
var imageContentTypes = []string{"image/png", "image/jpg", "image/jpeg", "image/gif"}
2022-07-06 23:19:05 +02:00
var defaultThumbSizes = []string{"100x100"}
2022-10-30 10:28:14 +02:00
// bindFileApi registers the file api endpoints and the corresponding handlers.
func bindFileApi(app core.App, rg *echo.Group) {
2022-07-06 23:19:05 +02:00
api := fileApi{app: app}
subGroup := rg.Group("/files", ActivityLogger(app))
subGroup.HEAD("/:collection/:recordId/:filename", api.download, LoadCollectionContext(api.app))
2022-07-06 23:19:05 +02:00
subGroup.GET("/:collection/:recordId/:filename", api.download, LoadCollectionContext(api.app))
}
type fileApi struct {
app core.App
}
func (api *fileApi) download(c echo.Context) error {
collection, _ := c.Get(ContextCollectionKey).(*models.Collection)
if collection == nil {
2022-10-30 10:28:14 +02:00
return NewNotFoundError("", nil)
2022-07-06 23:19:05 +02:00
}
recordId := c.PathParam("recordId")
if recordId == "" {
2022-10-30 10:28:14 +02:00
return NewNotFoundError("", nil)
2022-07-06 23:19:05 +02:00
}
2022-10-30 10:28:14 +02:00
record, err := api.app.Dao().FindRecordById(collection.Id, recordId)
2022-07-06 23:19:05 +02:00
if err != nil {
2022-10-30 10:28:14 +02:00
return NewNotFoundError("", err)
2022-07-06 23:19:05 +02:00
}
filename := c.PathParam("filename")
fileField := record.FindFileFieldByFile(filename)
if fileField == nil {
2022-10-30 10:28:14 +02:00
return NewNotFoundError("", nil)
2022-07-06 23:19:05 +02:00
}
2023-02-18 19:33:42 +02:00
2022-07-06 23:19:05 +02:00
options, _ := fileField.Options.(*schema.FileOptions)
2023-02-18 19:33:42 +02:00
baseFilesPath := record.BaseFilesPath()
// fetch the original view file field related record
if collection.IsView() {
fileRecord, err := api.app.Dao().FindRecordByViewFile(collection.Id, fileField.Name, filename)
if err != nil {
return NewNotFoundError("", fmt.Errorf("Failed to fetch view file field record: %w", err))
}
baseFilesPath = fileRecord.BaseFilesPath()
}
2022-07-06 23:19:05 +02:00
fs, err := api.app.NewFilesystem()
if err != nil {
2022-10-30 10:28:14 +02:00
return NewBadRequestError("Filesystem initialization failure.", err)
2022-07-06 23:19:05 +02:00
}
defer fs.Close()
2023-02-18 19:33:42 +02:00
originalPath := baseFilesPath + "/" + filename
2022-07-06 23:19:05 +02:00
servedPath := originalPath
servedName := filename
// check for valid thumb size param
thumbSize := c.QueryParam("thumb")
if thumbSize != "" && (list.ExistInSlice(thumbSize, defaultThumbSizes) || list.ExistInSlice(thumbSize, options.Thumbs)) {
// extract the original file meta attributes and check it existence
oAttrs, oAttrsErr := fs.Attributes(originalPath)
if oAttrsErr != nil {
2022-10-30 10:28:14 +02:00
return NewNotFoundError("", err)
2022-07-06 23:19:05 +02:00
}
// check if it is an image
if list.ExistInSlice(oAttrs.ContentType, imageContentTypes) {
// add thumb size as file suffix
servedName = thumbSize + "_" + filename
2023-02-18 19:33:42 +02:00
servedPath = baseFilesPath + "/thumbs_" + filename + "/" + servedName
2022-07-06 23:19:05 +02:00
// create a new thumb if it doesn exists
if exists, _ := fs.Exists(servedPath); !exists {
2022-08-17 21:29:11 +02:00
if err := fs.CreateThumb(originalPath, servedPath, thumbSize); err != nil {
2022-07-06 23:19:05 +02:00
servedPath = originalPath // fallback to the original
}
}
}
}
event := new(core.FileDownloadEvent)
event.HttpContext = c
event.Collection = collection
event.Record = record
event.FileField = fileField
event.ServedPath = servedPath
event.ServedName = servedName
2022-07-06 23:19:05 +02:00
// clickjacking shouldn't be a concern when serving uploaded files,
// so it safe to unset the global X-Frame-Options to allow files embedding
// (note: it is out of the hook to allow users to customize the behavior)
c.Response().Header().Del("X-Frame-Options")
2022-07-06 23:19:05 +02:00
return api.app.OnFileDownloadRequest().Trigger(event, func(e *core.FileDownloadEvent) error {
res := e.HttpContext.Response()
req := e.HttpContext.Request()
if err := fs.Serve(res, req, e.ServedPath, e.ServedName); err != nil {
2022-10-30 10:28:14 +02:00
return NewNotFoundError("", err)
2022-07-06 23:19:05 +02:00
}
return nil
})
}