package api import ( "encoding/json" "fmt" "log" "net/http" "strings" "git.hatecomputers.club/hatecomputers/hatecomputers.club/database" "git.hatecomputers.club/hatecomputers/hatecomputers.club/utils" ) type HcaptchaArgs struct { SiteKey string } func validateGuestbookEntry(entry *database.GuestbookEntry) []string { errors := []string{} if entry.Name == "" { errors = append(errors, "name is required") } if entry.Message == "" { errors = append(errors, "message is required") } messageLength := len(entry.Message) if messageLength < 10 || messageLength > 500 { errors = append(errors, "message must be between 10 and 500 characters") } newLines := strings.Count(entry.Message, "\n") if newLines > 10 { errors = append(errors, "message cannot contain more than 10 new lines") } return errors } func SignGuestbookContinuation(context *RequestContext, req *http.Request, resp http.ResponseWriter) ContinuationChain { return func(success Continuation, failure Continuation) ContinuationChain { name := req.FormValue("name") message := req.FormValue("message") hCaptchaResponse := req.FormValue("h-captcha-response") formErrors := FormError{ Errors: []string{}, } if hCaptchaResponse == "" { formErrors.Errors = append(formErrors.Errors, "hCaptcha is required") } entry := &database.GuestbookEntry{ ID: utils.RandomId(), Name: name, Message: message, } formErrors.Errors = append(formErrors.Errors, validateGuestbookEntry(entry)...) if len(formErrors.Errors) > 0 { (*context.TemplateData)["FormError"] = formErrors return failure(context, req, resp) } err := verifyHCaptcha(context.Args.HcaptchaSecret, hCaptchaResponse) if err != nil { log.Println(err) resp.WriteHeader(http.StatusBadRequest) return failure(context, req, resp) } _, err = database.SaveGuestbookEntry(context.DBConn, entry) if err != nil { log.Println(err) resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } return success(context, req, resp) } } func ListGuestbookContinuation(context *RequestContext, req *http.Request, resp http.ResponseWriter) ContinuationChain { return func(success Continuation, failure Continuation) ContinuationChain { entries, err := database.GetGuestbookEntries(context.DBConn) if err != nil { log.Println(err) resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } (*context.TemplateData)["GuestbookEntries"] = entries return success(context, req, resp) } } func HcaptchaArgsContinuation(context *RequestContext, req *http.Request, resp http.ResponseWriter) ContinuationChain { return func(success Continuation, failure Continuation) ContinuationChain { (*context.TemplateData)["HcaptchaArgs"] = HcaptchaArgs{ SiteKey: context.Args.HcaptchaSiteKey, } log.Println(context.Args.HcaptchaSiteKey) return success(context, req, resp) } } func verifyHCaptcha(secret, response string) error { verifyURL := "https://hcaptcha.com/siteverify" body := strings.NewReader("secret=" + secret + "&response=" + response) req, err := http.NewRequest("POST", verifyURL, body) if err != nil { return err } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") client := &http.Client{} resp, err := client.Do(req) if err != nil { return err } jsonResponse := struct { Success bool `json:"success"` }{} err = json.NewDecoder(resp.Body).Decode(&jsonResponse) if err != nil { return err } if !jsonResponse.Success { return fmt.Errorf("hcaptcha verification failed") } defer resp.Body.Close() return nil }