diff --git a/.gitignore b/.gitignore index b0675a0..314373c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ docker-compose.yml .DS_Store *bin -/tmp \ No newline at end of file +/tmp diff --git a/main.go b/main.go index b7bb84f..8b16302 100644 --- a/main.go +++ b/main.go @@ -50,6 +50,8 @@ func main() { r.GET("/", routes.GetHome) r.POST("/", routes.PostHome) + r.GET("/a/:id", routes.RedirectShortenedOverflowURL) + r.GET("/questions/:id", func(c *gin.Context) { // redirect user to the question with the title c.Redirect(302, fmt.Sprintf("/questions/%s/placeholder", c.Param("id"))) diff --git a/src/routes/home.go b/src/routes/home.go index c129e2f..27b511a 100644 --- a/src/routes/home.go +++ b/src/routes/home.go @@ -35,10 +35,14 @@ func PostHome(c *gin.Context) { soLink := body.URL + // remove the www. + soLink = strings.ReplaceAll(soLink, "www.", "") + // validate URL isStackOverflow := strings.HasPrefix(soLink, "https://stackoverflow.com/questions/") + isShortenedStackOverflow := strings.HasPrefix(soLink, "https://stackoverflow.com/a/") isStackExchange := stackExchangeRegex.MatchString(soLink) - if !isStackExchange && !isStackOverflow { + if !isStackExchange && !isStackOverflow && !isShortenedStackOverflow { c.HTML(400, "home.html", gin.H{ "errorMessage": "Invalid stack overflow/exchange URL", "theme": c.MustGet("theme").(string), @@ -47,7 +51,7 @@ func PostHome(c *gin.Context) { } // if stack overflow, trim https://stackoverflow.com - if isStackOverflow { + if isStackOverflow || isShortenedStackOverflow { c.Redirect(302, strings.TrimPrefix(soLink, "https://stackoverflow.com")) return } diff --git a/src/routes/shortened.go b/src/routes/shortened.go new file mode 100644 index 0000000..ed1ad1e --- /dev/null +++ b/src/routes/shortened.go @@ -0,0 +1,44 @@ +package routes + +import ( + "fmt" + "net/http" + "os" + + "github.com/gin-gonic/gin" + "github.com/go-resty/resty/v2" +) + +func RedirectShortenedOverflowURL(c *gin.Context) { + id := c.Param("id") + + // fetch the stack overflow URL + client := resty.New() + client.SetRedirectPolicy( + resty.RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }), + ) + + resp, err := client.R().Get(fmt.Sprintf("https://www.stackoverflow.com/a/%s", id)) + if err != nil { + c.HTML(400, "home.html", gin.H{ + "errorMessage": "Unable to fetch stack overflow URL", + "theme": c.MustGet("theme").(string), + }) + return + } + + if resp.StatusCode() != 302 { + c.HTML(400, "home.html", gin.H{ + "errorMessage": fmt.Sprintf("Unexpected HTTP status from origin: %d", resp.StatusCode()), + "theme": c.MustGet("theme").(string), + }) + return + } + + // get the redirect URL + location := resp.Header().Get("Location") + + c.Redirect(302, fmt.Sprintf("%s%s", os.Getenv("APP_URL"), location)) +}