Compare commits
14 Commits
de880da152
...
9dd951e0af
Author | SHA1 | Date | |
---|---|---|---|
9dd951e0af | |||
d133b45575 | |||
c9e2cf1a44 | |||
a04cfc2fc3 | |||
801ec1badc | |||
ef63d59931 | |||
a1b89e543c | |||
c9d498d1f4 | |||
48288da414 | |||
2a7121d85c | |||
2f160a8649 | |||
4e8606c44a | |||
ed37cc6af6 | |||
36a21ed859 |
10
.dockerignore
Normal file
10
.dockerignore
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
.git
|
||||||
|
|
||||||
|
ups.json
|
||||||
|
renovate.json
|
||||||
|
|
||||||
|
README.md
|
||||||
|
LICENSE.txt
|
||||||
|
|
||||||
|
docker-compose.example.yml
|
||||||
|
Dockerfile
|
@ -1,8 +1,15 @@
|
|||||||
name: Build and publish the docker image
|
name: build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["custom"]
|
branches:
|
||||||
|
- "custom"
|
||||||
|
paths-ignore:
|
||||||
|
- "README.md"
|
||||||
|
- "LICENSE.txt"
|
||||||
|
- "*.json"
|
||||||
|
- "docker-compose.example.yml"
|
||||||
|
- ".prettierrc"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: git.ngn.tf
|
REGISTRY: git.ngn.tf
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.24.2 AS build
|
FROM golang:1.24.3 AS build
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
# [ngn.tf] | AnonymousOverflow
|
# AnonymousOverflow | frontend for stackoverflow.com
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
A fork of the [AnonymousOverflow](https://github.com/httpjamesm/AnonymousOverflow) project, with my personal changes.
|
A fork of the
|
||||||
|
[AnonymousOverflow](https://github.com/httpjamesm/AnonymousOverflow) project,
|
||||||
|
with my personal changes.
|
||||||
|
2
main.go
2
main.go
@ -29,7 +29,7 @@ func main() {
|
|||||||
|
|
||||||
if os.Getenv("DEV") != "true" {
|
if os.Getenv("DEV") != "true" {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
fmt.Printf("Running in production mode. Listening on %s:%s.", host, port)
|
fmt.Printf("Listening on %s:%s\n", host, port)
|
||||||
}
|
}
|
||||||
|
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"anonymousoverflow/config"
|
"anonymousoverflow/src/utils"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -45,9 +45,8 @@ func Ratelimit() gin.HandlerFunc {
|
|||||||
|
|
||||||
// if they exceed 30 requests in 1 minute, return a 429
|
// if they exceed 30 requests in 1 minute, return a 429
|
||||||
if val.(int) > 30 {
|
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.",
|
"errorMessage": "You have exceeded the request limit. Please try again in a minute.",
|
||||||
"version": config.Version,
|
|
||||||
})
|
})
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"anonymousoverflow/config"
|
|
||||||
"anonymousoverflow/src/utils"
|
"anonymousoverflow/src/utils"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
@ -11,11 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func GetHome(c *gin.Context) {
|
func GetHome(c *gin.Context) {
|
||||||
theme := utils.GetThemeFromEnv()
|
utils.Render(c, 200, "home", nil)
|
||||||
c.HTML(200, "home.html", gin.H{
|
|
||||||
"version": config.Version,
|
|
||||||
"theme": theme,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type urlConversionRequest struct {
|
type urlConversionRequest struct {
|
||||||
@ -62,7 +57,7 @@ func PostHome(c *gin.Context) {
|
|||||||
body := urlConversionRequest{}
|
body := urlConversionRequest{}
|
||||||
|
|
||||||
if err := c.ShouldBind(&body); err != nil {
|
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",
|
"errorMessage": "Invalid request body",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
@ -71,10 +66,8 @@ func PostHome(c *gin.Context) {
|
|||||||
translated := translateUrl(body.URL)
|
translated := translateUrl(body.URL)
|
||||||
|
|
||||||
if translated == "" {
|
if translated == "" {
|
||||||
theme := utils.GetThemeFromEnv()
|
utils.Render(c, 400, "home", gin.H{
|
||||||
c.HTML(400, "home.html", gin.H{
|
|
||||||
"errorMessage": "Invalid stack overflow/exchange URL",
|
"errorMessage": "Invalid stack overflow/exchange URL",
|
||||||
"theme": theme,
|
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,12 @@ package routes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"anonymousoverflow/src/types"
|
"anonymousoverflow/src/types"
|
||||||
|
"anonymousoverflow/src/utils"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-resty/resty/v2"
|
|
||||||
"github.com/golang-jwt/jwt/v4"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -51,16 +51,15 @@ func GetImage(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// download the image
|
// download the image
|
||||||
client := resty.New()
|
res, err := utils.GET(claims.ImageURL)
|
||||||
resp, err := client.R().Get(claims.ImageURL)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.AbortWithStatus(500)
|
c.AbortWithStatus(500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the content type
|
// set the content type
|
||||||
c.Header("Content-Type", resp.Header().Get("Content-Type"))
|
c.Header("Content-Type", res.Header().Get("Content-Type"))
|
||||||
|
|
||||||
// write the image to the response
|
// write the image to the response
|
||||||
c.Writer.Write(resp.Body())
|
c.Writer.Write(res.Body())
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"anonymousoverflow/config"
|
|
||||||
"anonymousoverflow/src/utils"
|
"anonymousoverflow/src/utils"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
@ -14,16 +13,16 @@ func ChangeOptions(c *gin.Context) {
|
|||||||
switch name {
|
switch name {
|
||||||
case "images":
|
case "images":
|
||||||
text := "disabled"
|
text := "disabled"
|
||||||
|
|
||||||
if c.MustGet("disable_images").(bool) {
|
if c.MustGet("disable_images").(bool) {
|
||||||
text = "enabled"
|
text = "enabled"
|
||||||
}
|
}
|
||||||
|
|
||||||
c.SetCookie("disable_images", fmt.Sprintf("%t", !c.MustGet("disable_images").(bool)), 60*60*24*365*10, "/", "", false, true)
|
c.SetCookie("disable_images", fmt.Sprintf("%t", !c.MustGet("disable_images").(bool)), 60*60*24*365*10, "/", "", false, true)
|
||||||
theme := utils.GetThemeFromEnv()
|
utils.Render(c, 200, "home", gin.H{
|
||||||
c.HTML(200, "home.html", gin.H{
|
|
||||||
"successMessage": "Images are now " + text,
|
"successMessage": "Images are now " + text,
|
||||||
"version": config.Version,
|
|
||||||
"theme": theme,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
default:
|
default:
|
||||||
c.String(400, "400 Bad Request")
|
c.String(400, "400 Bad Request")
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package routes
|
|||||||
import (
|
import (
|
||||||
"anonymousoverflow/config"
|
"anonymousoverflow/config"
|
||||||
"anonymousoverflow/src/utils"
|
"anonymousoverflow/src/utils"
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html"
|
"html"
|
||||||
"html/template"
|
"html/template"
|
||||||
@ -15,7 +16,6 @@ import (
|
|||||||
|
|
||||||
"github.com/PuerkitoBio/goquery"
|
"github.com/PuerkitoBio/goquery"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-resty/resty/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var codeBlockRegex = regexp.MustCompile(`(?s)<pre><code>(.+?)<\/code><\/pre>`)
|
var codeBlockRegex = regexp.MustCompile(`(?s)<pre><code>(.+?)<\/code><\/pre>`)
|
||||||
@ -32,7 +32,7 @@ func ViewQuestion(c *gin.Context) {
|
|||||||
|
|
||||||
questionId := c.Param("id")
|
questionId := c.Param("id")
|
||||||
if _, err := strconv.Atoi(questionId); err != nil {
|
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",
|
"errorMessage": "Invalid question ID",
|
||||||
"version": config.Version,
|
"version": config.Version,
|
||||||
})
|
})
|
||||||
@ -53,44 +53,44 @@ func ViewQuestion(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
soLink := fmt.Sprintf("https://%s/questions/%s/%s?answertab=%s", domain, questionId, params.QuestionTitle, params.SoSortValue)
|
soLink := fmt.Sprintf("https://%s/questions/%s/%s?answertab=%s", domain, questionId, params.QuestionTitle, params.SoSortValue)
|
||||||
|
res, err := utils.GET(soLink)
|
||||||
|
|
||||||
resp, err := fetchQuestionData(soLink)
|
if err != nil {
|
||||||
|
fmt.Printf("failed to get %s: %s", soLink, err.Error())
|
||||||
|
|
||||||
if resp.StatusCode() != 200 {
|
utils.Render(c, 500, "home", gin.H{
|
||||||
c.HTML(500, "home.html", gin.H{
|
"errorMessage": fmt.Sprintf("Request to server failed"),
|
||||||
"errorMessage": fmt.Sprintf("Received a non-OK status code %d", resp.StatusCode()),
|
|
||||||
"version": config.Version,
|
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
respBody := resp.String()
|
if res.StatusCode() != 200 {
|
||||||
|
utils.Render(c, 500, "home", gin.H{
|
||||||
|
"errorMessage": fmt.Sprintf("Received a non-OK status code: %d", res.StatusCode()),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
respBodyReader := strings.NewReader(respBody)
|
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(res.Body()))
|
||||||
|
|
||||||
doc, err := goquery.NewDocumentFromReader(respBodyReader)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.HTML(500, "home.html", gin.H{
|
utils.Render(c, 500, "home", gin.H{
|
||||||
"errorMessage": "Unable to parse question data",
|
"errorMessage": "Unable to parse question data",
|
||||||
"version": config.Version,
|
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
newFilteredQuestion, err := extractQuestionData(doc, domain)
|
newFilteredQuestion, err := extractQuestionData(doc, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.HTML(500, "home.html", gin.H{
|
utils.Render(c, 500, "home", gin.H{
|
||||||
"errorMessage": "Failed to extract question data",
|
"errorMessage": "Failed to extract question data",
|
||||||
"version": config.Version,
|
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
answers, err := extractAnswersData(doc, domain)
|
answers, err := extractAnswersData(doc, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.HTML(500, "home.html", gin.H{
|
utils.Render(c, 500, "home", gin.H{
|
||||||
"errorMessage": "Failed to extract answer data",
|
"errorMessage": "Failed to extract answer data",
|
||||||
"version": config.Version,
|
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -101,18 +101,14 @@ func ViewQuestion(c *gin.Context) {
|
|||||||
imagePolicy = "'self'"
|
imagePolicy = "'self'"
|
||||||
}
|
}
|
||||||
|
|
||||||
theme := utils.GetThemeFromEnv()
|
utils.Render(c, 200, "question", gin.H{
|
||||||
|
|
||||||
c.HTML(200, "question.html", gin.H{
|
|
||||||
"question": newFilteredQuestion,
|
"question": newFilteredQuestion,
|
||||||
"answers": answers,
|
"answers": answers,
|
||||||
"imagePolicy": imagePolicy,
|
"imagePolicy": imagePolicy,
|
||||||
"currentUrl": fmt.Sprintf("%s%s", os.Getenv("APP_URL"), c.Request.URL.Path),
|
"currentUrl": fmt.Sprintf("%s%s", os.Getenv("APP_URL"), c.Request.URL.Path),
|
||||||
"sortValue": params.SoSortValue,
|
"sortValue": params.SoSortValue,
|
||||||
"domain": domain,
|
"domain": domain,
|
||||||
"theme": theme,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type viewQuestionInputs struct {
|
type viewQuestionInputs struct {
|
||||||
@ -127,9 +123,8 @@ func parseAndValidateParameters(c *gin.Context) (inputs viewQuestionInputs, err
|
|||||||
|
|
||||||
questionId := c.Param("id")
|
questionId := c.Param("id")
|
||||||
if _, err = strconv.Atoi(questionId); err != nil {
|
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",
|
"errorMessage": "Invalid question ID",
|
||||||
"version": config.Version,
|
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -155,13 +150,6 @@ func parseAndValidateParameters(c *gin.Context) (inputs viewQuestionInputs, err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchQuestionData sends the request to StackOverflow.
|
|
||||||
func fetchQuestionData(soLink string) (resp *resty.Response, err error) {
|
|
||||||
client := resty.New()
|
|
||||||
resp, err = client.R().Get(soLink)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// extractQuestionData parses the HTML document and extracts question data.
|
// extractQuestionData parses the HTML document and extracts question data.
|
||||||
func extractQuestionData(doc *goquery.Document, domain string) (question types.FilteredQuestion, err error) {
|
func extractQuestionData(doc *goquery.Document, domain string) (question types.FilteredQuestion, err error) {
|
||||||
// Extract the question title.
|
// Extract the question title.
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"anonymousoverflow/src/utils"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"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))
|
resp, err := client.R().Get(fmt.Sprintf("https://%s/a/%s/%s", domain, id, answerId))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.HTML(400, "home.html", gin.H{
|
utils.Render(c, 400, "home", gin.H{
|
||||||
"errorMessage": "Unable to fetch stack overflow URL",
|
"errorMessage": "Unable to fetch stack overflow URL",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode() != 302 {
|
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()),
|
"errorMessage": fmt.Sprintf("Unexpected HTTP status from origin: %d", resp.StatusCode()),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
@ -42,4 +42,4 @@ func ReplaceStackOverflowLinks(html string) string {
|
|||||||
// Replace the href attribute value in the anchor tag
|
// Replace the href attribute value in the anchor tag
|
||||||
return strings.Replace(match, hrefMatch[1], newUrl, 1)
|
return strings.Replace(match, hrefMatch[1], newUrl, 1)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
24
src/utils/render.go
Normal file
24
src/utils/render.go
Normal 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)
|
||||||
|
}
|
150
src/utils/request.go
Normal file
150
src/utils/request.go
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"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"`
|
||||||
|
OnlyCookies bool `json:"returnOnlyCookies"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cookie struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Expires time.Time `json:"expires"`
|
||||||
|
Size uint64 `json:"size"`
|
||||||
|
HttpOnly bool `json:"httpOnly"`
|
||||||
|
Secure bool `json:"secure"`
|
||||||
|
Session bool `json:"session"`
|
||||||
|
SameSite string `json:"sameSite"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Solution struct {
|
||||||
|
Status int `json:"status"`
|
||||||
|
Cookies []Cookie `json:"cookies"`
|
||||||
|
Agent string `json:"userAgent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Solution Solution `json:"solution"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cookie) ToCookie() *http.Cookie {
|
||||||
|
ss := http.SameSiteNoneMode
|
||||||
|
|
||||||
|
switch c.SameSite {
|
||||||
|
case "Lax":
|
||||||
|
ss = http.SameSiteLaxMode
|
||||||
|
|
||||||
|
case "Strict":
|
||||||
|
ss = http.SameSiteStrictMode
|
||||||
|
|
||||||
|
case "Default":
|
||||||
|
ss = http.SameSiteDefaultMode
|
||||||
|
}
|
||||||
|
|
||||||
|
return &http.Cookie{
|
||||||
|
Name: c.Name,
|
||||||
|
Value: c.Value,
|
||||||
|
Domain: c.Domain,
|
||||||
|
Path: c.Path,
|
||||||
|
Expires: c.Expires,
|
||||||
|
HttpOnly: c.HttpOnly,
|
||||||
|
Secure: c.Secure,
|
||||||
|
SameSite: ss,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Solution) HttpCookies() []*http.Cookie {
|
||||||
|
cookies := []*http.Cookie{}
|
||||||
|
|
||||||
|
for i := range s.Cookies {
|
||||||
|
cookies = append(cookies, s.Cookies[i].ToCookie())
|
||||||
|
}
|
||||||
|
|
||||||
|
return cookies
|
||||||
|
}
|
||||||
|
|
||||||
|
const USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.3"
|
||||||
|
|
||||||
|
var solution *Solution = nil
|
||||||
|
|
||||||
|
func Solve(target string) error {
|
||||||
|
fsurl := os.Getenv("FLARESOLVER")
|
||||||
|
|
||||||
|
if fsurl == "" {
|
||||||
|
return fmt.Errorf("flaresolver is not configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
fsurl, _ = url.JoinPath(fsurl, "/v1")
|
||||||
|
response := Response{}
|
||||||
|
client := resty.New()
|
||||||
|
|
||||||
|
res, err := client.R().
|
||||||
|
SetBody(Request{
|
||||||
|
Cmd: "request.get",
|
||||||
|
Url: target,
|
||||||
|
MaxTimeout: 40_000,
|
||||||
|
OnlyCookies: true,
|
||||||
|
}).
|
||||||
|
Post(fsurl)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("request failed: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.StatusCode() != 200 {
|
||||||
|
return fmt.Errorf("bad status code: %d", res.StatusCode())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(res.Body(), &response); err != nil {
|
||||||
|
return fmt.Errorf("failed to parse body: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
solution = &response.Solution
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GET(target string, no_retry ...bool) (*resty.Response, error) {
|
||||||
|
client := resty.New()
|
||||||
|
req := client.R()
|
||||||
|
|
||||||
|
if solution != nil {
|
||||||
|
req.SetCookies(solution.HttpCookies())
|
||||||
|
req.SetHeader("User-Agent", solution.Agent)
|
||||||
|
} else {
|
||||||
|
req.SetHeader("User-Agent", USER_AGENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := req.Get(target)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.StatusCode() != http.StatusForbidden {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = Solve(target); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(no_retry) == 0 {
|
||||||
|
return GET(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("solution did not work")
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user