add request util to properly use flareresolver
Some checks failed
Build and publish the docker image / build (push) Failing after 1m22s

Signed-off-by: ngn <ngn@ngn.tf>
This commit is contained in:
ngn 2025-05-02 16:49:38 +03:00
parent 4e8606c44a
commit 2f160a8649
Signed by: ngn
GPG Key ID: A3654DF5AD9F641D
8 changed files with 115 additions and 88 deletions

View File

@ -2,6 +2,7 @@ package middleware
import (
"anonymousoverflow/config"
"anonymousoverflow/src/utils"
"os"
"strings"
"sync"
@ -45,9 +46,8 @@ func Ratelimit() gin.HandlerFunc {
// if they exceed 30 requests in 1 minute, return a 429
if val.(int) > 30 {
c.HTML(429, "home.html", gin.H{
utils.Render(c, 429, "home", gin.H{
"errorMessage": "You have exceeded the request limit. Please try again in a minute.",
"version": config.Version,
})
c.Abort()
return

View File

@ -1,7 +1,6 @@
package routes
import (
"anonymousoverflow/config"
"anonymousoverflow/src/utils"
"fmt"
"regexp"
@ -11,11 +10,7 @@ import (
)
func GetHome(c *gin.Context) {
theme := utils.GetThemeFromEnv()
c.HTML(200, "home.html", gin.H{
"version": config.Version,
"theme": theme,
})
utils.Render(c, 200, "home", nil)
}
type urlConversionRequest struct {
@ -62,7 +57,7 @@ func PostHome(c *gin.Context) {
body := urlConversionRequest{}
if err := c.ShouldBind(&body); err != nil {
c.HTML(400, "home.html", gin.H{
utils.Render(c, 400, "home", gin.H{
"errorMessage": "Invalid request body",
})
return
@ -71,10 +66,8 @@ func PostHome(c *gin.Context) {
translated := translateUrl(body.URL)
if translated == "" {
theme := utils.GetThemeFromEnv()
c.HTML(400, "home.html", gin.H{
utils.Render(c, 400, "home", gin.H{
"errorMessage": "Invalid stack overflow/exchange URL",
"theme": theme,
})
return
}

View File

@ -2,12 +2,12 @@ package routes
import (
"anonymousoverflow/src/types"
"anonymousoverflow/src/utils"
"fmt"
"os"
"time"
"github.com/gin-gonic/gin"
"github.com/go-resty/resty/v2"
"github.com/golang-jwt/jwt/v4"
)
@ -51,33 +51,15 @@ func GetImage(c *gin.Context) {
}
// download the image
var resp *resty.Response = nil
client := resty.New()
if flareurl := os.Getenv("FLARERESOLVER"); flareurl == "" {
resp, err = client.R().Get(claims.ImageURL)
} else {
client.R().SetHeader("Content-Type", "application/json")
client.R().SetBody(struct {
Cmd string `json:"cmd"`
Url string `json:"url"`
MaxTimeout int `json:"maxTimeout"`
}{
Cmd: "request.get",
Url: claims.ImageURL,
MaxTimeout: 60000,
})
resp, err = client.R().Post(claims.ImageURL)
}
body, _, headers, err := utils.GET(claims.ImageURL)
if err != nil {
c.AbortWithStatus(500)
return
}
// set the content type
c.Header("Content-Type", resp.Header().Get("Content-Type"))
c.Header("Content-Type", headers.Get("Content-Type"))
// write the image to the response
c.Writer.Write(resp.Body())
c.Writer.Write(body)
}

View File

@ -1,7 +1,6 @@
package routes
import (
"anonymousoverflow/config"
"anonymousoverflow/src/utils"
"fmt"
@ -14,16 +13,16 @@ func ChangeOptions(c *gin.Context) {
switch name {
case "images":
text := "disabled"
if c.MustGet("disable_images").(bool) {
text = "enabled"
}
c.SetCookie("disable_images", fmt.Sprintf("%t", !c.MustGet("disable_images").(bool)), 60*60*24*365*10, "/", "", false, true)
theme := utils.GetThemeFromEnv()
c.HTML(200, "home.html", gin.H{
utils.Render(c, 200, "home", gin.H{
"successMessage": "Images are now " + text,
"version": config.Version,
"theme": theme,
})
default:
c.String(400, "400 Bad Request")
}

View File

@ -3,6 +3,7 @@ package routes
import (
"anonymousoverflow/config"
"anonymousoverflow/src/utils"
"bytes"
"fmt"
"html"
"html/template"
@ -15,7 +16,6 @@ import (
"github.com/PuerkitoBio/goquery"
"github.com/gin-gonic/gin"
"github.com/go-resty/resty/v2"
)
var codeBlockRegex = regexp.MustCompile(`(?s)<pre><code>(.+?)<\/code><\/pre>`)
@ -32,7 +32,7 @@ func ViewQuestion(c *gin.Context) {
questionId := c.Param("id")
if _, err := strconv.Atoi(questionId); err != nil {
c.HTML(400, "home.html", gin.H{
utils.Render(c, 400, "home", gin.H{
"errorMessage": "Invalid question ID",
"version": config.Version,
})
@ -53,44 +53,37 @@ func ViewQuestion(c *gin.Context) {
}
soLink := fmt.Sprintf("https://%s/questions/%s/%s?answertab=%s", domain, questionId, params.QuestionTitle, params.SoSortValue)
body, code, _, err := utils.GET(soLink)
resp, err := fetchQuestionData(soLink)
if resp.StatusCode() != 200 {
c.HTML(500, "home.html", gin.H{
"errorMessage": fmt.Sprintf("Received a non-OK status code %d", resp.StatusCode()),
"version": config.Version,
if code != 200 {
utils.Render(c, 500, "home", gin.H{
"errorMessage": fmt.Sprintf("Received a non-OK status code %d", code),
})
return
}
respBody := resp.String()
respBodyReader := strings.NewReader(respBody)
respBodyReader := bytes.NewReader(body)
doc, err := goquery.NewDocumentFromReader(respBodyReader)
if err != nil {
c.HTML(500, "home.html", gin.H{
utils.Render(c, 500, "home", gin.H{
"errorMessage": "Unable to parse question data",
"version": config.Version,
})
return
}
newFilteredQuestion, err := extractQuestionData(doc, domain)
if err != nil {
c.HTML(500, "home.html", gin.H{
utils.Render(c, 500, "home", gin.H{
"errorMessage": "Failed to extract question data",
"version": config.Version,
})
return
}
answers, err := extractAnswersData(doc, domain)
if err != nil {
c.HTML(500, "home.html", gin.H{
utils.Render(c, 500, "home", gin.H{
"errorMessage": "Failed to extract answer data",
"version": config.Version,
})
return
}
@ -101,18 +94,14 @@ func ViewQuestion(c *gin.Context) {
imagePolicy = "'self'"
}
theme := utils.GetThemeFromEnv()
c.HTML(200, "question.html", gin.H{
utils.Render(c, 200, "question", gin.H{
"question": newFilteredQuestion,
"answers": answers,
"imagePolicy": imagePolicy,
"currentUrl": fmt.Sprintf("%s%s", os.Getenv("APP_URL"), c.Request.URL.Path),
"sortValue": params.SoSortValue,
"domain": domain,
"theme": theme,
})
}
type viewQuestionInputs struct {
@ -127,9 +116,8 @@ func parseAndValidateParameters(c *gin.Context) (inputs viewQuestionInputs, err
questionId := c.Param("id")
if _, err = strconv.Atoi(questionId); err != nil {
c.HTML(400, "home.html", gin.H{
utils.Render(c, 400, "home", gin.H{
"errorMessage": "Invalid question ID",
"version": config.Version,
})
return
}
@ -155,29 +143,6 @@ func parseAndValidateParameters(c *gin.Context) (inputs viewQuestionInputs, err
return
}
// fetchQuestionData sends the request to StackOverflow.
func fetchQuestionData(soLink string) (*resty.Response, error) {
flareurl := ""
client := resty.New()
if flareurl = os.Getenv("FLARERESOLVER"); flareurl == "" {
return client.R().Get(soLink)
}
client.R().SetHeader("Content-Type", "application/json")
client.R().SetBody(struct {
Cmd string `json:"cmd"`
Url string `json:"url"`
MaxTimeout int `json:"maxTimeout"`
}{
Cmd: "request.get",
Url: soLink,
MaxTimeout: 60000,
})
return client.R().Post(flareurl)
}
// extractQuestionData parses the HTML document and extracts question data.
func extractQuestionData(doc *goquery.Document, domain string) (question types.FilteredQuestion, err error) {
// Extract the question title.

View File

@ -1,6 +1,7 @@
package routes
import (
"anonymousoverflow/src/utils"
"fmt"
"net/http"
"os"
@ -31,14 +32,14 @@ func RedirectShortenedOverflowURL(c *gin.Context) {
}
resp, err := client.R().Get(fmt.Sprintf("https://%s/a/%s/%s", domain, id, answerId))
if err != nil {
c.HTML(400, "home.html", gin.H{
utils.Render(c, 400, "home", gin.H{
"errorMessage": "Unable to fetch stack overflow URL",
})
return
}
if resp.StatusCode() != 302 {
c.HTML(400, "home.html", gin.H{
utils.Render(c, 400, "home", gin.H{
"errorMessage": fmt.Sprintf("Unexpected HTTP status from origin: %d", resp.StatusCode()),
})
return

24
src/utils/render.go Normal file
View File

@ -0,0 +1,24 @@
package utils
import (
"anonymousoverflow/config"
"github.com/gin-gonic/gin"
)
func Render(c *gin.Context, code int, temp string, _data ...gin.H) {
var data gin.H = nil
if len(_data) > 0 {
data = _data[0]
}
if data == nil {
data = gin.H{}
}
data["version"] = config.Version
data["theme"] = GetThemeFromEnv()
c.HTML(code, temp+".html", data)
}

63
src/utils/request.go Normal file
View File

@ -0,0 +1,63 @@
package utils
import (
"net/http"
"os"
"github.com/go-resty/resty/v2"
)
// https://github.com/FlareSolverr/FlareSolverr#-requestget
type request struct {
Cmd string `json:"cmd"`
Url string `json:"url"`
MaxTimeout int `json:"maxTimeout"`
}
type solution struct {
Status int `json:"status"`
Response []byte `json:"response"`
Headers map[string]string `json:"headers"`
}
type response struct {
Solution solution `json:"solution"`
}
func GET(url string) ([]byte, int, http.Header, error) {
var (
client *resty.Client = resty.New()
res *resty.Response = nil
frurl string = ""
err error
)
if frurl = os.Getenv("FLARERESOLVER"); frurl == "" {
if res, err := client.R().Get(url); err != nil {
return nil, 0, nil, err
} else {
return res.Body(), res.StatusCode(), res.Header(), nil
}
}
client.R().SetHeader("Content-Type", "application/json")
client.R().SetBody(request{
Cmd: "request.get",
Url: url,
MaxTimeout: 60000,
})
client.R().SetResult(&response{})
if res, err = client.R().Post(url); err != nil {
return nil, 0, nil, err
}
response := res.Result().(*response)
headers := http.Header{}
for k, v := range response.Solution.Headers {
headers.Add(k, v)
}
return response.Solution.Response, response.Solution.Status, headers, nil
}