package kennel import ( "encoding/json" "log" "net/http" "strings" "git.hatecomputers.club/hatecomputers/hatecomputers.club/adapters/files" "git.hatecomputers.club/hatecomputers/hatecomputers.club/api/types" "git.hatecomputers.club/hatecomputers/hatecomputers.club/database" "git.hatecomputers.club/hatecomputers/hatecomputers.club/utils" ) const MaxCatSize = 1024 * 100 // 60KB const CatsPath = "cats/" const CatsPrefix = "/uploads/cats/" const DefaultCatSpritesheet = "/static/img/cat_spritesheets/default.gif" const MaxUserCats = 15 func ListUserCatsContinuation(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { return func(success types.Continuation, failure types.Continuation) types.ContinuationChain { userID := context.User.ID cats, err := database.GetUserKennelCats(context.DBConn, userID) if err != nil { log.Println(err) resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } (*context.TemplateData)["Cats"] = cats return success(context, req, resp) } } func CreateCatContinuation(fileAdapter files.FilesAdapter, maxUserCats int, maxCatSize int, catsPath string, catsPrefix string, defaultCatSpritesheet string) func(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { return func(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { return func(success types.Continuation, failure types.Continuation) types.ContinuationChain { formErrors := types.BannerMessages{ Messages: []string{}, } numCats, err := database.CountUserKennelCats(context.DBConn, context.User.ID) if err != nil { log.Println(err) resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } if numCats >= maxUserCats { formErrors.Messages = append(formErrors.Messages, "max cats reached for user") } err = req.ParseMultipartForm(int64(maxCatSize)) if err != nil { formErrors.Messages = append(formErrors.Messages, "cat spritesheet too large") } catID := utils.RandomId() spritesheetPath := catsPrefix + catID if len(formErrors.Messages) == 0 { file, _, err := req.FormFile("spritesheet") if file != nil && err != nil { formErrors.Messages = append(formErrors.Messages, "error uploading spritesheet") } else if file != nil { defer file.Close() reader := http.MaxBytesReader(resp, file, int64(maxCatSize)) defer reader.Close() _, err = fileAdapter.CreateFile(catsPath+catID, reader) if err != nil { log.Println(err) formErrors.Messages = append(formErrors.Messages, "error saving spritesheet (is it too big?)") } } else if file == nil && err != nil { spritesheetPath = defaultCatSpritesheet } } link := req.FormValue("link") description := req.FormValue("description") name := req.FormValue("name") cat := &database.KennelCat{ ID: catID, UserID: context.User.ID, Name: name, Link: link, Description: description, Spritesheet: spritesheetPath, } formErrors.Messages = append(formErrors.Messages, validateCat(cat)...) if len(formErrors.Messages) == 0 { _, err := database.SaveKennelCat(context.DBConn, cat) if err != nil { log.Println(err) formErrors.Messages = append(formErrors.Messages, "failed to save cat") } } if len(formErrors.Messages) > 0 { (*context.TemplateData)["Error"] = formErrors (*context.TemplateData)["CatForm"] = cat resp.WriteHeader(http.StatusBadRequest) return failure(context, req, resp) } formSuccess := types.BannerMessages{ Messages: []string{"cat added."}, } (*context.TemplateData)["Success"] = formSuccess return success(context, req, resp) } } } func RemoveCatContinuation(fileAdapter files.FilesAdapter, catsPath string) func(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { return func(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { return func(success types.Continuation, failure types.Continuation) types.ContinuationChain { catID := req.FormValue("id") cat, err := database.GetKennelCat(context.DBConn, catID) if err != nil { log.Println(err) resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } if cat == nil || cat.UserID != context.User.ID { resp.WriteHeader(http.StatusUnauthorized) return failure(context, req, resp) } err = database.DeleteKennelCat(context.DBConn, catID) if err != nil { log.Println(err) resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } err = fileAdapter.DeleteFile(catsPath + catID) if err != nil && fileAdapter.FileExists(catsPath+catID) { log.Println(err) resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } return success(context, req, resp) } } } func RingContinuation(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { return func(success types.Continuation, failure types.Continuation) types.ContinuationChain { order := req.URL.Query().Get("order") if order == "random" { kennelCat, err := database.GetRandomKennelCat(context.DBConn) if err != nil { log.Println(err) resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } http.Redirect(resp, req, kennelCat.Link, http.StatusFound) return success(context, req, resp) } id := req.URL.Query().Get("id") if id == "" { resp.WriteHeader(http.StatusBadRequest) return failure(context, req, resp) } if order != "random" && order != "next" && order != "prev" { kennelCat, err := database.GetKennelCat(context.DBConn, id) if err != nil { log.Println(err) resp.WriteHeader(http.StatusNotFound) return failure(context, req, resp) } http.Redirect(resp, req, kennelCat.Link, http.StatusFound) return success(context, req, resp) } nextCat, err := database.GetNextKennelCat(context.DBConn, id, order == "next") if err != nil { log.Println(err) resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } http.Redirect(resp, req, nextCat.Link, http.StatusFound) return success(context, req, resp) } } func GetKennelContinuation(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { return func(success types.Continuation, failure types.Continuation) types.ContinuationChain { cats, err := database.GetKennel(context.DBConn) if err != nil { log.Println(err) resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } json, err := json.Marshal(cats) if err != nil { log.Println(err) resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } resp.Header().Set("Content-Type", "application/json") resp.Write(json) return success(context, req, resp) } } func validateCat(cat *database.KennelCat) []string { errors := []string{} if cat.Name == "" { errors = append(errors, "name is required") } if cat.Link == "" { errors = append(errors, "link is required") } if !strings.HasPrefix(cat.Link, "http://") && !strings.HasPrefix(cat.Link, "https://") { errors = append(errors, "link must be a valid URL") } if cat.Description == "" { errors = append(errors, "description is required") } if len(cat.Description) > 100 { errors = append(errors, "description must be less than 100 characters") } return errors }