Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ea7f253e8 | ||
|
|
bf2529753d | ||
|
|
e3389d2df3 | ||
|
|
b786796595 | ||
|
|
377a577c67 | ||
|
|
6eb7b481d8 | ||
|
|
2f1de95e06 | ||
|
|
7ae1a29932 | ||
|
|
63dcaeba3c | ||
|
|
62e03a384a | ||
|
|
7f4d749c55 | ||
|
|
e748cb09a5 | ||
|
|
c98e49f2b3 | ||
|
|
3e3eebcdc2 | ||
|
|
45a3fe2adf | ||
|
|
ec7f2089fc | ||
|
|
622143211c | ||
|
|
796ffd1c87 | ||
|
|
210837a404 | ||
|
|
aa1caaca98 | ||
|
|
ca26f6d88d |
2
.github/workflows/release-binaries.yaml
vendored
2
.github/workflows/release-binaries.yaml
vendored
@@ -22,7 +22,7 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: Set version
|
name: Set version
|
||||||
run: |
|
run: |
|
||||||
echo -n $(git describe --tags --abbrev=0) > cmd/VERSION
|
echo -n $(git describe --tags --abbrev=0) > handlers/VERSION
|
||||||
-
|
-
|
||||||
name: Set up Go
|
name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
|
|||||||
2
.github/workflows/release-docker.yaml
vendored
2
.github/workflows/release-docker.yaml
vendored
@@ -42,7 +42,7 @@ jobs:
|
|||||||
- name: Set version
|
- name: Set version
|
||||||
id: version
|
id: version
|
||||||
run: |
|
run: |
|
||||||
echo ${GITHUB_REF#refs/tags/v} > cmd/VERSION
|
echo ${GITHUB_REF#refs/tags/v} > handlers/VERSION
|
||||||
|
|
||||||
# Install the cosign tool except on PR
|
# Install the cosign tool except on PR
|
||||||
# https://github.com/sigstore/cosign-installer
|
# https://github.com/sigstore/cosign-installer
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ before:
|
|||||||
builds:
|
builds:
|
||||||
-
|
-
|
||||||
main: cmd/main.go
|
main: cmd/main.go
|
||||||
binary: kubero
|
binary: ladder
|
||||||
env:
|
env:
|
||||||
- CGO_ENABLED=0
|
- CGO_ENABLED=0
|
||||||
goos:
|
goos:
|
||||||
|
|||||||
@@ -21,3 +21,5 @@ RUN apt update && apt install -y ca-certificates && rm -rf /var/lib/apt/lists/*
|
|||||||
#EXPOSE 8080
|
#EXPOSE 8080
|
||||||
|
|
||||||
#ENTRYPOINT ["/usr/bin/dumb-init", "--"]
|
#ENTRYPOINT ["/usr/bin/dumb-init", "--"]
|
||||||
|
|
||||||
|
CMD ["sh", "-c", "/app/ladder"]
|
||||||
54
README.md
54
README.md
@@ -1,5 +1,5 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="public/pigeon.svg" width="100px">
|
<img src="assets/pigeon.svg" width="100px">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1 align="center">Ladder</h1>
|
<h1 align="center">Ladder</h1>
|
||||||
@@ -10,22 +10,29 @@
|
|||||||
|
|
||||||
Freedom of information is an essential pillar of democracy and informed decision-making. While media organizations have legitimate financial interests, it is crucial to strike a balance between profitability and the public's right to access information. The proliferation of paywalls raises concerns about the erosion of this fundamental freedom, and it is imperative for society to find innovative ways to preserve access to vital information without compromising the sustainability of journalism. In a world where knowledge should be shared and not commodified, paywalls should be critically examined to ensure that they do not undermine the principles of an open and informed society.
|
Freedom of information is an essential pillar of democracy and informed decision-making. While media organizations have legitimate financial interests, it is crucial to strike a balance between profitability and the public's right to access information. The proliferation of paywalls raises concerns about the erosion of this fundamental freedom, and it is imperative for society to find innovative ways to preserve access to vital information without compromising the sustainability of journalism. In a world where knowledge should be shared and not commodified, paywalls should be critically examined to ensure that they do not undermine the principles of an open and informed society.
|
||||||
|
|
||||||
Some site might have missing images or other formating issues. This is due to the fact that the site using javascript or CSS to load the images/JS/CSS. This is a limitation of this proxy. If you prefer a full experience, please concider buying a subscription for the site.
|
Certain sites may display missing images or encounter formatting issues. This can be attributed to the site's reliance on JavaScript or CSS for image and resource loading, which presents a limitation when accessed through this proxy. If you prefer a full experience, please concider buying a subscription for the site.
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
- [x] Bypass Paywalls
|
- [x] Bypass Paywalls
|
||||||
- [x] Remove CORS
|
- [x] Remove CORS headers from responses, assets, and images ...
|
||||||
- [x] Keep Site browsable
|
- [x] Keep site browsable
|
||||||
- [x] Docker Container
|
- [x] API
|
||||||
- [x] Linux Binary
|
- [x] Show RAW HTML
|
||||||
- [x] Mac OS Binary
|
- [x] Docker container
|
||||||
- [x] Windows Binary (Untested)
|
- [x] Linux binary
|
||||||
|
- [x] Mac OS binary
|
||||||
|
- [x] Windows binary (untested)
|
||||||
|
- [x] Remove most of the ads (unexpected side effect)
|
||||||
|
- [x] Basic Auth
|
||||||
|
- [x] Disable logs
|
||||||
|
- [x] Custom User Agent
|
||||||
|
- [x] Custom X-Forwarded-For IP
|
||||||
|
|
||||||
## How to use
|
## Installation
|
||||||
|
|
||||||
### Binary
|
### Binary
|
||||||
1) Download Binary
|
1) Download binary [here](https://github.com/kubero-dev/ladder/releases/latest)
|
||||||
2) Run Binary
|
2) Unpack and run the binary `./ladder`
|
||||||
3) Open Browser (Default: http://localhost:8080)
|
3) Open Browser (Default: http://localhost:8080)
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
@@ -35,10 +42,28 @@ docker run -p 8080:8080 -d --name ladder ghcr.io/kubero-dev/ladder:latest
|
|||||||
|
|
||||||
### Docker Compose
|
### Docker Compose
|
||||||
```bash
|
```bash
|
||||||
wget https://raw.githubusercontent.com/kubero-dev/ladder/main/docker-compose.yml
|
curl https://raw.githubusercontent.com/kubero-dev/ladder/main/docker-compose.yaml --output docker-compose.yaml
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Browser
|
||||||
|
1) Open Browser (Default: http://localhost:8080)
|
||||||
|
2) Enter URL
|
||||||
|
3) Press Enter
|
||||||
|
|
||||||
|
Or direct by appending the URL to the end of the proxy URL:
|
||||||
|
http://localhost:8080/https://www.google.com
|
||||||
|
|
||||||
|
### API
|
||||||
|
```bash
|
||||||
|
curl -X GET "http://localhost:8080/api/https://www.google.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
### RAW
|
||||||
|
http://localhost:8080/raw/https://www.google.com
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
### Environment Variables
|
### Environment Variables
|
||||||
@@ -47,3 +72,8 @@ docker-compose up -d
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `PORT` | Port to listen on | `8080` |
|
| `PORT` | Port to listen on | `8080` |
|
||||||
| `PREFORK` | Spawn multiple server instances | `false` |
|
| `PREFORK` | Spawn multiple server instances | `false` |
|
||||||
|
| `USER_AGENT` | User agent to emulate | `Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)` |
|
||||||
|
| `X_FORWARDED_FOR` | IP forwarder address | `66.249.66.1` |
|
||||||
|
| `USERPASS` | Enables Basic Auth, format `admin:123456` | `` |
|
||||||
|
| `LOG_URLS` | Log fetched URL's | `true` |
|
||||||
|
| `DISABLE_FORM` | Disables URL Form Frontpage | `false` |
|
||||||
|
|||||||
@@ -159,8 +159,11 @@
|
|||||||
});
|
});
|
||||||
document.getElementById('inputForm').addEventListener('submit', function (e) {
|
document.getElementById('inputForm').addEventListener('submit', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const inputValue = document.getElementById('inputField').value;
|
let url = document.getElementById('inputField').value;
|
||||||
window.location.href = '/' + inputValue;
|
if (url.indexOf('http') === -1) {
|
||||||
|
url = 'https://' + url;
|
||||||
|
}
|
||||||
|
window.location.href = '/' + url;
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
BIN
cmd/favicon.ico
Normal file
BIN
cmd/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
34
cmd/main.go
34
cmd/main.go
@@ -1,14 +1,22 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "embed"
|
||||||
|
|
||||||
"ladder/handlers"
|
"ladder/handlers"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/gofiber/fiber/v2/middleware/basicauth"
|
||||||
|
"github.com/gofiber/fiber/v2/middleware/favicon"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:embed favicon.ico
|
||||||
|
var faviconData string
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
prefork, _ := strconv.ParseBool(os.Getenv("PREFORK"))
|
prefork, _ := strconv.ParseBool(os.Getenv("PREFORK"))
|
||||||
@@ -18,8 +26,32 @@ func main() {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
userpass := os.Getenv("USERPASS")
|
||||||
|
if userpass != "" {
|
||||||
|
userpass := strings.Split(userpass, ":")
|
||||||
|
app.Use(basicauth.New(basicauth.Config{
|
||||||
|
Users: map[string]string{
|
||||||
|
userpass[0]: userpass[1],
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Use(favicon.New(favicon.Config{
|
||||||
|
Data: []byte(faviconData),
|
||||||
|
URL: "/favicon.ico",
|
||||||
|
}))
|
||||||
|
|
||||||
|
if os.Getenv("NOLOGS") != "true" {
|
||||||
|
app.Use(func(c *fiber.Ctx) error {
|
||||||
|
log.Println(c.Method(), c.Path())
|
||||||
|
return c.Next()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
app.Get("/", handlers.Form)
|
app.Get("/", handlers.Form)
|
||||||
app.Get("debug/*", handlers.Debug)
|
|
||||||
|
app.Get("raw/*", handlers.Raw)
|
||||||
|
app.Get("api/*", handlers.Api)
|
||||||
app.Get("/*", handlers.ProxySite)
|
app.Get("/*", handlers.ProxySite)
|
||||||
|
|
||||||
port := os.Getenv("PORT")
|
port := os.Getenv("PORT")
|
||||||
|
|||||||
@@ -5,12 +5,15 @@ services:
|
|||||||
container_name: ladder
|
container_name: ladder
|
||||||
build: .
|
build: .
|
||||||
#restart: always
|
#restart: always
|
||||||
#command: tail -f /dev/null
|
#command: sh -c ./ladder
|
||||||
command: sh -c ./ladder
|
|
||||||
environment:
|
environment:
|
||||||
- PORT=8080
|
- PORT=8080
|
||||||
- PREFORK=true
|
#- PREFORK=true
|
||||||
#- GODEBUG=netdns=go+4
|
#- X_FORWARDED_FOR=66.249.66.1
|
||||||
|
#- USER_AGENT=Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
|
||||||
|
#- USERPASS=foo:bar
|
||||||
|
#- LOG_URLS=true
|
||||||
|
#- GODEBUG=netdns=go
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
deploy:
|
deploy:
|
||||||
|
|||||||
8
go.mod
8
go.mod
@@ -2,17 +2,23 @@ module ladder
|
|||||||
|
|
||||||
go 1.21.1
|
go 1.21.1
|
||||||
|
|
||||||
|
require github.com/gofiber/fiber/v2 v2.50.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.0.6 // indirect
|
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||||
github.com/gofiber/fiber/v2 v2.50.0
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/google/uuid v1.4.0 // indirect
|
github.com/google/uuid v1.4.0 // indirect
|
||||||
github.com/klauspost/compress v1.17.2 // indirect
|
github.com/klauspost/compress v1.17.2 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/rivo/uniseg v0.4.4 // indirect
|
github.com/rivo/uniseg v0.4.4 // indirect
|
||||||
|
github.com/stretchr/objx v0.5.0 // indirect
|
||||||
|
github.com/stretchr/testify v1.8.4 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.50.0 // indirect
|
github.com/valyala/fasthttp v1.50.0 // indirect
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||||
golang.org/x/sys v0.13.0 // indirect
|
golang.org/x/sys v0.13.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
17
go.sum
17
go.sum
@@ -1,5 +1,8 @@
|
|||||||
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
||||||
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/gofiber/fiber/v2 v2.50.0 h1:ia0JaB+uw3GpNSCR5nvC5dsaxXjRU5OEu36aytx+zGw=
|
github.com/gofiber/fiber/v2 v2.50.0 h1:ia0JaB+uw3GpNSCR5nvC5dsaxXjRU5OEu36aytx+zGw=
|
||||||
github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw=
|
github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw=
|
||||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||||
@@ -13,9 +16,19 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
|||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
|
github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
|
||||||
@@ -26,3 +39,7 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
58
handlers/api.go
Normal file
58
handlers/api.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed VERSION
|
||||||
|
var version string
|
||||||
|
|
||||||
|
func Api(c *fiber.Ctx) error {
|
||||||
|
// Get the url from the URL
|
||||||
|
urlQuery := c.Params("*")
|
||||||
|
|
||||||
|
queries := c.Queries()
|
||||||
|
body, req, resp, err := fetchSite(urlQuery, queries)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR:", err)
|
||||||
|
c.SendStatus(500)
|
||||||
|
return c.SendString(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
response := Response{
|
||||||
|
Version: version,
|
||||||
|
Body: body,
|
||||||
|
}
|
||||||
|
response.Request.Headers = make([]interface{}, 0)
|
||||||
|
for k, v := range req.Header {
|
||||||
|
response.Request.Headers = append(response.Request.Headers, map[string]string{
|
||||||
|
"key": k,
|
||||||
|
"value": v[0],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Response.Headers = make([]interface{}, 0)
|
||||||
|
for k, v := range resp.Header {
|
||||||
|
response.Response.Headers = append(response.Response.Headers, map[string]string{
|
||||||
|
"key": k,
|
||||||
|
"value": v[0],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
Request struct {
|
||||||
|
Headers []interface{} `json:"headers"`
|
||||||
|
} `json:"request"`
|
||||||
|
Response struct {
|
||||||
|
Headers []interface{} `json:"headers"`
|
||||||
|
} `json:"response"`
|
||||||
|
}
|
||||||
44
handlers/api.test.go
Normal file
44
handlers/api.test.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// BEGIN: 7d5e1f7c7d5e
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestApi(t *testing.T) {
|
||||||
|
app := fiber.New()
|
||||||
|
app.Get("/api/*", Api)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
url string
|
||||||
|
expectedStatus int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid url",
|
||||||
|
url: "https://www.google.com",
|
||||||
|
expectedStatus: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid url",
|
||||||
|
url: "invalid-url",
|
||||||
|
expectedStatus: http.StatusBadRequest,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/api/"+tt.url, nil)
|
||||||
|
resp, err := app.Test(req)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, tt.expectedStatus, resp.StatusCode)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// END: 7d5e1f7c7d5e
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Debug(c *fiber.Ctx) error {
|
|
||||||
// Get the url from the URL
|
|
||||||
urlQuery := c.Params("*")
|
|
||||||
|
|
||||||
u, err := url.Parse(urlQuery)
|
|
||||||
if err != nil {
|
|
||||||
return c.SendString(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println(u.String())
|
|
||||||
|
|
||||||
// Fetch the site
|
|
||||||
client := &http.Client{}
|
|
||||||
req, _ := http.NewRequest("GET", u.String(), nil)
|
|
||||||
req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)")
|
|
||||||
req.Header.Set("X-Forwarded-For", "66.249.66.1")
|
|
||||||
req.Header.Set("Referer", u.String())
|
|
||||||
req.Header.Set("Host", u.Host)
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return c.SendString(err.Error())
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
bodyB, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("ERROR", err)
|
|
||||||
return c.SendString(err.Error())
|
|
||||||
}
|
|
||||||
body := rewriteHtml(bodyB, u)
|
|
||||||
return c.SendString(body)
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,20 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import "github.com/gofiber/fiber/v2"
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
func Form(c *fiber.Ctx) error {
|
func Form(c *fiber.Ctx) error {
|
||||||
|
if os.Getenv("DISABLE_FORM") == "true" {
|
||||||
|
c.Set("Content-Type", "text/html")
|
||||||
|
c.SendStatus(fiber.StatusNotFound)
|
||||||
|
return c.SendString("Form Disabled")
|
||||||
|
} else {
|
||||||
c.Set("Content-Type", "text/html")
|
c.Set("Content-Type", "text/html")
|
||||||
return c.SendString(html)
|
return c.SendString(html)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const html = `
|
const html = `
|
||||||
@@ -169,8 +179,11 @@ const html = `
|
|||||||
});
|
});
|
||||||
document.getElementById('inputForm').addEventListener('submit', function (e) {
|
document.getElementById('inputForm').addEventListener('submit', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const inputValue = document.getElementById('inputField').value;
|
let url = document.getElementById('inputField').value;
|
||||||
window.location.href = '/' + inputValue;
|
if (url.indexOf('http') === -1) {
|
||||||
|
url = 'https://' + url;
|
||||||
|
}
|
||||||
|
window.location.href = '/' + url;
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -6,49 +6,73 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var UserAgent = getenv("USER_AGENT", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)")
|
||||||
|
var ForwardedFor = getenv("X_FORWARDED_FOR", "66.249.66.1")
|
||||||
|
|
||||||
func ProxySite(c *fiber.Ctx) error {
|
func ProxySite(c *fiber.Ctx) error {
|
||||||
// Get the url from the URL
|
// Get the url from the URL
|
||||||
urlQuery := c.Params("*")
|
url := c.Params("*")
|
||||||
|
|
||||||
u, err := url.Parse(urlQuery)
|
queries := c.Queries()
|
||||||
|
body, _, resp, err := fetchSite(url, queries)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("ERROR", err)
|
log.Println("ERROR:", err)
|
||||||
c.SendStatus(500)
|
c.SendStatus(fiber.StatusInternalServerError)
|
||||||
return c.SendString(err.Error())
|
return c.SendString(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println(u.String())
|
c.Set("Content-Type", resp.Header.Get("Content-Type"))
|
||||||
|
return c.SendString(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchSite(urlpath string, queries map[string]string) (string, *http.Request, *http.Response, error) {
|
||||||
|
|
||||||
|
urlQuery := "?"
|
||||||
|
if len(queries) > 0 {
|
||||||
|
for k, v := range queries {
|
||||||
|
urlQuery += k + "=" + v + "&"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
urlQuery = strings.TrimSuffix(urlQuery, "&")
|
||||||
|
urlQuery = strings.TrimSuffix(urlQuery, "?")
|
||||||
|
|
||||||
|
u, err := url.Parse(urlpath)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Getenv("DEBUG ") == "true" {
|
||||||
|
log.Println(u.String() + urlQuery)
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch the site
|
// Fetch the site
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
req, _ := http.NewRequest("GET", u.String(), nil)
|
req, _ := http.NewRequest("GET", u.String()+urlQuery, nil)
|
||||||
req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)")
|
req.Header.Set("User-Agent", UserAgent)
|
||||||
req.Header.Set("X-Forwarded-For", "66.249.66.1")
|
req.Header.Set("X-Forwarded-For", ForwardedFor)
|
||||||
req.Header.Set("Referer", u.String())
|
req.Header.Set("Referer", u.String())
|
||||||
req.Header.Set("Host", u.Host)
|
req.Header.Set("Host", u.Host)
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.SendString(err.Error())
|
return "", nil, nil, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
bodyB, err := io.ReadAll(resp.Body)
|
bodyB, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("ERROR", err)
|
return "", nil, nil, err
|
||||||
c.SendStatus(500)
|
|
||||||
return c.SendString(err.Error())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body := rewriteHtml(bodyB, u)
|
body := rewriteHtml(bodyB, u)
|
||||||
c.Set("Content-Type", resp.Header.Get("Content-Type"))
|
return body, req, resp, nil
|
||||||
return c.SendString(body)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func rewriteHtml(bodyB []byte, u *url.URL) string {
|
func rewriteHtml(bodyB []byte, u *url.URL) string {
|
||||||
@@ -63,7 +87,7 @@ func rewriteHtml(bodyB []byte, u *url.URL) string {
|
|||||||
// scripts
|
// scripts
|
||||||
scriptPattern := `<script\s+([^>]*\s+)?src="(/)([^"]*)"`
|
scriptPattern := `<script\s+([^>]*\s+)?src="(/)([^"]*)"`
|
||||||
reScript := regexp.MustCompile(scriptPattern)
|
reScript := regexp.MustCompile(scriptPattern)
|
||||||
body = reScript.ReplaceAllString(body, fmt.Sprintf(`<script $1 script="%s$3"`, "/https://"+u.Host))
|
body = reScript.ReplaceAllString(body, fmt.Sprintf(`<script $1 script="%s$3"`, "/https://"+u.Host+"/"))
|
||||||
|
|
||||||
//body = strings.ReplaceAll(body, "srcset=\"/", "srcset=\"/https://"+u.Host+"/") // TODO: Needs a regex to rewrite the URL's
|
//body = strings.ReplaceAll(body, "srcset=\"/", "srcset=\"/https://"+u.Host+"/") // TODO: Needs a regex to rewrite the URL's
|
||||||
body = strings.ReplaceAll(body, "href=\"/", "href=\"/https://"+u.Host+"/")
|
body = strings.ReplaceAll(body, "href=\"/", "href=\"/https://"+u.Host+"/")
|
||||||
@@ -73,3 +97,11 @@ func rewriteHtml(bodyB []byte, u *url.URL) string {
|
|||||||
|
|
||||||
return body
|
return body
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getenv(key, fallback string) string {
|
||||||
|
value := os.Getenv(key)
|
||||||
|
if len(value) == 0 {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|||||||
58
handlers/proxy.test.go
Normal file
58
handlers/proxy.test.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// BEGIN: 6f8b3f5d5d5d
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProxySite(t *testing.T) {
|
||||||
|
app := fiber.New()
|
||||||
|
app.Get("/:url", ProxySite)
|
||||||
|
|
||||||
|
req := httptest.NewRequest("GET", "/https://example.com", nil)
|
||||||
|
resp, err := app.Test(req)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRewriteHtml(t *testing.T) {
|
||||||
|
bodyB := []byte(`
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Test Page</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<img src="/image.jpg">
|
||||||
|
<script src="/script.js"></script>
|
||||||
|
<a href="/about">About Us</a>
|
||||||
|
<div style="background-image: url('/background.jpg')"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`)
|
||||||
|
u := &url.URL{Host: "example.com"}
|
||||||
|
|
||||||
|
expected := `
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Test Page</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<img src="/https://example.com/image.jpg">
|
||||||
|
<script script="/https://example.com/script.js"></script>
|
||||||
|
<a href="/https://example.com/about">About Us</a>
|
||||||
|
<div style="background-image: url('/https://example.com/background.jpg')"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`
|
||||||
|
|
||||||
|
actual := rewriteHtml(bodyB, u)
|
||||||
|
assert.Equal(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
// END: 6f8b3f5d5d5d
|
||||||
21
handlers/raw.go
Normal file
21
handlers/raw.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Raw(c *fiber.Ctx) error {
|
||||||
|
// Get the url from the URL
|
||||||
|
urlQuery := c.Params("*")
|
||||||
|
|
||||||
|
queries := c.Queries()
|
||||||
|
body, _, _, err := fetchSite(urlQuery, queries)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR:", err)
|
||||||
|
c.SendStatus(500)
|
||||||
|
return c.SendString(err.Error())
|
||||||
|
}
|
||||||
|
return c.SendString(body)
|
||||||
|
}
|
||||||
60
handlers/raw.test.go
Normal file
60
handlers/raw.test.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// BEGIN: 7f8d9e6d4b5c
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRaw(t *testing.T) {
|
||||||
|
app := fiber.New()
|
||||||
|
app.Get("/raw/*", Raw)
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
url string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid url",
|
||||||
|
url: "https://www.google.com",
|
||||||
|
expected: "<!doctype html>",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid url",
|
||||||
|
url: "invalid-url",
|
||||||
|
expected: "parse invalid-url: invalid URI for request",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/raw/"+tc.url, nil)
|
||||||
|
resp, err := app.Test(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
t.Errorf("expected status OK; got %v", resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(string(body), tc.expected) {
|
||||||
|
t.Errorf("expected body to contain %q; got %q", tc.expected, string(body))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// END: 7f8d9e6d4b5c
|
||||||
Reference in New Issue
Block a user