2024-08-20 09:08:40 +00:00
package api
import (
2024-08-20 14:55:58 +00:00
"encoding/json"
2024-08-20 09:08:40 +00:00
"fmt"
"git.solsynth.dev/hydrogen/paperclip/pkg/internal/database"
"git.solsynth.dev/hydrogen/paperclip/pkg/internal/gap"
"git.solsynth.dev/hydrogen/paperclip/pkg/internal/models"
2024-08-20 14:55:58 +00:00
"git.solsynth.dev/hydrogen/paperclip/pkg/internal/server/exts"
2024-08-20 09:08:40 +00:00
"git.solsynth.dev/hydrogen/paperclip/pkg/internal/services"
"github.com/gofiber/fiber/v2"
"github.com/spf13/viper"
)
func createAttachmentMultipartPlaceholder ( c * fiber . Ctx ) error {
if err := gap . H . EnsureAuthenticated ( c ) ; err != nil {
return err
}
user := c . Locals ( "user" ) . ( models . Account )
var data struct {
Pool string ` json:"pool" validate:"required" `
Size int64 ` json:"size" validate:"required" `
2024-08-20 14:55:58 +00:00
FileName string ` json:"name" validate:"required" `
2024-08-20 09:08:40 +00:00
Alternative string ` json:"alt" `
MimeType string ` json:"mimetype" `
Metadata map [ string ] any ` json:"metadata" `
IsMature bool ` json:"is_mature" `
}
2024-08-20 14:55:58 +00:00
if err := exts . BindAndValidate ( c , & data ) ; err != nil {
return err
}
2024-08-20 09:08:40 +00:00
aliasingMap := viper . GetStringMapString ( "pools.aliases" )
if val , ok := aliasingMap [ data . Pool ] ; ok {
data . Pool = val
}
pool , err := services . GetAttachmentPoolByAlias ( data . Pool )
if err != nil {
return fiber . NewError ( fiber . StatusBadRequest , fmt . Sprintf ( "unable to get attachment pool info: %v" , err ) )
}
if err = gap . H . EnsureGrantedPerm ( c , "CreateAttachments" , data . Size ) ; err != nil {
return err
} else if pool . Config . Data ( ) . MaxFileSize != nil && * pool . Config . Data ( ) . MaxFileSize > data . Size {
return fiber . NewError ( fiber . StatusBadRequest , fmt . Sprintf ( "attachment pool %s doesn't allow file larger than %d" , pool . Alias , * pool . Config . Data ( ) . MaxFileSize ) )
}
metadata , err := services . NewAttachmentPlaceholder ( database . C , user , models . Attachment {
2024-08-20 14:55:58 +00:00
Name : data . FileName ,
Size : data . Size ,
2024-08-20 09:08:40 +00:00
Alternative : data . Alternative ,
MimeType : data . MimeType ,
Metadata : data . Metadata ,
IsMature : data . IsMature ,
IsAnalyzed : false ,
Destination : models . AttachmentDstTemporary ,
Pool : & pool ,
PoolID : & pool . ID ,
} )
if err != nil {
return fiber . NewError ( fiber . StatusBadRequest , err . Error ( ) )
}
2024-08-20 11:17:43 +00:00
return c . JSON ( fiber . Map {
2024-08-20 14:55:58 +00:00
"chunk_size" : viper . GetInt64 ( "performance.file_chunk_size" ) ,
"chunk_count" : len ( metadata . FileChunks ) ,
"meta" : metadata ,
2024-08-20 11:17:43 +00:00
} )
2024-08-20 09:08:40 +00:00
}
func uploadAttachmentMultipart ( c * fiber . Ctx ) error {
if err := gap . H . EnsureAuthenticated ( c ) ; err != nil {
return err
}
user := c . Locals ( "user" ) . ( models . Account )
rid := c . Params ( "file" )
cid := c . Params ( "chunk" )
file , err := c . FormFile ( "file" )
if err != nil {
return err
} else if file . Size > viper . GetInt64 ( "performance.file_chunk_size" ) {
return fiber . NewError ( fiber . StatusBadRequest , "file is too large for one chunk" )
}
meta , err := services . GetAttachmentByRID ( rid )
if err != nil {
return fiber . NewError ( fiber . StatusNotFound , fmt . Sprintf ( "attachment was not found: %v" , err ) )
} else if user . ID != meta . AccountID {
return fiber . NewError ( fiber . StatusForbidden , "you are not authorized to upload this attachment" )
}
if _ , ok := meta . FileChunks [ cid ] ; ! ok {
return fiber . NewError ( fiber . StatusNotFound , fmt . Sprintf ( "chunk %s was not found" , cid ) )
} else if services . CheckChunkExistsInTemporary ( meta , cid ) {
return fiber . NewError ( fiber . StatusNotFound , fmt . Sprintf ( "chunk %s was uploaded" , cid ) )
}
if err := services . UploadChunkToTemporary ( c , cid , file , meta ) ; err != nil {
return fiber . NewError ( fiber . StatusBadRequest , err . Error ( ) )
}
chunkArrange := make ( [ ] string , len ( meta . FileChunks ) )
isAllUploaded := true
for cid , idx := range meta . FileChunks {
if ! services . CheckChunkExistsInTemporary ( meta , cid ) {
isAllUploaded = false
break
2024-08-20 14:55:58 +00:00
} else if val , ok := idx . ( json . Number ) ; ok {
data , _ := val . Int64 ( )
chunkArrange [ data ] = cid
2024-08-20 09:08:40 +00:00
}
}
if ! isAllUploaded {
return c . JSON ( meta )
}
if meta , err = services . MergeFileChunks ( meta , chunkArrange ) ; err != nil {
return fiber . NewError ( fiber . StatusInternalServerError , err . Error ( ) )
} else {
return c . JSON ( meta )
}
}