mirror of
https://github.com/httpjamesm/AnonymousOverflow.git
synced 2024-12-06 19:16:43 +01:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 262aa01568 | |||
| f4ecc9be6a | |||
| 669187dd4d | |||
| 9e806186b7 | |||
| 2ef7e05f4b | |||
| 4760681c55 |
+1
-1
@@ -2,4 +2,4 @@
|
||||
docker-compose.yml
|
||||
.DS_Store
|
||||
*bin
|
||||
/tmp
|
||||
/tmp
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 4,
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
}
|
||||
+14
-9
@@ -1,19 +1,24 @@
|
||||
FROM golang:1.18-alpine
|
||||
|
||||
RUN apk add musl-dev
|
||||
RUN apk add libc-dev
|
||||
RUN apk add gcc
|
||||
FROM golang:1.19-alpine as build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY go.mod ./
|
||||
COPY go.sum ./
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
|
||||
RUN go mod download
|
||||
|
||||
COPY ./ /app
|
||||
COPY . .
|
||||
|
||||
RUN go build -o /anonymousoverflow
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
RUN go build -o anonymousoverflow
|
||||
|
||||
FROM scratch
|
||||
|
||||
COPY --from=build /app/anonymousoverflow /anonymousoverflow
|
||||
COPY templates /templates
|
||||
COPY public /public
|
||||
COPY --from=build /etc/ssl/certs /etc/ssl/certs
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
package config
|
||||
|
||||
var Version = "1.8.3"
|
||||
var Version = "1.9.0"
|
||||
|
||||
@@ -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")))
|
||||
|
||||
+3
-4
@@ -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,7 +26,6 @@ a {
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
|
||||
html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -45,9 +44,9 @@ html {
|
||||
.icon img {
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
padding: .25rem;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
details {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
+13
-5
@@ -19,9 +19,17 @@ body {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.parent {
|
||||
max-width: 90%;
|
||||
width: fit-content;
|
||||
@media (orientation: landscape) {
|
||||
.parent {
|
||||
max-width: 50%;
|
||||
width: fit-content;
|
||||
}
|
||||
}
|
||||
@media (orientation: portrait) {
|
||||
.parent {
|
||||
max-width: 90%;
|
||||
width: fit-content;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
@@ -31,7 +39,7 @@ body {
|
||||
|
||||
.card-tags {
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.card-tags .tag {
|
||||
@@ -94,7 +102,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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
+16
-7
@@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
@@ -14,20 +15,28 @@ import (
|
||||
var imgTagRegex = regexp.MustCompile(`<img[^>]*\s+src\s*=\s*"(.*?)"[^>]*>`)
|
||||
|
||||
func ReplaceImgTags(inHtml string) string {
|
||||
// find all img tags
|
||||
imgTags := imgTagRegex.FindAllString(inHtml, -1)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
var mu sync.Mutex
|
||||
|
||||
for _, imgTag := range imgTags {
|
||||
// parse the src="" attribute
|
||||
srcRegex := regexp.MustCompile(`src\s*=\s*"(.*?)"`)
|
||||
src := srcRegex.FindStringSubmatch(imgTag)[1]
|
||||
wg.Add(1)
|
||||
go func(imgTag string) {
|
||||
defer wg.Done()
|
||||
srcRegex := regexp.MustCompile(`src\s*=\s*"(.*?)"`)
|
||||
src := srcRegex.FindStringSubmatch(imgTag)[1]
|
||||
|
||||
authToken, _ := generateImageProxyAuth(src)
|
||||
authToken, _ := generateImageProxyAuth(src)
|
||||
|
||||
// replace the img tag with a proxied url
|
||||
inHtml = strings.Replace(inHtml, imgTag, fmt.Sprintf(`<img src="%s/proxy?auth=%s">`, os.Getenv("APP_URL"), authToken), 1)
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
inHtml = strings.Replace(inHtml, imgTag, fmt.Sprintf(`<img src="%s/proxy?auth=%s">`, os.Getenv("APP_URL"), authToken), 1)
|
||||
}(imgTag)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return inHtml
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user