internal/api/form.go
package api
import (
"fmt"
"log"
"net/http"
"github.com/containrrr/shoutrrr"
"github.com/dorianim/formrecevr/internal/config"
"github.com/dorianim/formrecevr/internal/template"
"github.com/gin-gonic/gin"
)
// ResponseBody is the body of a response
type ResponseBody struct {
Message string
}
// PostForm registers the route POST /api/v1/accounts
func PostForm(router *gin.RouterGroup) {
router.POST("/:formID", func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
formConfig := getForm(c.Param("formID"))
if formConfig == nil {
c.JSON(http.StatusNotFound, ResponseBody{Message: "Form not found"})
return
}
var err error
switch c.ContentType() {
case "application/x-www-form-urlencoded":
err = c.Request.ParseForm()
case "multipart/form-data":
err = c.Request.ParseMultipartForm(6400000)
default:
log.Printf("Unsupported Content-Type: %s", c.ContentType())
c.JSON(http.StatusBadRequest, ResponseBody{Message: "Unsupported Content-Type"})
return
}
if err != nil {
c.JSON(http.StatusBadRequest, ResponseBody{Message: "Malformed form data"})
log.Printf("Error parsing submited form: %v", err)
return
}
if formConfig.Turnstile.Enabled {
fmt.Println("Validating captcha")
var ip = c.Request.RemoteAddr
if config.GetConfig().Listen.UseForwardedHeaders {
ip = c.Request.Header.Get("X-Forwarded-For")
}
success, err := ValidateTurnstileToken(c.Request.Form.Get("cf-turnstile-response"), formConfig.Turnstile.SecretKey, ip)
if err != nil {
c.JSON(http.StatusInternalServerError, ResponseBody{Message: "Internal server error"})
log.Printf("Error validating captcha: %v", err)
return
}
if !success {
c.JSON(http.StatusBadRequest, ResponseBody{Message: "Invalid captcha"})
log.Printf("Invalid captcha!")
return
}
}
atLeastOneSuccess := false
for _, targetConfig := range formConfig.Targets {
if !targetConfig.Enabled {
continue
}
templateData := make(map[string]interface{})
for k, v := range c.Request.Form {
if k == "cf-turnstile-response" {
continue
}
if len(v) == 1 {
templateData[k] = v[0]
} else {
templateData[k] = v
}
}
templateData["params"] = targetConfig.Params
targetData, err := template.ExecuteTemplateFromFile(targetConfig.Template, templateData)
if err != nil {
log.Printf("Error processing body template %s: %v", targetConfig.Template, err)
continue
}
shoutrrrURL, err := template.ExecuteTemplateFromString(targetConfig.ShoutrrrURL, templateData)
if err != nil {
log.Printf("Error processing URL template %s: %v", targetConfig.ShoutrrrURL, err)
continue
}
err = shoutrrr.Send(shoutrrrURL, targetData)
if err != nil {
log.Printf("Error sending form to %s: %v", shoutrrrURL, err)
continue
}
atLeastOneSuccess = true
}
if !atLeastOneSuccess {
c.JSON(http.StatusInternalServerError, ResponseBody{Message: "Internal server error"})
return
}
c.JSON(http.StatusOK, ResponseBody{Message: "Success"})
})
}
func getForm(formID string) *config.FormConfig {
config := config.GetConfig()
for _, form := range config.Forms {
fmt.Println(form.ID, formID, form.Enabled)
if form.ID == formID && form.Enabled {
return form
}
}
return nil
}