Commit ce0ac207 authored by Lukas Böhm's avatar Lukas Böhm 🎱
Browse files

new error handling

parent a1b0d672
......@@ -7,7 +7,6 @@ import (
"github.com/google/uuid"
"github.com/gorilla/mux"
"github.com/rs/cors"
"gorm.io/driver/sqlserver"
"gorm.io/gorm"
"io/ioutil"
"log"
......@@ -16,304 +15,251 @@ import (
"path/filepath"
)
type HTTPError struct {
Error error
Message string
Code int
}
type endpointREST func(http.ResponseWriter, *http.Request) *HTTPError
func (fn endpointREST) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if e := fn(w, r); e != nil { // e is *HTTPError, not os.Error.
fmt.Errorf("Error (%s): %s - %i", e.Error.Error(), e.Message, e.Code)
if e.Code == 401 {
w.Header().Set("WWW-Authenticate", `Basic realm="Please enter the password"`)
}
http.Error(w, e.Message, e.Code)
}
}
/////////////////////////////////
//////////// routes /////////////
/////////////////////////////////
func AllShares(w http.ResponseWriter, _ *http.Request) {
func AllShares(w http.ResponseWriter, r *http.Request) *HTTPError {
fmt.Println("AllShares")
db, err := GetDatabase()
//return 500
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
return &HTTPError{err, "Can't get database", 500}
}
var shares []Share
err = db.Where("is_public = ? AND is_temporary = 0", 1).Find(&shares).Error
// return 500
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
return &HTTPError{err, "Can't fetch data", 500}
}
// return 200
SendJSON(w, shares)
return nil
}
func GetShare(w http.ResponseWriter, r *http.Request) {
func GetShare(w http.ResponseWriter, r *http.Request) *HTTPError {
fmt.Println("Get Share")
vars := mux.Vars(r)
db, err := GetDatabase()
//return 500
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
return &HTTPError{err, "Can't get database", 500}
}
var share Share
err = db.Preload("Attachments").Where("ID = ?", vars["id"]).First(&share).Error
// return 404
if errors.Is(err, gorm.ErrRecordNotFound) {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(err.Error()))
return
return &HTTPError{err, "Record not found", 404}
}
//return 500
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
return &HTTPError{err, "Can't fetch data", 500}
}
// return 424
if share.IsTemporary == true {
w.WriteHeader(http.StatusFailedDependency)
return
return &HTTPError{err, "Share is not finalized", 424}
}
//return 401
sid, pass, _ := r.BasicAuth()
if sid != share.ID.String() {
w.WriteHeader(http.StatusUnauthorized)
w.Header().Set("WWW-Authenticate", `Basic realm="Please enter the password"`)
w.Write([]byte("401 Unauthorized\n"))
return
return &HTTPError{err, "wrong username", 401}
}
if pass != share.Password {
w.WriteHeader(http.StatusUnauthorized)
w.Header().Set("WWW-Authenticate", `Basic realm="Please enter the password"`)
w.Write([]byte("401 Unauthorized\n"))
return
return &HTTPError{err, "wrong password", 401}
}
// return 200
SendJSON(w, share)
return nil
}
func DownloadFile(w http.ResponseWriter, r *http.Request) {
func DownloadFile(w http.ResponseWriter, r *http.Request) *HTTPError {
fmt.Println("Download file")
vars := mux.Vars(r)
db, err := GetDatabase()
//return 500
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
return &HTTPError{err, "Can't get database", 500}
}
var att Attachment
err = db.Where("id = ?", vars["att"]).First(&att).Error
// return 404
if errors.Is(err, gorm.ErrRecordNotFound) {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(err.Error()))
return
return &HTTPError{err, "Record not found", 404}
}
//return 500
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
return &HTTPError{err, "Can't fetch data", 500}
}
var share Share
err = db.Where("id = ?", att.ShareID).First(&share).Error
//return 500
if errors.Is(err, gorm.ErrRecordNotFound) {
return &HTTPError{err, "Record not found", 404}
}
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
return &HTTPError{err, "Can't fetch data", 500}
}
//return 401
sid, pass, _ := r.BasicAuth()
if sid != share.ID.String() {
w.WriteHeader(http.StatusUnauthorized)
w.Header().Set("WWW-Authenticate", `Basic realm="Please enter the password"`)
w.Write([]byte("401 Unauthorized\n"))
return
return &HTTPError{err, "wrong username", 401}
}
if pass != share.Password {
w.WriteHeader(http.StatusUnauthorized)
w.Header().Set("WWW-Authenticate", `Basic realm="Please enter the password"`)
w.Write([]byte("401 Unauthorized\n"))
return
return &HTTPError{err, "wrong password", 401}
}
// return 200
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", att.Filename))
http.ServeFile(w, r, filepath.Join(config.mediaDir, "data", vars["id"], vars["att"]))
return nil
}
func OpenShare(w http.ResponseWriter, r *http.Request) {
func OpenShare(w http.ResponseWriter, r *http.Request) *HTTPError {
fmt.Println("OpenShare")
db, err := GetDatabase()
//return 500
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
return &HTTPError{err, "Can't get database", 500}
}
// parse body
reqBody, err := ioutil.ReadAll(r.Body)
// return 400
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
return &HTTPError{err, "Request does not contain a valid body", 400}
}
// parse in json
var newShare Share
err = json.Unmarshal(reqBody, &newShare)
// return 500
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
return &HTTPError{err, "Can't parse data", 500}
}
// create temporary db entries
err = db.Create(&newShare).Error
newShare.IsTemporary = true
db.Save(&newShare)
// return 500
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
return &HTTPError{err, "Can't save data", 500}
}
// create temporary dir
err = os.MkdirAll(filepath.Join(config.mediaDir, "temp", newShare.ID.String()), os.ModePerm)
// return 500
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
return &HTTPError{err, "Can't create directory", 500}
}
// return 201
w.WriteHeader(http.StatusCreated)
SendJSON(w, newShare)
return nil
}
func CloseShare(w http.ResponseWriter, r *http.Request) {
func CloseShare(w http.ResponseWriter, r *http.Request) *HTTPError {
fmt.Println("CloseShare")
vars := mux.Vars(r)
db, err := GetDatabase()
//return 500
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
return &HTTPError{err, "Can't get database", 500}
}
// get stuff
var share Share
err = db.Where("id = ?", vars["id"]).First(&share).Error
// return 500
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
if errors.Is(err, gorm.ErrRecordNotFound) {
return &HTTPError{err, "Record not found", 404}
}
// return 424
if share.IsTemporary == false {
w.WriteHeader(http.StatusFailedDependency)
return
if err != nil {
return &HTTPError{err, "Can't fetch data", 500}
}
// move files to permanent location
oldPath := filepath.Join(config.mediaDir, "temp", vars["id"])
newPath := filepath.Join(config.mediaDir, "data", vars["id"])
err = os.Rename(oldPath, newPath)
// return 500
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
return &HTTPError{err, "Can't move directory", 500}
}
// set stuff permanent
share.IsTemporary = false
err = db.Save(&share).Error
// return 500
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
return &HTTPError{err, "Can't edit data", 500}
}
// return 200
SendJSON(w, share)
return nil
}
func UploadAttachment(w http.ResponseWriter, r *http.Request) {
func UploadAttachment(w http.ResponseWriter, r *http.Request) *HTTPError {
fmt.Println("UploadTest")
vars := mux.Vars(r)
db, err := GetDatabase()
//return 500
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
return &HTTPError{err, "Can't get database", 500}
}
// Parse file from body
err = r.ParseMultipartForm(config.chunkSize) // Maximum 10 MB in RAM
// return 400
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
return &HTTPError{err, "Request does not contain a valid body", 400}
}
file, handler, err := r.FormFile("files")
// return 400
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
return &HTTPError{err, "Request does not contain a valid body", 400}
}
defer file.Close() // TODO fehlerbehandlung
// add db entry TODO fehlerbehandlung für die ganze transaction
db.Begin()
var att Attachment
sid, err := uuid.Parse(vars["id"])
if err != nil {
log.Fatal(err)
{
// add db entry TODO fehlerbehandlung für die ganze transaction
db.Begin()
sid, err := uuid.Parse(vars["id"])
if err != nil {
fmt.Println(err)
}
att.ShareID = sid
att.Filename = handler.Filename
att.Filesize = handler.Size
db.Create(&att)
// save file
fileBytes, err := ioutil.ReadAll(file)
if err != nil {
fmt.Println(err)
}
err = ioutil.WriteFile(filepath.Join("media", "temp", vars["id"], att.ID.String()), fileBytes, os.ModePerm)
if err != nil {
fmt.Println(err)
db.Rollback()
}
db.Commit()
}
att.ShareID = sid
att.Filename = handler.Filename
att.Filesize = handler.Size
db.Create(&att)
// save file
fileBytes, err := ioutil.ReadAll(file)
if err != nil {
fmt.Println(err)
}
err = ioutil.WriteFile(filepath.Join("media", "temp", vars["id"], att.ID.String()), fileBytes, os.ModePerm)
if err != nil {
fmt.Println(err)
db.Rollback()
}
db.Commit()
//return 201
w.WriteHeader(http.StatusCreated)
SendJSON(w, att)
return nil
}
......@@ -324,28 +270,19 @@ func ConfigureRoutes() {
router := mux.NewRouter().StrictSlash(true)
handler := cors.Default().Handler(router)
router.HandleFunc("/shares", AllShares).Methods("GET")
router.HandleFunc("/shares", OpenShare).Methods("POST")
router.Handle("/shares", endpointREST(AllShares)).Methods("GET")
router.Handle("/shares", endpointREST(OpenShare)).Methods("POST")
router.HandleFunc("/share/{id}", GetShare).Methods("GET")
router.HandleFunc("/share/{id}", CloseShare).Methods("POST")
router.Handle("/share/{id}", endpointREST(GetShare)).Methods("GET")
router.Handle("/share/{id}", endpointREST(CloseShare)).Methods("POST")
router.HandleFunc("/share/{id}/attachments", UploadAttachment).Methods("POST")
router.Handle("/share/{id}/attachments", endpointREST(UploadAttachment)).Methods("POST")
router.HandleFunc("/share/{id}/attachment/{att}", DownloadFile).Methods("GET")
router.Handle("/share/{id}/attachment/{att}", endpointREST(DownloadFile)).Methods("GET")
log.Fatal(http.ListenAndServe(":6969", handler))
}
func GetDatabase()(*gorm.DB, error) {
dsn := os.Getenv("DATABASE_URI")
db, err := gorm.Open(sqlserver.Open(dsn), &gorm.Config{})
if err != nil {
return nil, errors.New("failed to connect to database")
}
return db, nil
}
func SendJSON(w http.ResponseWriter, res interface{}) {
w.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(w).Encode(res)
......
package main
import (
"errors"
"github.com/google/uuid"
"gorm.io/driver/sqlserver"
"gorm.io/gorm"
"log"
"os"
"time"
)
......@@ -31,19 +33,11 @@ type Attachment struct {
}
func (att *Attachment) BeforeCreate(scope *gorm.DB) error {
uid, err := uuid.NewRandom()
func GetDatabase()(*gorm.DB, error) {
dsn := os.Getenv("DATABASE_URI")
db, err := gorm.Open(sqlserver.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal(err)
return nil, errors.New("failed to connect to database")
}
att.ID = uid
return nil
}
func (sh *Share) BeforeCreate(scope *gorm.DB) error {
uid, err := uuid.NewRandom()
if err != nil {
log.Fatal(err)
}
sh.ID = uid
return nil
return db, nil
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment