hatecomputers.club/api/kennel/kennel.go

239 lines
7.6 KiB
Go

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
}