package kennel import ( "log" "net/http" "strconv" "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 GetCatStateAtContinuation(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { return func(success types.Continuation, failure types.Continuation) types.ContinuationChain { atLong, err := strconv.ParseInt(req.FormValue("at"), 10, 64) if err != nil { log.Println(err) resp.WriteHeader(http.StatusBadRequest) return failure(context, req, resp) } cats, err := database.GetKennelStateAt(context.DBConn, atLong) if err != nil { log.Println(err) resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } (*context.TemplateData)["EncodedState"] = cats.EncodedState 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 }