Compare commits

...

8 Commits

Author SHA1 Message Date
httpjamesm 487a62e7aa chore: version 1.8 2023-02-20 14:23:42 -05:00
httpjamesm abfd0cfb6f feat: SO/SE converter
refactor: rework some author logic
2023-02-20 14:17:24 -05:00
httpjamesm 63a30ab1cf feat: don't count static toward ratelimit 2023-02-20 13:56:44 -05:00
httpjamesm f1370c29b6 fix: pass down domain to comment for author URLs 2023-02-20 13:54:09 -05:00
httpjamesm d935ce72f6 fix: pass domain to question template 2023-02-20 13:49:54 -05:00
httpjamesm 45644a0503 feat: get stackexchange posts
fix: comment author distinguishing
2023-02-16 10:25:24 -05:00
httpjamesm f81b726612 chore: version 1.7.1 2023-02-14 20:25:26 -05:00
httpjamesm c55b8b6ed5 fix: redeclare correct answer author vars 2023-02-14 20:25:16 -05:00
8 changed files with 80 additions and 33 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
package config package config
var Version = "1.7" var Version = "1.8"
+9
View File
@@ -59,6 +59,15 @@ func main() {
// redirect user to the answer with the title // redirect user to the answer with the title
c.Redirect(302, fmt.Sprintf("/questions/%s/%s#%s", c.Param("id"), c.Param("title"), c.Param("answerId"))) c.Redirect(302, fmt.Sprintf("/questions/%s/%s#%s", c.Param("id"), c.Param("title"), c.Param("answerId")))
}) })
r.GET("/exchange/:sub/questions/:id/:title", routes.ViewQuestion)
r.GET("/exchange/:sub/questions/:id", func(c *gin.Context) {
// redirect user to the question with the title
c.Redirect(302, fmt.Sprintf("/exchange/%s/questions/%s/placeholder", c.Param("sub"), c.Param("id")))
})
r.GET("/exchange/:sub/questions/:id/:title/:answerId", func(c *gin.Context) {
// redirect user to the answer with the title
c.Redirect(302, fmt.Sprintf("/exchange/%s/questions/%s/%s#%s", c.Param("sub"), c.Param("id"), c.Param("title"), c.Param("answerId")))
})
r.GET("/proxy", routes.GetImage) r.GET("/proxy", routes.GetImage)
+7
View File
@@ -2,6 +2,7 @@ package middleware
import ( import (
"anonymousoverflow/config" "anonymousoverflow/config"
"strings"
"sync" "sync"
"time" "time"
@@ -12,6 +13,12 @@ var ipMap = sync.Map{}
func Ratelimit() gin.HandlerFunc { func Ratelimit() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
if strings.HasPrefix(c.Request.URL.Path, "/static") {
c.Next()
return
}
ip := c.ClientIP() ip := c.ClientIP()
// log request count as the value, ip as key // log request count as the value, ip as key
+16 -4
View File
@@ -3,6 +3,7 @@ package routes
import ( import (
"anonymousoverflow/config" "anonymousoverflow/config"
"fmt" "fmt"
"regexp"
"strings" "strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -19,6 +20,8 @@ type urlConversionRequest struct {
URL string `form:"url" binding:"required"` URL string `form:"url" binding:"required"`
} }
var stackExchangeRegex = regexp.MustCompile(`https://(.+).stackexchange.com/questions/`)
func PostHome(c *gin.Context) { func PostHome(c *gin.Context) {
body := urlConversionRequest{} body := urlConversionRequest{}
@@ -33,15 +36,24 @@ func PostHome(c *gin.Context) {
soLink := body.URL soLink := body.URL
// validate URL // validate URL
if !strings.HasPrefix(soLink, "https://stackoverflow.com/questions/") { isStackOverflow := strings.HasPrefix(soLink, "https://stackoverflow.com/questions/")
isStackExchange := stackExchangeRegex.MatchString(soLink)
if !isStackExchange && !isStackOverflow {
c.HTML(400, "home.html", gin.H{ c.HTML(400, "home.html", gin.H{
"errorMessage": "Invalid stack overflow URL", "errorMessage": "Invalid stack overflow/exchange URL",
"theme": c.MustGet("theme").(string), "theme": c.MustGet("theme").(string),
}) })
return return
} }
// redirect to the proxied thread // if stack overflow, trim https://stackoverflow.com
c.Redirect(302, fmt.Sprintf("/questions/%s", strings.TrimPrefix(soLink, "https://stackoverflow.com/questions/"))) if isStackOverflow {
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))))
} }
+40 -22
View File
@@ -53,7 +53,15 @@ func ViewQuestion(c *gin.Context) {
soSortValue = soSortValues["votes"] soSortValue = soSortValues["votes"]
} }
soLink := fmt.Sprintf("https://stackoverflow.com/questions/%s/%s?answertab=%s", questionId, questionTitle, soSortValue) sub := c.Param("sub")
domain := "stackoverflow.com"
if sub != "" {
domain = fmt.Sprintf("%s.stackexchange.com", sub)
}
soLink := fmt.Sprintf("https://%s/questions/%s/%s?answertab=%s", domain, questionId, questionTitle, soSortValue)
resp, err := client.R().Get(soLink) resp, err := client.R().Get(soLink)
if err != nil { if err != nil {
@@ -129,7 +137,7 @@ func ViewQuestion(c *gin.Context) {
newFilteredQuestion.ShortenedBody = shortenedBody newFilteredQuestion.ShortenedBody = shortenedBody
comments := utils.FindAndReturnComments(questionBodyParentHTML, questionPostLayout) comments := utils.FindAndReturnComments(questionBodyParentHTML, domain, questionPostLayout)
newFilteredQuestion.Comments = comments newFilteredQuestion.Comments = comments
// parse any code blocks and highlight them // parse any code blocks and highlight them
@@ -170,24 +178,33 @@ func ViewQuestion(c *gin.Context) {
userDetails := questionMetadata.Find("div.user-details") userDetails := questionMetadata.Find("div.user-details")
questionAuthor := "" questionAuthor := ""
questionAuthorURL := "" questionAuthorURL := fmt.Sprintf("https://%s", domain)
// check if the question has been edited
isQuestionEdited := false
questionMetadata.Find("a.js-gps-track").Each(func(i int, s *goquery.Selection) {
if strings.Contains(s.Text(), "edited") {
isQuestionEdited = true
return
}
})
userDetails.Find("a").Each(func(i int, s *goquery.Selection) { userDetails.Find("a").Each(func(i int, s *goquery.Selection) {
// get the second // if question has been edited, the author is the second element.
if i == 0 {
if s.Text() != "" { if isQuestionEdited {
// if it's not been edited, it means it's the first if i == 1 {
questionAuthor = s.Text() questionAuthor = s.Text()
questionAuthorURL, _ = s.Attr("href") questionAuthorURL += s.AttrOr("href", "")
return
}
} else {
// otherwise it's the first element
if i == 0 {
questionAuthor = s.Text()
questionAuthorURL += s.AttrOr("href", "")
return return
} }
}
// otherwise it's the second element
if i == 1 {
questionAuthor = s.Text()
questionAuthorURL, _ = s.Attr("href")
return
} }
}) })
@@ -213,17 +230,17 @@ func ViewQuestion(c *gin.Context) {
answerFooter := s.Find("div.mt24") answerFooter := s.Find("div.mt24")
answerAuthorURL := "" answerAuthorURL := fmt.Sprintf("https://%s", domain)
answerAuthorName := "" answerAuthorName := ""
answerTimestamp := "" answerTimestamp := ""
answerFooter.Find("div.post-signature").Each(func(i int, s *goquery.Selection) { answerFooter.Find("div.post-signature").Each(func(i int, s *goquery.Selection) {
questionAuthorDetails := questionMetadata.Find("div.user-details") answerAuthorDetails := s.Find("div.user-details")
if questionAuthorDetails.Length() > 0 { if answerAuthorDetails.Length() > 0 {
questionAuthor := questionAuthorDetails.Find("a").First() questionAuthor := answerAuthorDetails.Find("a").First()
newFilteredQuestion.AuthorName = html.EscapeString(questionAuthor.Text()) answerAuthorName = html.EscapeString(questionAuthor.Text())
newFilteredQuestion.AuthorURL = html.EscapeString(questionAuthor.AttrOr("href", "")) answerAuthorURL += html.EscapeString(questionAuthor.AttrOr("href", ""))
} }
answerTimestamp = html.EscapeString(s.Find("span.relativetime").Text()) answerTimestamp = html.EscapeString(s.Find("span.relativetime").Text())
@@ -248,7 +265,7 @@ func ViewQuestion(c *gin.Context) {
answerBodyHTML = strings.Replace(answerBodyHTML, codeBlock, highlightedCodeBlock, 1) answerBodyHTML = strings.Replace(answerBodyHTML, codeBlock, highlightedCodeBlock, 1)
} }
comments = utils.FindAndReturnComments(answerBodyHTML, postLayout) comments = utils.FindAndReturnComments(answerBodyHTML, domain, postLayout)
newFilteredAnswer.Comments = comments newFilteredAnswer.Comments = comments
newFilteredAnswer.Body = template.HTML(utils.ReplaceImgTags(answerBodyHTML)) newFilteredAnswer.Body = template.HTML(utils.ReplaceImgTags(answerBodyHTML))
@@ -269,6 +286,7 @@ func ViewQuestion(c *gin.Context) {
"theme": c.MustGet("theme").(string), "theme": c.MustGet("theme").(string),
"currentUrl": fmt.Sprintf("%s/questions/%s/%s", os.Getenv("APP_URL"), questionId, questionTitle), "currentUrl": fmt.Sprintf("%s/questions/%s/%s", os.Getenv("APP_URL"), questionId, questionTitle),
"sortValue": sortValue, "sortValue": sortValue,
"domain": domain,
}) })
} }
+4 -3
View File
@@ -2,12 +2,13 @@ package utils
import ( import (
"anonymousoverflow/src/types" "anonymousoverflow/src/types"
"fmt"
"html/template" "html/template"
"github.com/PuerkitoBio/goquery" "github.com/PuerkitoBio/goquery"
) )
func FindAndReturnComments(inHtml string, postLayout *goquery.Selection) (comments []types.FilteredComment) { func FindAndReturnComments(inHtml, domain string, postLayout *goquery.Selection) (comments []types.FilteredComment) {
commentsComponent := postLayout.Find("div.js-post-comments-component") commentsComponent := postLayout.Find("div.js-post-comments-component")
@@ -34,7 +35,7 @@ func FindAndReturnComments(inHtml string, postLayout *goquery.Selection) (commen
return return
} }
commentAuthorURL := "" commentAuthorURL := fmt.Sprintf("https://%s", domain)
commentAuthor := commentBody.Find("span.comment-user") commentAuthor := commentBody.Find("span.comment-user")
if commentAuthor.Length() == 0 { if commentAuthor.Length() == 0 {
@@ -43,7 +44,7 @@ func FindAndReturnComments(inHtml string, postLayout *goquery.Selection) (commen
return return
} }
commentAuthorURL = commentAuthor.AttrOr("href", "") commentAuthorURL += commentAuthor.AttrOr("href", "")
} }
commentTimestamp := commentBody.Find("span.relativetime-clean").Text() commentTimestamp := commentBody.Find("span.relativetime-clean").Text()
+1 -1
View File
@@ -19,7 +19,7 @@
<div class="comment-author"> <div class="comment-author">
Commented {{ $comment.Timestamp }} by Commented {{ $comment.Timestamp }} by
<a <a
href="https://stackoverflow.com{{ $comment.AuthorURL }}" href="{{ $comment.AuthorURL }}"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
>{{ $comment.AuthorName }}</a >{{ $comment.AuthorName }}</a
+2 -2
View File
@@ -30,7 +30,7 @@
<p class="timestamp"> <p class="timestamp">
Asked {{ .question.Timestamp }} by Asked {{ .question.Timestamp }} by
<a <a
href="https://stackoverflow.com{{ .question.AuthorURL }}" href="{{ .question.AuthorURL }}"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
>{{ .question.AuthorName }}</a >{{ .question.AuthorName }}</a
@@ -87,7 +87,7 @@
<div class="answer-author"> <div class="answer-author">
Answered {{ $answer.Timestamp }} by Answered {{ $answer.Timestamp }} by
<a <a
href="https://stackoverflow.com{{ $answer.AuthorURL }}" href="{{ $answer.AuthorURL }}"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
>{{ $answer.AuthorName }}</a >{{ $answer.AuthorName }}</a