Reviewed-on: #1develop
@ -0,0 +1 @@ | |||
/Frontend/public/images/* |
@ -0,0 +1,51 @@ | |||
package Api | |||
import ( | |||
"errors" | |||
"log" | |||
"net/http" | |||
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Database" | |||
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Models" | |||
"github.com/gorilla/mux" | |||
) | |||
func getPostId(r *http.Request) (string, error) { | |||
var ( | |||
urlVars map[string]string | |||
id string | |||
ok bool | |||
) | |||
urlVars = mux.Vars(r) | |||
id, ok = urlVars["postID"] | |||
if !ok { | |||
return id, errors.New("Could not get id") | |||
} | |||
return id, nil | |||
} | |||
func getPostById(w http.ResponseWriter, r *http.Request) (Models.Post, error) { | |||
var ( | |||
postData Models.Post | |||
id string | |||
err error | |||
) | |||
id, err = getPostId(r) | |||
if err != nil { | |||
log.Printf("Error encountered getting id\n") | |||
JsonReturn(w, 500, "An error occured") | |||
return postData, err | |||
} | |||
postData, err = Database.GetPostById(id) | |||
if err != nil { | |||
log.Printf("Could not find pet with id %s\n", id) | |||
JsonReturn(w, 404, "Not found") | |||
return postData, err | |||
} | |||
return postData, nil | |||
} |
@ -0,0 +1,50 @@ | |||
package Api | |||
import ( | |||
"errors" | |||
"log" | |||
"net/http" | |||
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Database" | |||
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Models" | |||
"github.com/gorilla/mux" | |||
) | |||
func getPostImageId(r *http.Request) (string, error) { | |||
var ( | |||
urlVars map[string]string | |||
id string | |||
ok bool | |||
) | |||
urlVars = mux.Vars(r) | |||
id, ok = urlVars["imageID"] | |||
if !ok { | |||
return id, errors.New("Could not get id") | |||
} | |||
return id, nil | |||
} | |||
func getPostImageById(w http.ResponseWriter, r *http.Request) (Models.PostImage, error) { | |||
var ( | |||
postImageData Models.PostImage | |||
id string | |||
err error | |||
) | |||
id, err = getPostImageId(r) | |||
if err != nil { | |||
log.Printf("Error encountered getting id\n") | |||
JsonReturn(w, 500, "An error occured") | |||
return postImageData, err | |||
} | |||
postImageData, err = Database.GetPostImageById(id) | |||
if err != nil { | |||
log.Printf("Could not find pet with id %s\n", id) | |||
JsonReturn(w, 404, "Not found") | |||
return postImageData, err | |||
} | |||
return postImageData, nil | |||
} |
@ -0,0 +1,127 @@ | |||
package Api | |||
import ( | |||
"encoding/json" | |||
"io/ioutil" | |||
"log" | |||
"mime/multipart" | |||
"net/http" | |||
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Database" | |||
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Models" | |||
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Util" | |||
"github.com/gofrs/uuid" | |||
) | |||
func createPostImage(w http.ResponseWriter, r *http.Request) { | |||
var ( | |||
postData Models.Post | |||
postID string | |||
postUUID uuid.UUID | |||
postImage Models.PostImage | |||
formData *multipart.Form | |||
fileHeaders []*multipart.FileHeader | |||
fileHeader *multipart.FileHeader | |||
file multipart.File | |||
fileBytes []byte | |||
fileObject Util.FileObject | |||
returnJson []byte | |||
err error | |||
) | |||
postID, err = getPostId(r) | |||
if err != nil { | |||
log.Printf("Error encountered getting id\n") | |||
JsonReturn(w, 500, "An error occured") | |||
return | |||
} | |||
postUUID = uuid.FromStringOrNil(postID) | |||
err = r.ParseMultipartForm(20 << 20) | |||
if err != nil { | |||
log.Printf("Error encountered parsing multipart form: %s\n", err.Error()) | |||
JsonReturn(w, 500, "An error occured") | |||
return | |||
} | |||
formData = r.MultipartForm | |||
fileHeaders = formData.File["files"] | |||
for _, fileHeader = range fileHeaders { | |||
file, err = fileHeader.Open() | |||
if err != nil { | |||
log.Printf("Error encountered while post image upload: %s\n", err.Error()) | |||
JsonReturn(w, 500, "An error occured") | |||
return | |||
} | |||
defer file.Close() | |||
fileBytes, err = ioutil.ReadAll(file) | |||
if err != nil { | |||
log.Printf("Error encountered while post image upload: %s\n", err.Error()) | |||
JsonReturn(w, 500, "An error occured") | |||
return | |||
} | |||
fileObject, err = Util.WriteFile(fileBytes, "image") | |||
if err != nil { | |||
log.Printf("Error encountered while post image upload: %s\n", err.Error()) | |||
JsonReturn(w, 415, "Invalid filetype") | |||
return | |||
} | |||
postImage = Models.PostImage{ | |||
PostID: postUUID, | |||
Filepath: fileObject.Filepath, | |||
Mimetype: fileObject.Mimetype, | |||
Size: fileObject.Size, | |||
} | |||
err = Database.CreatePostImage(&postImage) | |||
if err != nil { | |||
log.Printf("Error encountered while creating post_image record: %s\n", err.Error()) | |||
JsonReturn(w, 500, "An error occured") | |||
return | |||
} | |||
} | |||
postData, err = getPostById(w, r) | |||
if err != nil { | |||
return | |||
} | |||
returnJson, err = json.MarshalIndent(postData, "", " ") | |||
if err != nil { | |||
JsonReturn(w, 500, "An error occured") | |||
return | |||
} | |||
// Return updated json | |||
w.WriteHeader(http.StatusOK) | |||
w.Write(returnJson) | |||
} | |||
func deletePostImage(w http.ResponseWriter, r *http.Request) { | |||
var ( | |||
postImageData Models.PostImage | |||
err error | |||
) | |||
postImageData, err = getPostImageById(w, r) | |||
if err != nil { | |||
return | |||
} | |||
err = Database.DeletePostImage(&postImageData) | |||
if err != nil { | |||
log.Printf("An error occured: %s\n", err.Error()) | |||
JsonReturn(w, 500, "An error occured") | |||
return | |||
} | |||
// Return updated json | |||
w.WriteHeader(http.StatusOK) | |||
} |
@ -0,0 +1,273 @@ | |||
package Api | |||
import ( | |||
"bytes" | |||
"encoding/json" | |||
"errors" | |||
"fmt" | |||
"io" | |||
"io/ioutil" | |||
"log" | |||
"mime/multipart" | |||
"net/http" | |||
"net/http/httptest" | |||
"os" | |||
"path" | |||
"path/filepath" | |||
"runtime" | |||
"strconv" | |||
"testing" | |||
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Database" | |||
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Models" | |||
"github.com/gorilla/mux" | |||
"gorm.io/gorm" | |||
) | |||
func init() { | |||
// Fix working directory for tests | |||
_, filename, _, _ := runtime.Caller(0) | |||
dir := path.Join(path.Dir(filename), "..") | |||
err := os.Chdir(dir) | |||
if err != nil { | |||
panic(err) | |||
} | |||
log.SetOutput(ioutil.Discard) | |||
Database.Init() | |||
r = mux.NewRouter() | |||
} | |||
func get(url string) (resp *http.Response, err error) { | |||
for i := 0; i < 5; i++ { | |||
resp, err = http.Get(url) | |||
if err == nil { | |||
return resp, err | |||
} | |||
} | |||
return resp, err | |||
} | |||
// Image generates a *os.File with a random image using the loremflickr.com service | |||
func fakeImage(width, height int, categories []string, prefix string, categoriesStrict bool) *os.File { | |||
url := "https://loremflickr.com" | |||
switch prefix { | |||
case "g": | |||
url += "/g" | |||
case "p": | |||
url += "/p" | |||
case "red": | |||
url += "/red" | |||
case "green": | |||
url += "/green" | |||
case "blue": | |||
url += "/blue" | |||
} | |||
url += string('/') + strconv.Itoa(width) + string('/') + strconv.Itoa(height) | |||
if len(categories) > 0 { | |||
url += string('/') | |||
for _, category := range categories { | |||
url += category + string(',') | |||
} | |||
if categoriesStrict { | |||
url += "/all" | |||
} | |||
} | |||
resp, err := get(url) | |||
defer resp.Body.Close() | |||
if err != nil { | |||
panic(err) | |||
} | |||
f, err := ioutil.TempFile(os.TempDir(), "loremflickr-img-*.jpg") | |||
if err != nil { | |||
panic(err) | |||
} | |||
io.Copy(f, resp.Body) | |||
err = f.Close() | |||
if err != nil { | |||
panic(err) | |||
} | |||
return f | |||
} | |||
func Test_createPostImages(t *testing.T) { | |||
t.Log("Testing createPostImages...") | |||
r.HandleFunc("/post/{postID}/image", createPostImage).Methods("POST") | |||
ts := httptest.NewServer(r) | |||
defer ts.Close() | |||
postData := Models.Post{ | |||
Title: "Test post", | |||
Content: "Test content", | |||
FrontPage: true, | |||
Order: 1, | |||
PostLinks: []Models.PostLink{ | |||
{ | |||
Type: "Facebook", | |||
Link: "http://facebook.com/", | |||
}, | |||
}, | |||
} | |||
err := Database.CreatePost(&postData) | |||
if err != nil { | |||
t.Errorf("Expected nil, recieved %s", err.Error()) | |||
} | |||
image := fakeImage(100, 100, []string{}, "", false) | |||
image, err = os.Open(image.Name()) | |||
if err != nil { | |||
t.Errorf("Expected nil, recieved %s", err.Error()) | |||
} | |||
body := &bytes.Buffer{} | |||
writer := multipart.NewWriter(body) | |||
part, err := writer.CreateFormFile("files", filepath.Base(image.Name())) | |||
if err != nil { | |||
t.Errorf("Expected nil, recieved %s", err.Error()) | |||
} | |||
io.Copy(part, image) | |||
writer.Close() | |||
request, err := http.NewRequest( | |||
"POST", | |||
fmt.Sprintf( | |||
"%s/post/%s/image", | |||
ts.URL, | |||
postData.ID, | |||
), | |||
body, | |||
) | |||
if err != nil { | |||
t.Errorf("Expected nil, recieved %s", err.Error()) | |||
} | |||
request.Header.Add("Content-Type", writer.FormDataContentType()) | |||
client := &http.Client{} | |||
res, err := client.Do(request) | |||
if err != nil { | |||
t.Errorf("Expected nil, recieved %s", err.Error()) | |||
} | |||
if res.StatusCode != http.StatusOK { | |||
t.Errorf("Expected %d, recieved %d", http.StatusOK, res.StatusCode) | |||
} | |||
defer res.Body.Close() | |||
updatePostData := new(Models.Post) | |||
err = json.NewDecoder(res.Body).Decode(updatePostData) | |||
if err != nil { | |||
t.Errorf("Expected nil, recieved %s", err.Error()) | |||
} | |||
defer Database.DB. | |||
Session(&gorm.Session{FullSaveAssociations: true}). | |||
Unscoped(). | |||
Select("PostLinks", "PostImages"). | |||
Delete(&postData) | |||
if len(updatePostData.PostImages) != 1 { | |||
t.Errorf("Expected len(updatePostData.PostImages) == 1, recieved %d", len(updatePostData.PostImages)) | |||
} | |||
for _, f := range updatePostData.PostImages { | |||
if _, err := os.Stat("./" + f.Filepath); errors.Is(err, os.ErrNotExist) { | |||
t.Errorf( | |||
"File ./%s does not exist", | |||
f.Filepath, | |||
) | |||
} else { | |||
os.Remove("./" + f.Filepath) | |||
} | |||
} | |||
} | |||
func Test_deletePostImages(t *testing.T) { | |||
t.Log("Testing createPostImages...") | |||
r.HandleFunc("/post/{postID}/image/{imageID}", deletePostImage).Methods("DELETE") | |||
ts := httptest.NewServer(r) | |||
defer ts.Close() | |||
image := fakeImage(100, 100, []string{}, "", false) | |||
image, err := os.Open(image.Name()) | |||
if err != nil { | |||
t.Errorf("Expected nil, recieved %s", err.Error()) | |||
} | |||
defer image.Close() | |||
postData := Models.Post{ | |||
Title: "Test post", | |||
Content: "Test content", | |||
FrontPage: true, | |||
Order: 1, | |||
PostLinks: []Models.PostLink{ | |||
{ | |||
Type: "Facebook", | |||
Link: "http://facebook.com/", | |||
}, | |||
}, | |||
PostImages: []Models.PostImage{ | |||
{ | |||
Filepath: image.Name(), | |||
}, | |||
}, | |||
} | |||
err = Database.CreatePost(&postData) | |||
if err != nil { | |||
t.Errorf("Expected nil, recieved %s", err.Error()) | |||
} | |||
defer Database.DB. | |||
Session(&gorm.Session{FullSaveAssociations: true}). | |||
Unscoped(). | |||
Select("PostLinks", "PostImages"). | |||
Delete(&postData) | |||
req, err := http.NewRequest("DELETE", fmt.Sprintf( | |||
"%s/post/%s/image/%s", | |||
ts.URL, | |||
postData.ID, | |||
postData.PostImages[0].ID, | |||
), nil) | |||
if err != nil { | |||
t.Errorf("Expected nil, recieved %s", err.Error()) | |||
} | |||
res, err := http.DefaultClient.Do(req) | |||
if err != nil { | |||
t.Errorf("Expected nil, recieved %s", err.Error()) | |||
return | |||
} | |||
defer res.Body.Close() | |||
if res.StatusCode != http.StatusOK { | |||
t.Errorf("Expected %d, recieved %d", http.StatusOK, res.StatusCode) | |||
} | |||
} |
@ -0,0 +1,33 @@ | |||
package Database | |||
import ( | |||
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Models" | |||
"gorm.io/gorm" | |||
"gorm.io/gorm/clause" | |||
) | |||
func CreatePostImage(postImageData *Models.PostImage) error { | |||
return DB.Session(&gorm.Session{FullSaveAssociations: true}). | |||
Create(postImageData). | |||
Error | |||
} | |||
func GetPostImageById(id string) (Models.PostImage, error) { | |||
var ( | |||
postImageData Models.PostImage | |||
err error | |||
) | |||
err = DB.Preload(clause.Associations). | |||
First(&postImageData, "id = ?", id). | |||
Error | |||
return postImageData, err | |||
} | |||
func DeletePostImage(postImageData *Models.PostImage) error { | |||
return DB.Session(&gorm.Session{FullSaveAssociations: true}). | |||
Delete(postImageData). | |||
Error | |||
} |
@ -0,0 +1,12 @@ | |||
FLAC_FILES = $(shell find . -type d -not -path '*/\.git/*' -not -path '*\/.git' -not -path '.') | |||
default: test build | |||
build: | |||
go build -o SuddenImpactRecords main | |||
test: | |||
for dir in ${FLAC_FILES}; do \ | |||
go test $$dir; \ | |||
done | |||
@ -0,0 +1,67 @@ | |||
package Util | |||
import ( | |||
"errors" | |||
"fmt" | |||
"io/ioutil" | |||
"os" | |||
"strings" | |||
"github.com/gabriel-vasile/mimetype" | |||
) | |||
type FileObject struct { | |||
Filepath string | |||
Mimetype string | |||
Size int64 | |||
} | |||
func WriteFile(fileBytes []byte, acceptedMime string) (FileObject, error) { | |||
var ( | |||
mime *mimetype.MIME | |||
mimeSplit []string | |||
file *os.File | |||
fi os.FileInfo | |||
fileObject FileObject | |||
err error | |||
) | |||
mime = mimetype.Detect(fileBytes) | |||
mimeSplit = strings.Split(mime.String(), "/") | |||
if mimeSplit[0] != acceptedMime { | |||
return fileObject, errors.New("Invalid filetype provided") | |||
} | |||
file, err = ioutil.TempFile( | |||
fmt.Sprintf( | |||
"./Frontend/public/%ss/", | |||
mimeSplit[0], | |||
), | |||
fmt.Sprintf( | |||
"%ss-*%s", | |||
mimeSplit[0], | |||
mime.Extension(), | |||
), | |||
) | |||
if err != nil { | |||
return fileObject, err | |||
} | |||
defer file.Close() | |||
_, err = file.Write(fileBytes) | |||
fi, err = file.Stat() | |||
if err != nil { | |||
return fileObject, err | |||
} | |||
fileObject = FileObject{ | |||
Filepath: file.Name(), | |||
Mimetype: mime.String(), | |||
Size: fi.Size(), | |||
} | |||
return fileObject, err | |||
} |