Implement external exchanges (#99)
* Implement external exchanges * test: translateUrl test cases * fix: double slash bug in translateUrl --------- Co-authored-by: httpjamesm <github@httpjames.space>
This commit is contained in:
parent
89126a7377
commit
94032f4f90
@ -20,7 +20,41 @@ type urlConversionRequest struct {
|
||||
URL string `form:"url" binding:"required"`
|
||||
}
|
||||
|
||||
var stackExchangeRegex = regexp.MustCompile(`https://(.+).stackexchange.com/questions/`)
|
||||
var coreRegex = regexp.MustCompile(`(?:https?://)?(?:www\.)?([^/]+)(/(?:questions|q|a)/.+)`)
|
||||
|
||||
// Will return `nil` if `rawUrl` is invalid.
|
||||
func translateUrl(rawUrl string) string {
|
||||
coreMatches := coreRegex.FindStringSubmatch(rawUrl)
|
||||
if coreMatches == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
domain := coreMatches[1]
|
||||
rest := coreMatches[2]
|
||||
|
||||
exchange := ""
|
||||
if domain == "stackoverflow.com" {
|
||||
// No exchange parameter needed.
|
||||
} else if sub, found := strings.CutSuffix(domain, ".stackexchange.com"); found {
|
||||
if sub == "" {
|
||||
return ""
|
||||
} else if strings.Contains(sub, ".") {
|
||||
// Anything containing dots is interpreted as a full domain, so we use the correct full domain.
|
||||
exchange = domain
|
||||
} else {
|
||||
exchange = sub
|
||||
}
|
||||
} else {
|
||||
exchange = domain
|
||||
}
|
||||
|
||||
// Ensure we properly format the return string to avoid double slashes
|
||||
if exchange == "" {
|
||||
return rest
|
||||
} else {
|
||||
return fmt.Sprintf("/exchange/%s%s", exchange, rest)
|
||||
}
|
||||
}
|
||||
|
||||
func PostHome(c *gin.Context) {
|
||||
body := urlConversionRequest{}
|
||||
@ -33,16 +67,9 @@ func PostHome(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
soLink := body.URL
|
||||
translated := translateUrl(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/") || strings.HasPrefix(soLink, "https://stackoverflow.com/q/")
|
||||
isStackExchange := stackExchangeRegex.MatchString(soLink)
|
||||
if !isStackExchange && !isStackOverflow && !isShortenedStackOverflow {
|
||||
if translated == "" {
|
||||
c.HTML(400, "home.html", gin.H{
|
||||
"errorMessage": "Invalid stack overflow/exchange URL",
|
||||
"theme": c.MustGet("theme").(string),
|
||||
@ -50,14 +77,5 @@ func PostHome(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// if stack overflow, trim https://stackoverflow.com
|
||||
if isStackOverflow || isShortenedStackOverflow {
|
||||
c.Redirect(302, strings.TrimPrefix(soLink, "https://stackoverflow.com"))
|
||||
return
|
||||
}
|
||||
|
||||
// if stack exchange, extract the subdomain
|
||||
sub := stackExchangeRegex.FindStringSubmatch(soLink)[1]
|
||||
|
||||
c.Redirect(302, fmt.Sprintf("/exchange/%s/%s", sub, strings.TrimPrefix(soLink, fmt.Sprintf("https://%s.stackexchange.com", sub))))
|
||||
c.Redirect(302, translated)
|
||||
}
|
||||
|
32
src/routes/home_test.go
Normal file
32
src/routes/home_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTranslateUrl(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// Test with a Valid StackOverflow URL
|
||||
assert.Equal("/questions/example-question", translateUrl("https://stackoverflow.com/questions/example-question"), "StackOverflow URL should not be modified")
|
||||
|
||||
// Test with Complex Subdomain
|
||||
assert.Equal("/exchange/meta.math.stackexchange.com/q/example-question", translateUrl("https://meta.math.stackexchange.com/q/example-question"), "Complex StackExchange subdomain should be used as full exchange")
|
||||
|
||||
// Test with Non-StackExchange Domain
|
||||
assert.Equal("/exchange/example.com/questions/example-question", translateUrl("https://example.com/questions/example-question"), "Non-StackExchange domain should be detected as exchange")
|
||||
|
||||
// Test with Invalid URL
|
||||
assert.Equal("", translateUrl("This is not a URL"), "Invalid URL should return an empty string")
|
||||
|
||||
// Test with Empty String
|
||||
assert.Equal("", translateUrl(""), "Empty string should return an empty string")
|
||||
|
||||
// Test with Missing Path
|
||||
assert.Equal("", translateUrl("https://stackoverflow.com"), "URL without path should return an empty string")
|
||||
|
||||
// Test with Valid URL but Root Domain for StackExchange
|
||||
assert.Equal("", translateUrl("https://stackexchange.com"), "Root StackExchange domain without subdomain should return an empty string")
|
||||
}
|
@ -47,7 +47,9 @@ func ViewQuestion(c *gin.Context) {
|
||||
|
||||
domain := "stackoverflow.com"
|
||||
|
||||
if params.Sub != "" {
|
||||
if strings.Contains(params.Sub, ".") {
|
||||
domain = params.Sub
|
||||
} else if params.Sub != "" {
|
||||
domain = fmt.Sprintf("%s.stackexchange.com", params.Sub)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user