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"
2024-10-27 05:13:40 +00:00
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
2024-11-02 02:41:31 +00:00
"git.solsynth.dev/hypernet/paperclip/pkg/internal/database"
"git.solsynth.dev/hypernet/paperclip/pkg/internal/models"
"git.solsynth.dev/hypernet/paperclip/pkg/internal/server/exts"
"git.solsynth.dev/hypernet/paperclip/pkg/internal/services"
2024-08-20 09:08:40 +00:00
"github.com/gofiber/fiber/v2"
"github.com/spf13/viper"
)
func createAttachmentMultipartPlaceholder ( c * fiber . Ctx ) error {
2024-11-02 18:14:56 +00:00
user := c . Locals ( "nex_user" ) . ( * sec . UserInfo )
2024-08-20 09:08:40 +00:00
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 ) )
}
2024-10-27 05:13:40 +00:00
if ! user . HasPermNode ( "CreateAttachments" , data . Size ) {
return fiber . NewError ( fiber . StatusForbidden , "you are not permitted to create attachments like this large" )
2024-08-20 09:08:40 +00:00
} 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 {
2024-11-02 18:14:56 +00:00
user := c . Locals ( "nex_user" ) . ( * sec . UserInfo )
2024-08-20 09:08:40 +00:00
rid := c . Params ( "file" )
cid := c . Params ( "chunk" )
2024-11-05 12:56:53 +00:00
fileData := c . Body ( )
if len ( fileData ) == 0 {
return fiber . NewError ( fiber . StatusBadRequest , "no file data" )
} else if len ( fileData ) > viper . GetInt ( "performance.file_chunk_size" ) {
2024-08-20 09:08:40 +00:00
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 ) )
}
2024-11-05 12:56:53 +00:00
if err := services . UploadChunkToTemporaryWithRaw ( c , cid , fileData , meta ) ; err != nil {
2024-08-20 09:08:40 +00:00
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 )
}
}