Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf2529753d | ||
|
|
e3389d2df3 | ||
|
|
b786796595 | ||
|
|
377a577c67 | ||
|
|
6eb7b481d8 | ||
|
|
2f1de95e06 | ||
|
|
7ae1a29932 | ||
|
|
63dcaeba3c | ||
|
|
62e03a384a | ||
|
|
7f4d749c55 | ||
|
|
e748cb09a5 | ||
|
|
c98e49f2b3 |
26
README.md
26
README.md
@@ -14,16 +14,19 @@ Certain sites may display missing images or encounter formatting issues. This ca
|
|||||||
|
|
||||||
### Features
|
### Features
|
||||||
- [x] Bypass Paywalls
|
- [x] Bypass Paywalls
|
||||||
- [x] Remove CORS headers from responses, Assets, and images ...
|
- [x] Remove CORS headers from responses, assets, and images ...
|
||||||
- [x] Keep site browsable
|
- [x] Keep site browsable
|
||||||
- [x] Add a debug path
|
- [x] API
|
||||||
- [x] Add a API
|
- [x] Show RAW HTML
|
||||||
- [x] Docker container
|
- [x] Docker container
|
||||||
- [x] Linux binary
|
- [x] Linux binary
|
||||||
- [x] Mac OS binary
|
- [x] Mac OS binary
|
||||||
- [x] Windows binary (Untested)
|
- [x] Windows binary (untested)
|
||||||
- [x] Remove most of the ads (unexpected side effect)
|
- [x] Remove most of the ads (unexpected side effect)
|
||||||
- [ ] Basic Auth
|
- [x] Basic Auth
|
||||||
|
- [x] Disable logs
|
||||||
|
- [x] Custom User Agent
|
||||||
|
- [x] Custom X-Forwarded-For IP
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -39,7 +42,7 @@ 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.yaml
|
curl https://raw.githubusercontent.com/kubero-dev/ladder/main/docker-compose.yaml --output docker-compose.yaml
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -53,15 +56,13 @@ docker-compose up -d
|
|||||||
Or direct by appending the URL to the end of the proxy URL:
|
Or direct by appending the URL to the end of the proxy URL:
|
||||||
http://localhost:8080/https://www.google.com
|
http://localhost:8080/https://www.google.com
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### API
|
### API
|
||||||
```bash
|
```bash
|
||||||
curl -X GET "http://localhost:8080/api/https://www.google.com"
|
curl -X GET "http://localhost:8080/api/https://www.google.com"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Debug
|
### RAW
|
||||||
http://localhost:8080/debug/https://www.google.com
|
http://localhost:8080/raw/https://www.google.com
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
@@ -72,4 +73,7 @@ http://localhost:8080/debug/https://www.google.com
|
|||||||
| `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)` |
|
| `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` |
|
| `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>
|
||||||
|
|||||||
BIN
cmd/favicon.ico
Normal file
BIN
cmd/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
31
cmd/main.go
31
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,7 +26,30 @@ 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("raw/*", handlers.Raw)
|
app.Get("raw/*", handlers.Raw)
|
||||||
app.Get("api/*", handlers.Api)
|
app.Get("api/*", handlers.Api)
|
||||||
app.Get("/*", handlers.ProxySite)
|
app.Get("/*", handlers.ProxySite)
|
||||||
|
|||||||
@@ -5,14 +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
|
||||||
- X_FORWARDED_FOR=66.249.66.1
|
#- X_FORWARDED_FOR=66.249.66.1
|
||||||
- USER_AGENT=Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
|
#- USER_AGENT=Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
|
||||||
#- GODEBUG=netdns=go+4
|
#- USERPASS=foo:bar
|
||||||
|
#- LOG_URLS=true
|
||||||
|
#- GODEBUG=netdns=go
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
deploy:
|
deploy:
|
||||||
|
|||||||
@@ -1,47 +1,31 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
_ "embed"
|
||||||
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:embed VERSION
|
||||||
|
var version string
|
||||||
|
|
||||||
func Api(c *fiber.Ctx) error {
|
func Api(c *fiber.Ctx) error {
|
||||||
// Get the url from the URL
|
// Get the url from the URL
|
||||||
urlQuery := c.Params("*")
|
urlQuery := c.Params("*")
|
||||||
|
|
||||||
u, err := url.Parse(urlQuery)
|
queries := c.Queries()
|
||||||
|
body, req, resp, err := fetchSite(urlQuery, queries)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Println("ERROR:", err)
|
||||||
|
c.SendStatus(500)
|
||||||
return c.SendString(err.Error())
|
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", UserAgent)
|
|
||||||
req.Header.Set("X-Forwarded-For", ForwardedFor)
|
|
||||||
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)
|
|
||||||
response := Response{
|
response := Response{
|
||||||
Body: body,
|
Version: version,
|
||||||
|
Body: body,
|
||||||
}
|
}
|
||||||
response.Request.Headers = make([]interface{}, 0)
|
response.Request.Headers = make([]interface{}, 0)
|
||||||
for k, v := range req.Header {
|
for k, v := range req.Header {
|
||||||
@@ -63,6 +47,7 @@ func Api(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Response struct {
|
type Response struct {
|
||||||
|
Version string `json:"version"`
|
||||||
Body string `json:"body"`
|
Body string `json:"body"`
|
||||||
Request struct {
|
Request struct {
|
||||||
Headers []interface{} `json:"headers"`
|
Headers []interface{} `json:"headers"`
|
||||||
|
|||||||
@@ -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 {
|
||||||
c.Set("Content-Type", "text/html")
|
if os.Getenv("DISABLE_FORM") == "true" {
|
||||||
return c.SendString(html)
|
c.Set("Content-Type", "text/html")
|
||||||
|
c.SendStatus(fiber.StatusNotFound)
|
||||||
|
return c.SendString("Form Disabled")
|
||||||
|
} else {
|
||||||
|
c.Set("Content-Type", "text/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>
|
||||||
|
|||||||
@@ -18,20 +18,43 @@ 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", UserAgent)
|
req.Header.Set("User-Agent", UserAgent)
|
||||||
req.Header.Set("X-Forwarded-For", ForwardedFor)
|
req.Header.Set("X-Forwarded-For", ForwardedFor)
|
||||||
req.Header.Set("Referer", u.String())
|
req.Header.Set("Referer", u.String())
|
||||||
@@ -39,20 +62,17 @@ func ProxySite(c *fiber.Ctx) error {
|
|||||||
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 {
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
@@ -13,32 +10,12 @@ func Raw(c *fiber.Ctx) error {
|
|||||||
// Get the url from the URL
|
// Get the url from the URL
|
||||||
urlQuery := c.Params("*")
|
urlQuery := c.Params("*")
|
||||||
|
|
||||||
u, err := url.Parse(urlQuery)
|
queries := c.Queries()
|
||||||
|
body, _, _, err := fetchSite(urlQuery, queries)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Println("ERROR:", err)
|
||||||
|
c.SendStatus(500)
|
||||||
return c.SendString(err.Error())
|
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", UserAgent)
|
|
||||||
req.Header.Set("X-Forwarded-For", ForwardedFor)
|
|
||||||
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)
|
return c.SendString(body)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user