Compare commits

..

5 Commits

Author SHA1 Message Date
httpjamesm 073cc57e56 Merge branch 'main' into redirect-short-links 2023-08-21 02:01:48 -04:00
httpjamesm 1d8feea290 fix: remove log 2023-08-21 00:56:53 -04:00
httpjamesm 7fa26139bc fix: tell resty not to follow redirect 2023-08-21 00:56:39 -04:00
httpjamesm 6c7f8248e7 feat: accept shortened URLs in converter 2023-08-21 00:51:38 -04:00
httpjamesm 1db9b465d7 feat: redirect shortened URLs 2023-08-21 00:50:29 -04:00
7 changed files with 62 additions and 25 deletions
+1 -1
View File
@@ -2,4 +2,4 @@
docker-compose.yml
.DS_Store
*bin
/tmp
/tmp
-6
View File
@@ -1,6 +0,0 @@
{
"trailingComma": "es5",
"tabWidth": 4,
"semi": false,
"singleQuote": true
}
+2
View File
@@ -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")))
+4 -3
View File
@@ -10,7 +10,7 @@
--link-color: #92adff;
}
[data-theme='light'] {
[data-theme="light"] {
--main-bg: rgb(219, 219, 219);
--text-color: #000;
--muted-text-color: #636363;
@@ -26,6 +26,7 @@ a {
color: var(--link-color);
}
html {
margin: 0;
padding: 0;
@@ -44,9 +45,9 @@ html {
.icon img {
background: white;
border-radius: 50%;
padding: 0.25rem;
padding: .25rem;
}
details {
cursor: pointer;
}
}
+5 -13
View File
@@ -19,17 +19,9 @@ body {
box-sizing: border-box;
}
@media (orientation: landscape) {
.parent {
max-width: 50%;
width: fit-content;
}
}
@media (orientation: portrait) {
.parent {
max-width: 90%;
width: fit-content;
}
.parent {
max-width: 90%;
width: fit-content;
}
.header {
@@ -39,7 +31,7 @@ body {
.card-tags {
display: flex;
gap: 0.5rem;
gap: .5rem;
}
.card-tags .tag {
@@ -102,7 +94,7 @@ img {
margin-bottom: 1rem;
background-color: var(--meta-bg);
color: var(--text-color);
display: flex;
justify-content: space-between;
align-items: center;
+6 -2
View File
@@ -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
}
+44
View File
@@ -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))
}