mirror of
https://github.com/Forceu/Gokapi.git
synced 2026-01-07 01:19:34 -06:00
Fixed AWS upload / download
This commit is contained in:
@@ -35,8 +35,8 @@ func main() {
|
||||
configuration.Load()
|
||||
resetPassword(passedFlags)
|
||||
createSsl(passedFlags)
|
||||
aws.Init()
|
||||
if aws.IsCredentialProvided(true) {
|
||||
|
||||
if aws.Init() {
|
||||
fmt.Println("Saving new files to cloud storage")
|
||||
} else {
|
||||
fmt.Println("Saving new files to local storage")
|
||||
|
||||
@@ -57,7 +57,6 @@ type Configuration struct {
|
||||
SaltFiles string `json:"SaltFiles"`
|
||||
LengthId int `json:"LengthId"`
|
||||
DataDir string `json:"DataDir"`
|
||||
AwsBucket string `json:"AwsBucket"`
|
||||
MaxMemory int `json:"MaxMemory"`
|
||||
UseSsl bool `json:"UseSsl"`
|
||||
}
|
||||
@@ -77,7 +76,6 @@ func Load() {
|
||||
err = decoder.Decode(&serverSettings)
|
||||
helper.Check(err)
|
||||
updateConfig()
|
||||
serverSettings.AwsBucket = Environment.AwsBucket
|
||||
serverSettings.MaxMemory = Environment.MaxMemory
|
||||
helper.CreateDir(serverSettings.DataDir)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
testconfiguration.Create(false)
|
||||
os.Setenv("GOKAPI_AWS_BUCKET", "bucket")
|
||||
exitVal := m.Run()
|
||||
testconfiguration.Delete()
|
||||
os.Exit(exitVal)
|
||||
@@ -70,7 +69,6 @@ func TestCreateNewConfig(t *testing.T) {
|
||||
test.IsEqualString(t, serverSettings.RedirectUrl, "http://test2.com")
|
||||
test.IsEqualString(t, serverSettings.AdminPassword, "5bbf5684437a4c658d2e0890d784694afb63f715")
|
||||
test.IsEqualString(t, HashPassword("testtest2", false), "5bbf5684437a4c658d2e0890d784694afb63f715")
|
||||
test.IsEqualString(t, serverSettings.AwsBucket, "bucket")
|
||||
test.IsEqualInt(t, serverSettings.LengthId, 15)
|
||||
test.IsEqualBool(t, serverSettings.UseSsl, false)
|
||||
os.Remove("test/config.json")
|
||||
@@ -101,7 +99,6 @@ func TestUpgradeDb(t *testing.T) {
|
||||
test.IsEqualBool(t, serverSettings.DownloadStatus == nil, false)
|
||||
test.IsEqualString(t, serverSettings.Files["MgXJLe4XLfpXcL12ec4i"].ContentType, "application/octet-stream")
|
||||
test.IsEqualInt(t, serverSettings.ConfigVersion, currentConfigVersion)
|
||||
test.IsEqualString(t, serverSettings.AwsBucket, "bucket")
|
||||
test.IsEqualBool(t, serverSettings.UseSsl, true)
|
||||
os.Unsetenv("GOKAPI_USE_SSL")
|
||||
testconfiguration.Create(false)
|
||||
|
||||
@@ -45,13 +45,16 @@ func NewFile(fileContent io.Reader, fileHeader *multipart.FileHeader, uploadRequ
|
||||
ContentType: fileHeader.Header.Get("Content-Type"),
|
||||
}
|
||||
addHotlink(&file)
|
||||
if aws.IsAvailable() {
|
||||
aws.AddBucketName(&file)
|
||||
}
|
||||
settings := configuration.GetServerSettings()
|
||||
filename := settings.DataDir + "/" + file.SHA256
|
||||
dataDir := settings.DataDir
|
||||
file.AwsBucket = settings.AwsBucket
|
||||
settings.Files[id] = file
|
||||
configuration.ReleaseAndSave()
|
||||
if aws.IsCredentialProvided(false) {
|
||||
if aws.IsAvailable() {
|
||||
aws.AddBucketName(&file)
|
||||
_, err := aws.Upload(reader, file)
|
||||
if err != nil {
|
||||
return models.File{}, err
|
||||
@@ -174,6 +177,8 @@ func ServeFile(file models.File, w http.ResponseWriter, r *http.Request, forceDo
|
||||
defer storageData.Close()
|
||||
if forceDownload {
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=\""+file.Name+"\"")
|
||||
} else {
|
||||
w.Header().Set("Content-Disposition", "inline; filename=\""+file.Name+"\"")
|
||||
}
|
||||
w.Header().Set("Content-Length", strconv.FormatInt(size, 10))
|
||||
w.Header().Set("Content-Type", file.ContentType)
|
||||
@@ -183,7 +188,7 @@ func ServeFile(file models.File, w http.ResponseWriter, r *http.Request, forceDo
|
||||
} else {
|
||||
// If file is stored on AWS
|
||||
downloadstatus.SetDownload(file)
|
||||
err := aws.RedirectToDownload(w, r, file)
|
||||
err := aws.RedirectToDownload(w, r, file, forceDownload)
|
||||
helper.Check(err)
|
||||
// We are not setting a download complete status, as there is no reliable way to confirm that the
|
||||
// file has been completely downloaded. It expires automatically after 24 hours.
|
||||
@@ -277,6 +282,12 @@ func DeleteFile(keyId string) bool {
|
||||
}
|
||||
item.ExpireAt = 0
|
||||
settings.Files[keyId] = item
|
||||
for _, status := range settings.DownloadStatus {
|
||||
if status.FileId == item.Id {
|
||||
status.ExpireAt = 0
|
||||
settings.DownloadStatus[status.Id] = status
|
||||
}
|
||||
}
|
||||
configuration.Release()
|
||||
CleanUp(false)
|
||||
return true
|
||||
|
||||
@@ -131,7 +131,7 @@ func TestNewFile(t *testing.T) {
|
||||
bigFile.Close()
|
||||
os.Remove("bigfile")
|
||||
|
||||
if aws.IsAvailable {
|
||||
if aws.IsIncludedInBuild {
|
||||
testconfiguration.EnableS3()
|
||||
file, err = NewFile(bytes.NewReader(content), &header, request)
|
||||
test.IsNil(t, err)
|
||||
@@ -158,7 +158,7 @@ func TestServeFile(t *testing.T) {
|
||||
test.IsNil(t, err)
|
||||
test.IsEqualString(t, string(content), "This is a file for testing purposes")
|
||||
|
||||
if aws.IsAvailable {
|
||||
if aws.IsIncludedInBuild {
|
||||
testconfiguration.EnableS3()
|
||||
r = httptest.NewRequest("GET", "/upload", nil)
|
||||
w = httptest.NewRecorder()
|
||||
@@ -236,7 +236,7 @@ func TestCleanUp(t *testing.T) {
|
||||
test.IsEqualString(t, settings.Files["cleanuptest123456789"].Name, "")
|
||||
test.FileDoesNotExist(t, "test/data/2341354656543213246465465465432456898794")
|
||||
|
||||
if aws.IsAvailable {
|
||||
if aws.IsIncludedInBuild {
|
||||
testconfiguration.EnableS3()
|
||||
test.IsEqualString(t, settings.Files["awsTest1234567890123"].Name, "Aws Test File")
|
||||
testconfiguration.DisableS3()
|
||||
@@ -259,7 +259,7 @@ func TestDeleteFile(t *testing.T) {
|
||||
result = DeleteFile("")
|
||||
test.IsEqualBool(t, result, false)
|
||||
|
||||
if aws.IsAvailable {
|
||||
if aws.IsIncludedInBuild {
|
||||
testconfiguration.EnableS3()
|
||||
result, err := aws.FileExists(settings.Files["awsTest1234567890123"])
|
||||
test.IsEqualBool(t, result, true)
|
||||
|
||||
@@ -4,7 +4,6 @@ package aws
|
||||
|
||||
import (
|
||||
"Gokapi/internal/configuration/cloudconfig"
|
||||
"Gokapi/internal/environment"
|
||||
"Gokapi/internal/models"
|
||||
"fmt"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
@@ -19,51 +18,48 @@ import (
|
||||
)
|
||||
|
||||
var awsConfig models.AwsConfig
|
||||
var environmentHolder environment.Environment
|
||||
|
||||
// IsAvailable is true if Gokapi has been compiled with AWS support or the API is being mocked
|
||||
const IsAvailable = true
|
||||
var isCorrectLogin bool
|
||||
|
||||
// IsIncludedInBuild is true if Gokapi has been compiled with AWS support or the API is being mocked
|
||||
const IsIncludedInBuild = true
|
||||
|
||||
// IsMockApi is true if the API is being mocked and therefore can only be used for testing purposes
|
||||
const IsMockApi = false
|
||||
|
||||
// IsCredentialProvided returns true if all credentials are provided
|
||||
func IsCredentialProvided(checkIfValid bool) bool {
|
||||
e := getEnvironment()
|
||||
isProvided := e.IsAwsProvided()
|
||||
if !isProvided {
|
||||
return false
|
||||
}
|
||||
if checkIfValid {
|
||||
return isValidLogin()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func getEnvironment() *environment.Environment {
|
||||
if environmentHolder == (environment.Environment{}) {
|
||||
environmentHolder = environment.New()
|
||||
}
|
||||
return &environmentHolder
|
||||
}
|
||||
|
||||
// Init reads the credentials for AWS
|
||||
func Init() {
|
||||
// Init reads the credentials for AWS. Returns true if valid
|
||||
func Init() bool {
|
||||
config, ok := cloudconfig.Load()
|
||||
if ok {
|
||||
fmt.Println("AWS config loaded")
|
||||
awsConfig = config.Aws
|
||||
}
|
||||
return isValidLogin()
|
||||
}
|
||||
|
||||
// AddBucketName adds the bucket name to the file to be stored
|
||||
func AddBucketName(file *models.File) {
|
||||
file.AwsBucket = awsConfig.Bucket
|
||||
}
|
||||
|
||||
// IsAvailable returns true if valid credentials have been passed
|
||||
func IsAvailable() bool {
|
||||
return isCorrectLogin
|
||||
}
|
||||
|
||||
func isValidLogin() bool {
|
||||
sess := createSession()
|
||||
svc := s3.New(sess)
|
||||
_, err := svc.Config.Credentials.Get()
|
||||
if !awsConfig.IsAllProvided() {
|
||||
return false
|
||||
}
|
||||
_, err := FileExists(models.File{AwsBucket: awsConfig.Bucket, SHA256: "invalid"})
|
||||
if err != nil {
|
||||
fmt.Println("WARNING: AWS login not successful: " + err.Error())
|
||||
fmt.Println("WARNING: AWS login not successful")
|
||||
fmt.Println(err.Error())
|
||||
isCorrectLogin = false
|
||||
return false
|
||||
}
|
||||
fmt.Println("AWS login successful")
|
||||
isCorrectLogin = true
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -110,14 +106,21 @@ func Download(writer io.WriterAt, file models.File) (int64, error) {
|
||||
|
||||
// RedirectToDownload creates a presigned link that is valid for 15 seconds and redirects the
|
||||
// client to this url
|
||||
func RedirectToDownload(w http.ResponseWriter, r *http.Request, file models.File) error {
|
||||
func RedirectToDownload(w http.ResponseWriter, r *http.Request, file models.File, forceDownload bool) error {
|
||||
sess := createSession()
|
||||
s3svc := s3.New(sess)
|
||||
|
||||
contentDisposition := "inline; filename=\"" + file.Name + "\""
|
||||
if forceDownload {
|
||||
contentDisposition = "Attachment; filename=\"" + file.Name + "\""
|
||||
}
|
||||
|
||||
req, _ := s3svc.GetObjectRequest(&s3.GetObjectInput{
|
||||
Bucket: aws.String(file.AwsBucket),
|
||||
Key: aws.String(file.SHA256),
|
||||
ResponseContentDisposition: aws.String("filename=" + file.Name),
|
||||
ResponseContentDisposition: aws.String(contentDisposition),
|
||||
ResponseCacheControl: aws.String("no-store"),
|
||||
ResponseContentType: aws.String(file.ContentType),
|
||||
})
|
||||
|
||||
url, err := req.Presign(15 * time.Second)
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
)
|
||||
|
||||
var uploadedFiles []models.File
|
||||
var isCorrectLogin bool
|
||||
|
||||
const (
|
||||
region = "mock-region-1"
|
||||
@@ -20,25 +21,39 @@ const (
|
||||
accessKey = "accKey"
|
||||
)
|
||||
|
||||
// Init reads the credentials for AWS
|
||||
func Init() {
|
||||
}
|
||||
|
||||
// IsAvailable is true if Gokapi has been compiled with AWS support or the API is being mocked
|
||||
const IsAvailable = true
|
||||
// IsIncludedInBuild is true if Gokapi has been compiled with AWS support or the API is being mocked
|
||||
const IsIncludedInBuild = true
|
||||
|
||||
// IsMockApi is true if the API is being mocked and therefore can only be used for testing purposes
|
||||
const IsMockApi = true
|
||||
|
||||
// Init reads the credentials for AWS
|
||||
func Init() bool {
|
||||
return isValidCredentials()
|
||||
}
|
||||
|
||||
// IsAvailable returns true if valid credentials have been passed
|
||||
func IsAvailable() bool {
|
||||
return isCorrectLogin
|
||||
}
|
||||
|
||||
|
||||
// AddBucketName adds the bucket name to the file to be stored
|
||||
func AddBucketName(file *models.File) {
|
||||
file.AwsBucket = bucketName
|
||||
}
|
||||
|
||||
func isValidCredentials() bool {
|
||||
requiredKeys := []string{"GOKAPI_AWS_BUCKET", "AWS_REGION", "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"}
|
||||
requiredValues := []string{bucketName, region, accessId, accessKey}
|
||||
for i, key := range requiredKeys {
|
||||
val, _ := os.LookupEnv(key)
|
||||
if val != requiredValues[i] {
|
||||
isCorrectLogin = false
|
||||
return false
|
||||
}
|
||||
}
|
||||
isCorrectLogin = true
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -93,7 +108,7 @@ func isUploaded(file models.File) bool {
|
||||
|
||||
// RedirectToDownload creates a presigned link that is valid for 15 seconds and redirects the
|
||||
// client to this url
|
||||
func RedirectToDownload(w http.ResponseWriter, r *http.Request, file models.File) error {
|
||||
func RedirectToDownload(w http.ResponseWriter, r *http.Request, file models.File, forceDownload bool) error {
|
||||
if !isValidCredentials() {
|
||||
return errors.New("invalid credentials / invalid bucket / invalid region")
|
||||
}
|
||||
|
||||
@@ -11,21 +11,27 @@ import (
|
||||
|
||||
const errorString = "AWS not supported in this build"
|
||||
|
||||
// IsAvailable is true if Gokapi has been compiled with AWS support or the API is being mocked
|
||||
const IsAvailable = false
|
||||
// IsIncludedInBuild is true if Gokapi has been compiled with AWS support or the API is being mocked
|
||||
const IsIncludedInBuild = false
|
||||
|
||||
// IsMockApi is true if the API is being mocked and therefore can only be used for testing purposes
|
||||
const IsMockApi = false
|
||||
|
||||
// Init reads the credentials for AWS
|
||||
func Init() {
|
||||
func Init() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCredentialProvided returns true if all credentials are provided, however does not check them to be valid
|
||||
func IsCredentialProvided(checkIfValid bool) bool {
|
||||
// IsAvailable returns true if valid credentials have been passed
|
||||
func IsAvailable() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// AddBucketName adds the bucket name to the file to be stored
|
||||
func AddBucketName(file *models.File) {
|
||||
return
|
||||
}
|
||||
|
||||
// Upload uploads a file to AWS
|
||||
func Upload(input io.Reader, file models.File) (string, error) {
|
||||
return "", errors.New(errorString)
|
||||
@@ -38,7 +44,7 @@ func Download(writer io.WriterAt, file models.File) (int64, error) {
|
||||
|
||||
// RedirectToDownload creates a presigned link that is valid for 15 seconds and redirects the
|
||||
// client to this url
|
||||
func RedirectToDownload(w http.ResponseWriter, r *http.Request, file models.File) error {
|
||||
func RedirectToDownload(w http.ResponseWriter, r *http.Request, file models.File, forceDownload bool) error {
|
||||
return errors.New(errorString)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"Gokapi/internal/environment"
|
||||
"Gokapi/internal/models"
|
||||
"Gokapi/internal/test"
|
||||
"fmt"
|
||||
@@ -61,7 +60,7 @@ func TestDownloadFromAws(t *testing.T) {
|
||||
func TestRedirectToDownload(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("GET", "/download", nil)
|
||||
err := RedirectToDownload(w, r, testFile)
|
||||
err := RedirectToDownload(w, r, testFile, false)
|
||||
test.IsNil(t, err)
|
||||
fmt.Println(w.Body.String())
|
||||
test.ResponseBodyContains(t, w, "<a href=\"https://")
|
||||
@@ -95,28 +94,3 @@ func TestDeleteObject(t *testing.T) {
|
||||
test.IsEqualBool(t, result, true)
|
||||
test.IsNil(t, err)
|
||||
}
|
||||
|
||||
func TestIsCredentialProvided(t *testing.T) {
|
||||
os.Unsetenv("GOKAPI_AWS_REGION")
|
||||
os.Unsetenv("GOKAPI_AWS_KEY")
|
||||
os.Unsetenv("GOKAPI_KEY_SECRET")
|
||||
os.Unsetenv("GOKAPI_AWS_BUCKET")
|
||||
environmentHolder = environment.Environment{}
|
||||
test.IsEqualBool(t, IsCredentialProvided(false), false)
|
||||
environmentHolder = environment.Environment{}
|
||||
os.Setenv("GOKAPI_AWS_REGION", "valid")
|
||||
environmentHolder = environment.Environment{}
|
||||
test.IsEqualBool(t, IsCredentialProvided(false), false)
|
||||
environmentHolder = environment.Environment{}
|
||||
os.Setenv("GOKAPI_AWS_KEY", "valid")
|
||||
test.IsEqualBool(t, IsCredentialProvided(false), false)
|
||||
test.IsEqualBool(t, IsCredentialProvided(true), false)
|
||||
environmentHolder = environment.Environment{}
|
||||
os.Setenv("GOKAPI_AWS_KEY_SECRET", "valid")
|
||||
test.IsEqualBool(t, IsCredentialProvided(false), false)
|
||||
test.IsEqualBool(t, IsCredentialProvided(true), false)
|
||||
environmentHolder = environment.Environment{}
|
||||
os.Setenv("GOKAPI_AWS_BUCKET", "valid")
|
||||
test.IsEqualBool(t, IsCredentialProvided(false), true)
|
||||
test.IsEqualBool(t, IsCredentialProvided(true), true)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user