2024-06-22 04:29:20 +00:00
package api
2024-05-17 07:59:51 +00:00
import (
"fmt"
2024-07-25 14:02:26 +00:00
"net/url"
"path/filepath"
2024-08-18 11:53:50 +00:00
"strconv"
2024-07-25 14:02:26 +00:00
2024-06-16 15:24:54 +00:00
"git.solsynth.dev/hydrogen/paperclip/pkg/internal/database"
2024-06-22 04:29:20 +00:00
"git.solsynth.dev/hydrogen/paperclip/pkg/internal/gap"
"git.solsynth.dev/hydrogen/paperclip/pkg/internal/server/exts"
2024-05-17 07:59:51 +00:00
2024-06-22 04:18:54 +00:00
"git.solsynth.dev/hydrogen/paperclip/pkg/internal/models"
"git.solsynth.dev/hydrogen/paperclip/pkg/internal/services"
2024-05-17 07:59:51 +00:00
"github.com/gofiber/fiber/v2"
jsoniter "github.com/json-iterator/go"
"github.com/samber/lo"
"github.com/spf13/viper"
)
func openAttachment ( c * fiber . Ctx ) error {
2024-08-18 11:53:50 +00:00
id := c . Params ( "id" )
2024-05-17 07:59:51 +00:00
2024-08-18 11:53:50 +00:00
var err error
var metadata models . Attachment
if numericId , numericErr := strconv . Atoi ( id ) ; numericErr == nil {
metadata , err = services . GetAttachmentByID ( uint ( numericId ) )
} else {
metadata , err = services . GetAttachmentByRID ( id )
}
2024-05-17 07:59:51 +00:00
if err != nil {
return fiber . NewError ( fiber . StatusNotFound )
}
2024-07-28 13:03:56 +00:00
var destMap map [ string ] any
if metadata . Destination == models . AttachmentDstTemporary {
destMap = viper . GetStringMap ( "destinations.temporary" )
} else {
destMap = viper . GetStringMap ( "destinations.permanent" )
2024-05-17 07:59:51 +00:00
}
2024-07-28 13:03:56 +00:00
var dest models . BaseDestination
rawDest , _ := jsoniter . Marshal ( destMap )
_ = jsoniter . Unmarshal ( rawDest , & dest )
2024-05-17 07:59:51 +00:00
2024-07-28 13:03:56 +00:00
switch dest . Type {
2024-05-17 07:59:51 +00:00
case models . DestinationTypeLocal :
var destConfigured models . LocalDestination
_ = jsoniter . Unmarshal ( rawDest , & destConfigured )
2024-06-01 14:15:41 +00:00
if len ( metadata . MimeType ) > 0 {
c . Set ( fiber . HeaderContentType , metadata . MimeType )
}
return c . SendFile ( filepath . Join ( destConfigured . Path , metadata . Uuid ) , false )
2024-05-17 07:59:51 +00:00
case models . DestinationTypeS3 :
var destConfigured models . S3Destination
_ = jsoniter . Unmarshal ( rawDest , & destConfigured )
2024-07-29 05:22:57 +00:00
if len ( destConfigured . AccessBaseURL ) > 0 {
return c . Redirect ( fmt . Sprintf (
"%s/%s" ,
destConfigured . AccessBaseURL ,
url . QueryEscape ( filepath . Join ( destConfigured . Path , metadata . Uuid ) ) ,
) , fiber . StatusMovedPermanently )
} else {
protocol := lo . Ternary ( destConfigured . EnableSSL , "https" , "http" )
return c . Redirect ( fmt . Sprintf (
"%s://%s.%s/%s" ,
protocol ,
destConfigured . Bucket ,
destConfigured . Endpoint ,
url . QueryEscape ( filepath . Join ( destConfigured . Path , metadata . Uuid ) ) ,
) , fiber . StatusMovedPermanently )
}
2024-05-17 07:59:51 +00:00
default :
2024-07-28 13:03:56 +00:00
return fmt . Errorf ( "invalid destination: unsupported protocol %s" , dest . Type )
2024-05-17 07:59:51 +00:00
}
}
func getAttachmentMeta ( c * fiber . Ctx ) error {
2024-05-20 14:31:55 +00:00
id , _ := c . ParamsInt ( "id" )
2024-05-17 07:59:51 +00:00
2024-05-20 14:31:55 +00:00
metadata , err := services . GetAttachmentByID ( uint ( id ) )
2024-05-17 07:59:51 +00:00
if err != nil {
return fiber . NewError ( fiber . StatusNotFound )
}
return c . JSON ( metadata )
}
func createAttachment ( c * fiber . Ctx ) error {
2024-07-27 17:15:52 +00:00
if err := gap . H . EnsureAuthenticated ( c ) ; err != nil {
return err
2024-06-23 11:38:53 +00:00
}
2024-08-03 07:43:15 +00:00
user := c . Locals ( "user" ) . ( models . Account )
2024-05-17 07:59:51 +00:00
2024-08-18 06:09:52 +00:00
poolAlias := c . FormValue ( "pool" )
if len ( poolAlias ) == 0 {
poolAlias = c . FormValue ( "usage" )
}
aliasingMap := viper . GetStringMapString ( "pools.aliases" )
if val , ok := aliasingMap [ poolAlias ] ; ok {
poolAlias = val
}
pool , err := services . GetAttachmentPoolByAlias ( poolAlias )
if err != nil {
return fiber . NewError ( fiber . StatusBadRequest , fmt . Sprintf ( "unable to get attachment pool info: %v" , err ) )
2024-05-17 07:59:51 +00:00
}
file , err := c . FormFile ( "file" )
if err != nil {
return err
}
2024-08-18 04:01:41 +00:00
if err = gap . H . EnsureGrantedPerm ( c , "CreateAttachments" , file . Size ) ; err != nil {
2024-07-27 17:15:52 +00:00
return err
2024-08-18 06:09:52 +00:00
} else if pool . Config . Data ( ) . MaxFileSize != nil && file . Size > * pool . Config . Data ( ) . MaxFileSize {
return fiber . NewError ( fiber . StatusBadRequest , fmt . Sprintf ( "attachment pool %s doesn't allow file larger than %d" , pool . Alias , * pool . Config . Data ( ) . MaxFileSize ) )
2024-05-17 12:36:00 +00:00
}
2024-05-20 12:02:44 +00:00
usermeta := make ( map [ string ] any )
2024-05-17 07:59:51 +00:00
_ = jsoniter . UnmarshalFromString ( c . FormValue ( "metadata" ) , & usermeta )
tx := database . C . Begin ( )
2024-07-28 17:47:33 +00:00
2024-07-28 16:01:51 +00:00
metadata , err := services . NewAttachmentMetadata ( tx , user , file , models . Attachment {
2024-05-17 07:59:51 +00:00
Alternative : c . FormValue ( "alt" ) ,
MimeType : c . FormValue ( "mimetype" ) ,
2024-05-20 12:02:44 +00:00
Metadata : usermeta ,
2024-05-17 07:59:51 +00:00
IsMature : len ( c . FormValue ( "mature" ) ) > 0 ,
2024-07-29 05:22:57 +00:00
IsAnalyzed : false ,
2024-07-28 13:03:56 +00:00
Destination : models . AttachmentDstTemporary ,
2024-08-18 06:09:52 +00:00
Pool : & pool ,
PoolID : & pool . ID ,
2024-05-17 07:59:51 +00:00
} )
if err != nil {
tx . Rollback ( )
return fiber . NewError ( fiber . StatusBadRequest , err . Error ( ) )
}
2024-07-28 16:01:51 +00:00
if err := services . UploadFileToTemporary ( c , file , metadata ) ; err != nil {
tx . Rollback ( )
return fiber . NewError ( fiber . StatusBadRequest , err . Error ( ) )
2024-05-17 07:59:51 +00:00
}
tx . Commit ( )
2024-08-02 13:38:47 +00:00
metadata . Account = user
2024-08-18 06:09:52 +00:00
metadata . Pool = & pool
2024-07-28 16:01:51 +00:00
services . PublishAnalyzeTask ( metadata )
2024-05-17 07:59:51 +00:00
return c . JSON ( metadata )
}
2024-05-20 12:02:44 +00:00
func updateAttachmentMeta ( c * fiber . Ctx ) error {
2024-05-20 14:31:55 +00:00
id , _ := c . ParamsInt ( "id" , 0 )
2024-06-22 04:29:20 +00:00
if err := gap . H . EnsureAuthenticated ( c ) ; err != nil {
return err
}
2024-06-23 11:38:53 +00:00
user := c . Locals ( "user" ) . ( models . Account )
2024-05-20 12:02:44 +00:00
var data struct {
2024-07-29 13:11:42 +00:00
Alternative string ` json:"alt" `
IsMature bool ` json:"is_mature" `
2024-05-20 12:02:44 +00:00
}
2024-06-22 04:29:20 +00:00
if err := exts . BindAndValidate ( c , & data ) ; err != nil {
2024-05-20 12:02:44 +00:00
return err
}
var attachment models . Attachment
2024-07-25 14:15:06 +00:00
if err := database . C . Where ( "id = ? AND account_id = ?" , id , user . ID ) . First ( & attachment ) . Error ; err != nil {
2024-05-20 12:02:44 +00:00
return fiber . NewError ( fiber . StatusBadRequest , err . Error ( ) )
}
attachment . Alternative = data . Alternative
attachment . IsMature = data . IsMature
2024-06-29 13:16:11 +00:00
if attachment , err := services . UpdateAttachment ( attachment ) ; err != nil {
2024-05-20 12:02:44 +00:00
return fiber . NewError ( fiber . StatusBadRequest , err . Error ( ) )
2024-06-29 13:16:11 +00:00
} else {
return c . JSON ( attachment )
2024-05-20 12:02:44 +00:00
}
}
2024-05-17 07:59:51 +00:00
func deleteAttachment ( c * fiber . Ctx ) error {
id , _ := c . ParamsInt ( "id" , 0 )
2024-06-22 04:29:20 +00:00
if err := gap . H . EnsureAuthenticated ( c ) ; err != nil {
return err
}
2024-06-23 11:38:53 +00:00
user := c . Locals ( "user" ) . ( models . Account )
2024-05-17 07:59:51 +00:00
attachment , err := services . GetAttachmentByID ( uint ( id ) )
if err != nil {
return fiber . NewError ( fiber . StatusNotFound , err . Error ( ) )
2024-07-28 13:03:56 +00:00
} else if attachment . AccountID != user . ID {
2024-05-17 07:59:51 +00:00
return fiber . NewError ( fiber . StatusNotFound , "record not created by you" )
}
if err := services . DeleteAttachment ( attachment ) ; err != nil {
return fiber . NewError ( fiber . StatusInternalServerError , err . Error ( ) )
} else {
return c . SendStatus ( fiber . StatusOK )
}
}