Compare commits
132 Commits
v0.0.3
...
add-versio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5392992350 | ||
|
|
7be62e2735 | ||
|
|
5e76ff0879 | ||
|
|
ee641bf8f6 | ||
|
|
2fb089ea28 | ||
|
|
9f857eca8b | ||
|
|
0673255fc8 | ||
|
|
4dbc103cf7 | ||
|
|
514facd2c0 | ||
|
|
531b7da811 | ||
|
|
9a53f28b3f | ||
|
|
6cbccbfadb | ||
|
|
b07d49f230 | ||
|
|
af10efb7f2 | ||
|
|
3f0f4207a1 | ||
|
|
2236c4fff9 | ||
|
|
78454f8713 | ||
|
|
6bff28e18d | ||
|
|
cdd429e4be | ||
|
|
11ee581fd4 | ||
|
|
4e44a24261 | ||
|
|
0ddb029aae | ||
|
|
a262afe035 | ||
|
|
fdca9d39d9 | ||
|
|
30a6ab501d | ||
|
|
7a51243ff4 | ||
|
|
c8b94dc702 | ||
|
|
fbc9567820 | ||
|
|
4d5c25c148 | ||
|
|
082868af2d | ||
|
|
a4abce78fb | ||
|
|
190de6d9c5 | ||
|
|
bdd19dcbb6 | ||
|
|
02e6b1c090 | ||
|
|
84a173a3af | ||
|
|
c91cbeb8a2 | ||
|
|
fb0ccc9ad5 | ||
|
|
d56864a841 | ||
|
|
31902c21d2 | ||
|
|
0011095fd3 | ||
|
|
e4e0619c9d | ||
|
|
cd14e879ba | ||
|
|
3b1152ade0 | ||
|
|
136387cd34 | ||
|
|
571eb4174d | ||
|
|
f87e35b5f8 | ||
|
|
8d1554e10e | ||
|
|
ff5bb61891 | ||
|
|
936b418b00 | ||
|
|
ac44f12d85 | ||
|
|
b6f0c644f8 | ||
|
|
66c4b3c911 | ||
|
|
924696c015 | ||
|
|
81aa00c2ea | ||
|
|
6c1f58e2e7 | ||
|
|
d3c995df34 | ||
|
|
6f4a2daeca | ||
|
|
f728b2c1de | ||
|
|
bc346a3954 | ||
|
|
5442da81b9 | ||
|
|
73b13914fe | ||
|
|
b127f81a9b | ||
|
|
79438a0b59 | ||
|
|
8058ebf0ca | ||
|
|
afca5eda80 | ||
|
|
1aa917e0c1 | ||
|
|
84617b32e3 | ||
|
|
501dfb106a | ||
|
|
719373bb7d | ||
|
|
a9f22ef428 | ||
|
|
54926a6644 | ||
|
|
63933991fd | ||
|
|
46ca742f92 | ||
|
|
a1e63c9ecb | ||
|
|
9e9c50181c | ||
|
|
43e90cf7f2 | ||
|
|
46a32ec548 | ||
|
|
bad7eebd36 | ||
|
|
51476759da | ||
|
|
1a708959f7 | ||
|
|
1f89661ed9 | ||
|
|
a7299049c3 | ||
|
|
3a1d2bc187 | ||
|
|
46c91a05d0 | ||
|
|
5df9a937c5 | ||
|
|
945f499e88 | ||
|
|
cdcbfd4ee9 | ||
|
|
a2f909501c | ||
|
|
cc56f03607 | ||
|
|
e3eb866d48 | ||
|
|
34a2683457 | ||
|
|
07513f6dc4 | ||
|
|
ec4dc5c2cc | ||
|
|
ba87d6b980 | ||
|
|
a6ee6aebfc | ||
|
|
7b519c7016 | ||
|
|
fba9db3d94 | ||
|
|
0dd5edd5ba | ||
|
|
4023718b12 | ||
|
|
cb02f52a46 | ||
|
|
3e20678e3d | ||
|
|
184a79b0af | ||
|
|
923d3178ec | ||
|
|
a1e5d540fe | ||
|
|
890d6929e0 | ||
|
|
8a6320ccca | ||
|
|
67f9af0296 | ||
|
|
cde6ed7229 | ||
|
|
96dd4de876 | ||
|
|
d5c58f42da | ||
|
|
d34c5680b3 | ||
|
|
6dfdaaa25b | ||
|
|
7ea7f253e8 | ||
|
|
bf2529753d | ||
|
|
e3389d2df3 | ||
|
|
b786796595 | ||
|
|
377a577c67 | ||
|
|
6eb7b481d8 | ||
|
|
2f1de95e06 | ||
|
|
7ae1a29932 | ||
|
|
63dcaeba3c | ||
|
|
62e03a384a | ||
|
|
7f4d749c55 | ||
|
|
e748cb09a5 | ||
|
|
c98e49f2b3 | ||
|
|
3e3eebcdc2 | ||
|
|
45a3fe2adf | ||
|
|
ec7f2089fc | ||
|
|
622143211c | ||
|
|
796ffd1c87 | ||
|
|
210837a404 | ||
|
|
aa1caaca98 |
42
.github/workflows/build-css.yaml
vendored
Normal file
42
.github/workflows/build-css.yaml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: Build Tailwind CSS
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "handlers/form.html"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
tailwindbuilder:
|
||||
permissions:
|
||||
# Give the default GITHUB_TOKEN write permission to commit and push the
|
||||
# added or changed files to the repository.
|
||||
contents: write
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
-
|
||||
name: Build Tailwind CSS
|
||||
run: pnpm build
|
||||
-
|
||||
name: Commit generated stylesheet
|
||||
run: |
|
||||
if git diff --quiet cmd/styles.css; then
|
||||
echo "No changes to commit."
|
||||
exit 0
|
||||
else
|
||||
echo "Changes detected, committing..."
|
||||
git config --global user.name "Github action"
|
||||
git config --global user.email "username@users.noreply.github.com"
|
||||
git add cmd
|
||||
git commit -m "Generated stylesheet"
|
||||
git push
|
||||
fi
|
||||
10
.github/workflows/release-binaries.yaml
vendored
10
.github/workflows/release-binaries.yaml
vendored
@@ -22,7 +22,11 @@ jobs:
|
||||
-
|
||||
name: Set version
|
||||
run: |
|
||||
echo -n $(git describe --tags --abbrev=0) > cmd/VERSION
|
||||
VERSION=$(git describe --tags --abbrev=0)
|
||||
echo -n $VERSION > handlers/VERSION
|
||||
sed -i 's\VERSION\${VERSION}\g' handlers/form.html
|
||||
echo handlers/form.html >> .gitignore
|
||||
echo .gitignore >> .gitignore
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
@@ -36,5 +40,5 @@ jobs:
|
||||
version: latest
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GORELEASER_GITHUB_TOKEN: ${{ secrets.GORELEASER_GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.PAT_GORELEASER }}
|
||||
# GORELEASER_GITHUB_TOKEN: ${{ secrets.GORELEASER_GITHUB_TOKEN }}
|
||||
6
.github/workflows/release-docker.yaml
vendored
6
.github/workflows/release-docker.yaml
vendored
@@ -42,7 +42,11 @@ jobs:
|
||||
- name: Set version
|
||||
id: version
|
||||
run: |
|
||||
echo ${GITHUB_REF#refs/tags/v} > cmd/VERSION
|
||||
VERSION=$(git describe --tags --abbrev=0)
|
||||
echo -n $VERSION > handlers/VERSION
|
||||
sed -i 's\VERSION\${VERSION}\g' handlers/form.html
|
||||
echo handlers/form.html >> .gitignore
|
||||
echo .gitignore >> .gitignore
|
||||
|
||||
# Install the cosign tool except on PR
|
||||
# https://github.com/sigstore/cosign-installer
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@
|
||||
ladder
|
||||
|
||||
VERSION
|
||||
output.css
|
||||
24
.golangci-lint.yaml
Normal file
24
.golangci-lint.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
linters:
|
||||
enable:
|
||||
- errcheck
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- unused
|
||||
- cyclop
|
||||
- dupword
|
||||
- wsl
|
||||
- varnamelen
|
||||
- usestdlibvars
|
||||
- unparam
|
||||
- revive
|
||||
- prealloc
|
||||
- misspell
|
||||
- gocyclo
|
||||
- funlen
|
||||
- bodyclose
|
||||
|
||||
linters-settings:
|
||||
cyclop:
|
||||
max-complexity: 15
|
||||
@@ -7,7 +7,7 @@ before:
|
||||
builds:
|
||||
-
|
||||
main: cmd/main.go
|
||||
binary: kubero
|
||||
binary: ladder
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
@@ -33,10 +33,10 @@ changelog:
|
||||
#brews:
|
||||
# -
|
||||
# repository:
|
||||
# owner: kubero-dev
|
||||
# owner: everywall
|
||||
# name: homebrew-ladder
|
||||
# token: "{{ .Env.GORELEASER_GITHUB_TOKEN }}"
|
||||
# homepage: "https://www.kubero.dev"
|
||||
# description: "Manage your kubero applications with the CLI"
|
||||
# homepage: "https://www.everyladder.dev"
|
||||
# description: "Manage your everyladder applications modify every website"
|
||||
# test: |
|
||||
# system "#{bin}/kubero", "--version"
|
||||
# system "#{bin}/everyladder", "--version"
|
||||
10
Makefile
Normal file
10
Makefile
Normal file
@@ -0,0 +1,10 @@
|
||||
lint:
|
||||
gofumpt -l -w .
|
||||
golangci-lint run -c .golangci-lint.yaml
|
||||
|
||||
go mod tidy
|
||||
go clean
|
||||
|
||||
install-linters:
|
||||
go install mvdan.cc/gofumpt@latest
|
||||
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.2
|
||||
162
README.md
162
README.md
@@ -1,8 +1,10 @@
|
||||
<p align="center">
|
||||
<img src="public/pigeon.svg" width="100px">
|
||||
<img src="assets/pigeon.svg" width="100px">
|
||||
</p>
|
||||
|
||||
<h1 align="center">Ladder</h1>
|
||||
<div><img alt="License" src="https://img.shields.io/github/license/everywall/ladder"> <img alt="go.mod Go version " src="https://img.shields.io/github/go-mod/go-version/everywall/ladder"> <img alt="GitHub tag (with filter)" src="https://img.shields.io/github/v/tag/everywall/ladder"> <img alt="GitHub (Pre-)Release Date" src="https://img.shields.io/github/release-date-pre/everywall/ladder"> <img alt="GitHub Downloads all releases" src="https://img.shields.io/github/downloads/everywall/ladder/total"> <img alt="GitHub Build Status (with event)" src="https://img.shields.io/github/actions/workflow/status/everywall/ladder/release-binaries.yaml"></div>
|
||||
|
||||
|
||||
*Ladder is a web proxy to help bypass paywalls.* This is a selfhosted version of [1ft.io](https://1ft.io) and [12ft.io](https://12ft.io). It is inspired by [13ft](https://github.com/wasi-master/13ft).
|
||||
|
||||
@@ -10,40 +12,172 @@
|
||||
|
||||
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.
|
||||
> **Disclaimer:** This project is intended for educational purposes only. The author does not endorse or encourage any unethical or illegal activity. Use this tool at your own risk.
|
||||
|
||||
### Features
|
||||
- [x] Bypass Paywalls
|
||||
- [x] Remove CORS
|
||||
- [x] Keep Site browsable
|
||||
- [x] Docker Container
|
||||
- [x] Linux Binary
|
||||
- [x] Mac OS Binary
|
||||
- [x] Windows Binary (Untested)
|
||||
- [x] Remove CORS headers from responses, assets, and images ...
|
||||
- [x] Apply domain based ruleset/code to modify response / requested URL
|
||||
- [x] Keep site browsable
|
||||
- [x] API
|
||||
- [x] Fetch RAW HTML
|
||||
- [x] Custom User Agent
|
||||
- [x] Custom X-Forwarded-For IP
|
||||
- [x] [Docker container](https://github.com/everywall/ladder/pkgs/container/ladder) (amd64, arm64)
|
||||
- [x] Linux binary
|
||||
- [x] Mac OS binary
|
||||
- [x] Windows binary (untested)
|
||||
- [x] Removes most of the ads (unexpected side effect ¯\\\_(ツ)_/¯ )
|
||||
- [x] Basic Auth
|
||||
- [x] Disable logs
|
||||
- [x] No Tracking
|
||||
- [x] Limit the proxy to a list of domains
|
||||
- [x] Expose Ruleset to other ladders
|
||||
- [x] Fetch from Google Cache
|
||||
- [ ] Optional TOR proxy
|
||||
- [ ] A key to share only one URL
|
||||
|
||||
## How to use
|
||||
### Limitations
|
||||
Some sites do not expose their content to search engines, which means that the proxy cannot access the content. A future version will try to fetch the content from Google Cache.
|
||||
|
||||
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 consider buying a subscription for the site.
|
||||
|
||||
## Installation
|
||||
|
||||
> **Warning:** If your instance will be publicly accessible, make sure to enable Basic Auth. This will prevent unauthorized users from using your proxy. If you do not enable Basic Auth, anyone can use your proxy to browse nasty/illegal stuff. And you will be responsible for it.
|
||||
|
||||
### Binary
|
||||
1) Download Binary
|
||||
2) Run Binary
|
||||
1) Download binary [here](https://github.com/everywall/ladder/releases/latest)
|
||||
2) Unpack and run the binary `./ladder`
|
||||
3) Open Browser (Default: http://localhost:8080)
|
||||
|
||||
### Docker
|
||||
```bash
|
||||
docker run -p 8080:8080 -d --name ladder ghcr.io/kubero-dev/ladder:latest
|
||||
docker run -p 8080:8080 -d --name ladder ghcr.io/everywall/ladder:latest
|
||||
```
|
||||
|
||||
### Docker Compose
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/kubero-dev/ladder/main/docker-compose.yml
|
||||
curl https://raw.githubusercontent.com/everywall/ladder/main/docker-compose.yaml --output docker-compose.yaml
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### Helm
|
||||
See [README.md](/helm-chart/README.md) in helm-chart sub-directory for more information.
|
||||
|
||||
## 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.example.com
|
||||
|
||||
Or create a bookmark with the following URL:
|
||||
```javascript
|
||||
javascript:window.location.href="http://localhost:8080/"+location.href
|
||||
```
|
||||
|
||||
### API
|
||||
```bash
|
||||
curl -X GET "http://localhost:8080/api/https://www.example.com"
|
||||
```
|
||||
|
||||
### RAW
|
||||
http://localhost:8080/raw/https://www.example.com
|
||||
|
||||
|
||||
### Running Ruleset
|
||||
http://localhost:8080/ruleset
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
| Variable | Description | Value |
|
||||
| --- | --- | --- |
|
||||
| `PORT` | Port to listen on | `8080` |
|
||||
| `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` |
|
||||
| `FORM_PATH` | Path to custom Form HTML | `` |
|
||||
| `RULESET` | URL to a ruleset file | `https://raw.githubusercontent.com/everywall/ladder/main/ruleset.yaml` or `/path/to/my/rules.yaml` |
|
||||
| `EXPOSE_RULESET` | Make your Ruleset available to other ladders | `true` |
|
||||
| `ALLOWED_DOMAINS` | Comma separated list of allowed domains. Empty = no limitations | `` |
|
||||
| `ALLOWED_DOMAINS_RULESET` | Allow Domains from Ruleset. false = no limitations | `false` |
|
||||
|
||||
`ALLOWED_DOMAINS` and `ALLOWED_DOMAINS_RULESET` are joined together. If both are empty, no limitations are applied.
|
||||
|
||||
### Ruleset
|
||||
|
||||
It is possible to apply custom rules to modify the response or the requested URL. This can be used to remove unwanted or modify elements from the page. The ruleset is a YAML file that contains a list of rules for each domain and is loaded on startup
|
||||
|
||||
See in [ruleset.yaml](ruleset.yaml) for an example.
|
||||
|
||||
```yaml
|
||||
- domain: example.com # Includes all subdomains
|
||||
domains: # Additional domains to apply the rule
|
||||
- www.example.de
|
||||
- www.beispiel.de
|
||||
headers:
|
||||
x-forwarded-for: none # override X-Forwarded-For header or delete with none
|
||||
referer: none # override Referer header or delete with none
|
||||
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36
|
||||
content-security-policy: script-src 'self'; # override response header
|
||||
cookie: privacy=1
|
||||
regexRules:
|
||||
- match: <script\s+([^>]*\s+)?src="(/)([^"]*)"
|
||||
replace: <script $1 script="/https://www.example.com/$3"
|
||||
injections:
|
||||
- position: head # Position where to inject the code
|
||||
append: | # possible keys: append, prepend, replace
|
||||
<script>
|
||||
window.localStorage.clear();
|
||||
console.log("test");
|
||||
alert("Hello!");
|
||||
</script>
|
||||
- domain: www.anotherdomain.com # Domain where the rule applies
|
||||
paths: # Paths where the rule applies
|
||||
- /article
|
||||
googleCache: false # Use Google Cache to fetch the content
|
||||
regexRules: # Regex rules to apply
|
||||
- match: <script\s+([^>]*\s+)?src="(/)([^"]*)"
|
||||
replace: <script $1 script="/https://www.example.com/$3"
|
||||
injections:
|
||||
- position: .left-content article .post-title # Position where to inject the code into DOM
|
||||
replace: |
|
||||
<h1>My Custom Title</h1>
|
||||
- position: .left-content article # Position where to inject the code into DOM
|
||||
prepend: |
|
||||
<h2>Subtitle</h2>
|
||||
- domain: demo.com
|
||||
headers:
|
||||
content-security-policy: script-src 'self';
|
||||
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36
|
||||
urlMods: # Modify the URL
|
||||
query:
|
||||
- key: amp # (this will append ?amp=1 to the URL)
|
||||
value: 1
|
||||
domain:
|
||||
- match: www # regex to match part of domain
|
||||
replace: amp # (this would modify the domain from www.demo.de to amp.demo.de)
|
||||
path:
|
||||
- match: ^ # regex to match part of path
|
||||
replace: /amp/ # (modify the url from https://www.demo.com/article/ to https://www.demo.de/amp/article/)
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
To run a development server at http://localhost:8080:
|
||||
|
||||
```bash
|
||||
echo "DEV" > handler/VERSION
|
||||
RULESET="./ruleset.yaml" go run cmd/main.go
|
||||
```
|
||||
|
||||
This project uses [pnpm](https://pnpm.io/) to build a stylesheet with the [Tailwind CSS](https://tailwindcss.com/) classes. For local development, if you modify styles in `form.html`, run `pnpm build` to generate a new stylesheet.
|
||||
|
||||
|
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 |
88
cmd/main.go
88
cmd/main.go
@@ -1,31 +1,95 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"ladder/handlers"
|
||||
"embed"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"ladder/handlers"
|
||||
|
||||
"github.com/akamensky/argparse"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/basicauth"
|
||||
"github.com/gofiber/fiber/v2/middleware/favicon"
|
||||
)
|
||||
|
||||
func main() {
|
||||
//go:embed favicon.ico
|
||||
var faviconData string
|
||||
//go:embed styles.css
|
||||
var cssData embed.FS
|
||||
|
||||
func main() {
|
||||
parser := argparse.NewParser("ladder", "Every Wall needs a Ladder")
|
||||
|
||||
portEnv := os.Getenv("PORT")
|
||||
if os.Getenv("PORT") == "" {
|
||||
portEnv = "8080"
|
||||
}
|
||||
port := parser.String("p", "port", &argparse.Options{
|
||||
Required: false,
|
||||
Default: portEnv,
|
||||
Help: "Port the webserver will listen on",
|
||||
})
|
||||
|
||||
prefork := parser.Flag("P", "prefork", &argparse.Options{
|
||||
Required: false,
|
||||
Help: "This will spawn multiple processes listening",
|
||||
})
|
||||
|
||||
err := parser.Parse(os.Args)
|
||||
if err != nil {
|
||||
fmt.Print(parser.Usage(err))
|
||||
}
|
||||
|
||||
if os.Getenv("PREFORK") == "true" {
|
||||
*prefork = true
|
||||
}
|
||||
|
||||
prefork, _ := strconv.ParseBool(os.Getenv("PREFORK"))
|
||||
app := fiber.New(
|
||||
fiber.Config{
|
||||
Prefork: prefork,
|
||||
Prefork: *prefork,
|
||||
GETOnly: true,
|
||||
},
|
||||
)
|
||||
|
||||
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("debug/*", handlers.Debug)
|
||||
app.Get("/styles.css", func(c *fiber.Ctx) error {
|
||||
cssData, err := cssData.ReadFile("styles.css")
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
|
||||
}
|
||||
c.Set("Content-Type", "text/css")
|
||||
return c.Send(cssData)
|
||||
})
|
||||
app.Get("ruleset", handlers.Ruleset)
|
||||
|
||||
app.Get("raw/*", handlers.Raw)
|
||||
app.Get("api/*", handlers.Api)
|
||||
app.Get("/*", handlers.ProxySite)
|
||||
|
||||
port := os.Getenv("PORT")
|
||||
if os.Getenv("PORT") == "" {
|
||||
port = "8080"
|
||||
}
|
||||
log.Fatal(app.Listen(":" + port))
|
||||
|
||||
log.Fatal(app.Listen(":" + *port))
|
||||
}
|
||||
|
||||
1
cmd/styles.css
Normal file
1
cmd/styles.css
Normal file
File diff suppressed because one or more lines are too long
@@ -1,23 +1,26 @@
|
||||
version: '3'
|
||||
services:
|
||||
ladder:
|
||||
image: ghcr.io/kubero-dev/ladder:latest
|
||||
image: ghcr.io/everywall/ladder:latest
|
||||
container_name: ladder
|
||||
build: .
|
||||
#build: .
|
||||
#restart: always
|
||||
#command: tail -f /dev/null
|
||||
#command: sh -c ./ladder
|
||||
environment:
|
||||
- PORT=8080
|
||||
- PREFORK=true
|
||||
#- GODEBUG=netdns=go+4
|
||||
- RULESET=/app/ruleset.yaml
|
||||
#- ALLOWED_DOMAINS_RULESET=false
|
||||
#- EXPOSE_RULESET=true
|
||||
#- PREFORK=false
|
||||
#- DISABLE_FORM=fase
|
||||
#- FORM_PATH=/app/form.html
|
||||
#- 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:
|
||||
- "8080:8080"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: "0.50"
|
||||
memory: 512M
|
||||
reservations:
|
||||
cpus: "0.25"
|
||||
memory: 128M
|
||||
volumes:
|
||||
- ./ruleset.yaml:/app/ruleset.yaml
|
||||
- ./handlers/form.html:/app/form.html
|
||||
15
go.mod
15
go.mod
@@ -3,16 +3,27 @@ module ladder
|
||||
go 1.21.1
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.8.1
|
||||
github.com/akamensky/argparse v1.4.0
|
||||
github.com/gofiber/fiber/v2 v2.50.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/google/uuid v1.4.0 // indirect
|
||||
github.com/klauspost/compress v1.17.2 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // 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/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.50.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/net v0.18.0 // indirect
|
||||
golang.org/x/sys v0.14.0 // indirect
|
||||
)
|
||||
|
||||
61
go.sum
61
go.sum
@@ -1,5 +1,14 @@
|
||||
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
|
||||
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
|
||||
github.com/akamensky/argparse v1.4.0 h1:YGzvsTqCvbEZhL8zZu2AiA5nq805NZh75JNj4ajn1xc=
|
||||
github.com/akamensky/argparse v1.4.0/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA=
|
||||
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
||||
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
|
||||
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
|
||||
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
|
||||
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/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw=
|
||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||
@@ -13,16 +22,64 @@ 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-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
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.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
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/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
|
||||
github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
|
||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
|
||||
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
59
handlers/api.go
Normal file
59
handlers/api.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"log"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
//nolint:all
|
||||
//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([]any, 0, len(req.Header))
|
||||
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([]any, 0, len(resp.Header))
|
||||
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)
|
||||
}
|
||||
192
handlers/form.go
192
handlers/form.go
@@ -1,179 +1,31 @@
|
||||
package handlers
|
||||
|
||||
import "github.com/gofiber/fiber/v2"
|
||||
import (
|
||||
_ "embed"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
//go:embed form.html
|
||||
var formHtml string
|
||||
|
||||
func Form(c *fiber.Ctx) error {
|
||||
if os.Getenv("DISABLE_FORM") == "true" {
|
||||
c.Set("Content-Type", "text/html")
|
||||
return c.SendString(html)
|
||||
c.SendStatus(fiber.StatusNotFound)
|
||||
return c.SendString("Form Disabled")
|
||||
} else {
|
||||
if os.Getenv("FORM_PATH") != "" {
|
||||
dat, err := os.ReadFile(os.Getenv("FORM_PATH"))
|
||||
if err != nil {
|
||||
log.Println("ERROR: unable to load custom form", err)
|
||||
} else {
|
||||
formHtml = string(dat)
|
||||
}
|
||||
|
||||
const html = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Ladder</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
|
||||
</head>
|
||||
<style>
|
||||
body {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
text-transform: uppercase;
|
||||
font-size: 70px;
|
||||
font-weight: 600;
|
||||
color: #fdfdfe;
|
||||
text-shadow: 0px 0px 5px #7AA7D1, 0px 0px 10px #7AA7D1, 0px 0px 10px #7AA7D1,
|
||||
0px 0px 20px #7AA7D1;
|
||||
c.Set("Content-Type", "text/html")
|
||||
return c.SendString(formHtml)
|
||||
}
|
||||
.logo-title {
|
||||
font-family: 'Arial', sans-serif;
|
||||
font-size: 2rem;
|
||||
color: #fff;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.logo {
|
||||
text-align: center;
|
||||
}
|
||||
.github-corner {
|
||||
animation: octocat-wave 560ms ease-in-out;
|
||||
position:absolute;
|
||||
top:0;
|
||||
right:0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="https://github.com/kubero-dev/ladder">
|
||||
<div class="github-corner" aria-label="View source on GitHub">
|
||||
<svg
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
version="1.1"
|
||||
width="146"
|
||||
height="146"
|
||||
id="svg2">
|
||||
<defs
|
||||
id="defs8">
|
||||
<filter
|
||||
height="1.096"
|
||||
y="-0.048"
|
||||
width="1.096"
|
||||
x="-0.048"
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter6">
|
||||
<feGaussianBlur
|
||||
stdDeviation="3"
|
||||
id="feGaussianBlur4" />
|
||||
</filter>
|
||||
</defs>
|
||||
<path
|
||||
d="M 152,140 6,-6 H 48 L 152,98 Z"
|
||||
style="opacity:0.8;filter:url(#filter6)"
|
||||
id="path10" />
|
||||
<path
|
||||
d="M 146,134 12,0 h 42 l 92,92 z"
|
||||
style="fill:#007200"
|
||||
id="path12" />
|
||||
<g
|
||||
aria-label="Fork me on GitHub"
|
||||
transform="rotate(45)"
|
||||
style="font-family:Collegiate;fill:#ffffff"
|
||||
id="g42">
|
||||
<path
|
||||
d="m 53.643,-19.486 c 0,0.688 -0.016,1.2 -0.064,1.504 h 2.08 c -0.048,-0.32 -0.064,-0.8 -0.064,-1.424 v -3.344 h 1.76 c 0.416,0 0.736,0.016 0.944,0.048 v -1.76 c -0.24,0.032 -0.592,0.048 -1.088,0.048 h -1.616 v -2.496 h 1.936 c 0.56,0 0.944,0.016 1.184,0.048 v -1.792 h -5.136 c 0.048,0.272 0.064,0.784 0.064,1.504 z"
|
||||
id="path14" />
|
||||
<path
|
||||
d="m 62.424,-17.87 c 1.008,0 1.776,-0.368 2.272,-1.088 0.432,-0.624 0.656,-1.472 0.656,-2.544 0,-2.416 -0.976,-3.616 -2.928,-3.616 -1.968,0 -2.96,1.2 -2.96,3.616 0,1.072 0.224,1.936 0.656,2.56 0.512,0.72 1.28,1.072 2.304,1.072 z m -0.016,-5.68 c 0.496,0 0.816,0.24 0.976,0.704 0.096,0.272 0.144,0.72 0.144,1.344 0,0.64 -0.048,1.088 -0.144,1.36 -0.16,0.464 -0.48,0.688 -0.976,0.688 -0.496,0 -0.816,-0.24 -0.976,-0.704 -0.096,-0.272 -0.144,-0.72 -0.144,-1.344 0,-0.624 0.048,-1.072 0.144,-1.344 0.16,-0.464 0.48,-0.704 0.976,-0.704 z"
|
||||
id="path16" />
|
||||
<path
|
||||
d="m 68.293,-17.982 c -0.032,-0.24 -0.048,-0.64 -0.048,-1.184 v -3.888 c 0.352,-0.304 0.672,-0.464 0.976,-0.464 0.224,0 0.48,0.096 0.752,0.288 v -1.808 c -0.224,-0.08 -0.432,-0.128 -0.624,-0.128 -0.448,0 -0.832,0.192 -1.152,0.56 v -0.48 h -1.744 c 0.032,0.192 0.048,0.544 0.048,1.04 v 4.976 c 0,0.512 -0.016,0.88 -0.048,1.088 z"
|
||||
id="path18" />
|
||||
<path
|
||||
d="m 72.857,-17.982 c -0.032,-0.24 -0.048,-0.64 -0.048,-1.184 v -2.448 l 1.472,2.816 c 0.208,0.384 0.32,0.656 0.368,0.816 h 1.872 l -2.352,-4.416 2.144,-2.752 h -2.064 c -0.08,0.176 -0.192,0.352 -0.352,0.56 l -1.088,1.44 v -4.496 c 0,-0.464 0.016,-0.8 0.048,-1.008 h -1.824 c 0.032,0.192 0.048,0.544 0.048,1.04 v 8.544 c 0,0.512 -0.016,0.88 -0.048,1.088 z"
|
||||
id="path20" />
|
||||
<path
|
||||
d="m 85.08,-24.478 c -0.384,-0.432 -0.896,-0.656 -1.52,-0.656 -0.416,0 -0.864,0.192 -1.328,0.56 v -0.512 l -1.76,-0.016 c 0.032,0.176 0.048,0.544 0.048,1.12 v 4.992 c 0,0.496 -0.016,0.832 -0.048,1.008 h 1.856 c 0,-0.064 -0.048,-0.64 -0.048,-1.008 v -3.984 c 0.304,-0.288 0.608,-0.432 0.928,-0.432 0.656,0 0.864,0.416 0.864,1.76 l -0.016,2.16 c 0,0.656 -0.032,1.168 -0.08,1.504 h 1.92 c -0.048,-0.256 -0.064,-0.752 -0.064,-1.472 v -2.192 c 0,-0.56 -0.048,-1.056 -0.144,-1.504 0.208,-0.176 0.544,-0.256 0.976,-0.256 0.64,0 0.96,0.592 0.96,1.76 v 2.16 c 0,0.656 -0.032,1.168 -0.08,1.504 h 1.904 c -0.048,-0.256 -0.064,-0.752 -0.064,-1.472 v -2.192 c 0,-0.96 -0.176,-1.744 -0.512,-2.368 -0.432,-0.752 -1.056,-1.12 -1.888,-1.12 -0.736,0 -1.376,0.224 -1.904,0.656 z"
|
||||
id="path22" />
|
||||
<path
|
||||
d="m 95.905,-20.99 c 0.032,-0.304 0.048,-0.624 0.048,-0.992 0,-0.944 -0.224,-1.696 -0.656,-2.256 -0.464,-0.592 -1.136,-0.896 -2.016,-0.896 -0.896,0 -1.6,0.368 -2.112,1.088 -0.464,0.656 -0.688,1.456 -0.688,2.432 0,1.136 0.272,2.048 0.832,2.704 0.576,0.72 1.392,1.072 2.448,1.072 0.496,0 1.056,-0.128 1.712,-0.368 v -1.712 c -0.464,0.304 -1.008,0.464 -1.6,0.464 -0.944,0 -1.44,-0.512 -1.52,-1.536 z m -2.576,-2.672 c 0.64,0 0.96,0.4 0.976,1.216 h -1.968 c 0.048,-0.816 0.368,-1.216 0.992,-1.216 z"
|
||||
id="path24" />
|
||||
<use
|
||||
xlink:href="#path16"
|
||||
transform="translate(40.438)"
|
||||
id="use26" />
|
||||
<path
|
||||
d="m 110.187,-25.15 c -0.496,0 -0.992,0.208 -1.472,0.64 v -0.576 h -1.76 c 0.032,0.176 0.048,0.56 0.048,1.184 v 4.912 c 0,0.496 -0.016,0.832 -0.048,1.008 h 1.856 c 0,-0.064 -0.048,-0.64 -0.048,-1.008 v -3.936 c 0.368,-0.352 0.736,-0.528 1.088,-0.528 0.784,0 1.168,0.608 1.152,1.808 l -0.016,2.16 c -0.016,0.752 -0.032,1.264 -0.064,1.504 h 1.92 c -0.048,-0.256 -0.064,-0.752 -0.064,-1.472 v -2.192 c 0,-0.944 -0.192,-1.744 -0.592,-2.384 -0.464,-0.752 -1.136,-1.136 -2,-1.12 z"
|
||||
id="path28" />
|
||||
<path
|
||||
d="m 123.877,-17.982 c 0.144,0.016 0.256,0.016 0.336,0 0,-0.192 -0.064,-0.768 -0.064,-1.36 v -1.856 c 0,-0.56 0.016,-1.056 0.064,-1.52 h -1.952 c 0.032,0.704 0.048,1.12 0.032,1.248 0,1.28 -0.512,1.92 -1.552,1.92 -1.264,0 -1.904,-1.264 -1.904,-3.776 0,-2.48 0.784,-3.728 2.352,-3.728 0.752,0 1.472,0.288 2.16,0.88 v -1.824 c -0.608,-0.528 -1.328,-0.8 -2.16,-0.8 -2.896,0 -4.416,1.904 -4.416,5.424 0,3.696 1.328,5.552 3.968,5.552 0.592,0 1.12,-0.128 1.584,-0.368 0.368,-0.208 0.624,-0.432 0.768,-0.688 z"
|
||||
id="path30" />
|
||||
<path
|
||||
d="m 126.49,-26.334 c 0.592,0 1.104,-0.544 1.104,-1.184 0,-0.656 -0.512,-1.2 -1.104,-1.2 -0.624,0 -1.12,0.544 -1.12,1.2 0,0.64 0.496,1.184 1.12,1.184 z m 0.896,8.352 c -0.016,-0.24 -0.032,-0.64 -0.032,-1.184 v -4.912 c 0,-0.464 0.016,-0.8 0.032,-1.008 h -1.808 c 0.016,0.192 0.032,0.544 0.032,1.04 v 4.976 c 0,0.512 -0.016,0.88 -0.032,1.088 z"
|
||||
id="path32" />
|
||||
<path
|
||||
d="m 130.783,-25.742 c 0,-0.256 0.016,-0.48 0.048,-0.688 h -1.856 c 0.032,0.176 0.048,0.416 0.048,0.72 v 0.624 h -0.784 v 1.552 c 0.224,-0.032 0.4,-0.048 0.544,-0.048 l 0.24,0.016 v 0.032 h -0.016 v 2.864 c 0,0.896 0.112,1.552 0.336,1.968 0.304,0.56 0.832,0.832 1.616,0.832 0.56,0 1.024,-0.112 1.424,-0.32 v -1.6 c -0.272,0.176 -0.56,0.272 -0.896,0.272 -0.464,0 -0.704,-0.352 -0.704,-1.072 v -2.976 h 0.688 c 0.256,0 0.592,0.032 0.704,0.032 v -1.552 h -1.392 z"
|
||||
id="path34" />
|
||||
<path
|
||||
d="m 140.259,-27.678 c 0,-0.416 0.016,-0.736 0.064,-0.976 h -2.096 c 0.048,0.24 0.064,0.688 0.064,1.344 v 2.8 h -2.912 v -3.024 c 0,-0.48 0.016,-0.848 0.064,-1.12 h -2.08 c 0.048,0.256 0.064,0.624 0.064,1.12 v 8.432 c 0,0.496 -0.016,0.864 -0.064,1.12 h 2.08 c -0.048,-0.24 -0.064,-0.656 -0.064,-1.232 v -3.552 h 2.912 v 3.568 c 0,0.528 -0.016,0.944 -0.064,1.216 h 2.096 c -0.048,-0.24 -0.064,-0.624 -0.064,-1.12 v -3.664 h 0.528 v -1.744 h -0.528 z"
|
||||
id="path36" />
|
||||
<path
|
||||
d="m 144.402,-17.918 c 0.56,0 1.072,-0.208 1.568,-0.64 v 0.576 h 1.744 c -0.016,-0.176 -0.032,-0.576 -0.032,-1.2 v -4.896 c 0,-0.496 0.016,-0.832 0.032,-1.008 h -1.856 c 0,0.048 0.064,0.64 0.064,1.008 v 3.936 c -0.368,0.352 -0.704,0.528 -1.008,0.528 -0.432,0 -0.72,-0.16 -0.88,-0.496 -0.144,-0.272 -0.208,-0.704 -0.208,-1.312 l 0.016,-2.16 c 0.016,-0.768 0.032,-1.264 0.064,-1.504 h -1.92 c 0.048,0.256 0.064,0.752 0.064,1.472 v 2.192 c 0,0.976 0.16,1.776 0.48,2.384 0.4,0.752 1.024,1.12 1.872,1.12 z"
|
||||
id="path38" />
|
||||
<path
|
||||
d="m 152.31,-17.934 c 0.848,0 1.536,-0.416 2.048,-1.232 0.432,-0.704 0.64,-1.536 0.64,-2.48 0,-0.928 -0.208,-1.712 -0.608,-2.368 -0.48,-0.752 -1.136,-1.12 -1.984,-1.12 -0.464,0 -0.944,0.16 -1.44,0.464 v -2.784 c 0,-0.608 0.016,-1.008 0.032,-1.2 h -1.824 c 0.032,0.176 0.048,0.576 0.048,1.2 v 8.464 c 0,0.496 -0.016,0.832 -0.048,1.008 h 1.696 v -0.576 c 0.384,0.416 0.864,0.624 1.44,0.624 z m -0.24,-5.52 c 0.736,0 1.104,0.608 1.104,1.808 0,0.496 -0.08,0.928 -0.256,1.296 -0.208,0.464 -0.528,0.688 -0.944,0.688 -0.336,0 -0.672,-0.16 -1.008,-0.496 v -2.768 c 0.384,-0.352 0.752,-0.528 1.104,-0.528 z"
|
||||
id="path40" />
|
||||
</g>
|
||||
<path
|
||||
d="m 52,0 94,94 M 14,0 146,132"
|
||||
style="fill:none;stroke:#ffffff;stroke-dasharray:2, 1;stroke-opacity:0.95"
|
||||
id="path44" />
|
||||
</svg>
|
||||
</div>
|
||||
</a>
|
||||
<div class="container">
|
||||
<div class="logo">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="250" height="250" viewBox="0 0 512 512">
|
||||
<path fill="#7AA7D1" d="M262.074 485.246C254.809 485.265 247.407 485.534 240.165 484.99L226.178 483.306C119.737 468.826 34.1354 383.43 25.3176 274.714C24.3655 262.975 23.5876 253.161 24.3295 241.148C31.4284 126.212 123.985 31.919 238.633 24.1259L250.022 23.8366C258.02 23.8001 266.212 23.491 274.183 24.1306C320.519 27.8489 366.348 45.9743 402.232 75.4548L416.996 88.2751C444.342 114.373 464.257 146.819 475.911 182.72L480.415 197.211C486.174 219.054 488.67 242.773 487.436 265.259L486.416 275.75C478.783 352.041 436.405 418.1 369.36 455.394L355.463 462.875C326.247 477.031 294.517 484.631 262.074 485.246ZM253.547 72.4475C161.905 73.0454 83.5901 144.289 73.0095 234.5C69.9101 260.926 74.7763 292.594 83.9003 317.156C104.53 372.691 153.9 416.616 211.281 430.903C226.663 434.733 242.223 436.307 258.044 436.227C353.394 435.507 430.296 361.835 438.445 267.978C439.794 252.442 438.591 236.759 435.59 221.5C419.554 139.955 353.067 79.4187 269.856 72.7052C264.479 72.2714 258.981 72.423 253.586 72.4127L253.547 72.4475Z"/>
|
||||
<path fill="#7AA7D1" d="M153.196 310.121L133.153 285.021C140.83 283.798 148.978 285.092 156.741 284.353L156.637 277.725L124.406 278.002C123.298 277.325 122.856 276.187 122.058 275.193L116.089 267.862C110.469 260.975 103.827 254.843 98.6026 247.669C103.918 246.839 105.248 246.537 111.14 246.523L129.093 246.327C130.152 238.785 128.62 240.843 122.138 240.758C111.929 240.623 110.659 242.014 105.004 234.661L97.9953 225.654C94.8172 221.729 91.2219 218.104 88.2631 214.005C84.1351 208.286 90.1658 209.504 94.601 209.489L236.752 209.545C257.761 209.569 268.184 211.009 285.766 221.678L285.835 206.051C285.837 197.542 286.201 189.141 284.549 180.748C280.22 158.757 260.541 143.877 240.897 135.739C238.055 134.561 232.259 133.654 235.575 129.851C244.784 119.288 263.68 111.99 277.085 111.105C288.697 109.828 301.096 113.537 311.75 117.703C360.649 136.827 393.225 183.042 398.561 234.866C402.204 270.253 391.733 308.356 367.999 335.1C332.832 374.727 269.877 384.883 223.294 360.397C206.156 351.388 183.673 333.299 175.08 316.6C173.511 313.551 174.005 313.555 170.443 313.52L160.641 313.449C158.957 313.435 156.263 314.031 155.122 312.487L153.196 310.121Z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<header>
|
||||
<h1 class="center-align logo-title">ladddddddder</h1>
|
||||
</header>
|
||||
<form id="inputForm" class="col s12" method="get">
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
<input type="text" id="inputField" name="inputField" class="validate" required>
|
||||
<label for="inputField">URL</label>
|
||||
</div>
|
||||
<!--
|
||||
<div class="input-field col s2">
|
||||
<button class="btn waves-effect waves-light" type="submit" name="action">Submit
|
||||
<i class="material-icons right">go</i>
|
||||
</button>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
M.AutoInit();
|
||||
});
|
||||
document.getElementById('inputForm').addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
const inputValue = document.getElementById('inputField').value;
|
||||
window.location.href = '/' + inputValue;
|
||||
return false;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
79
handlers/form.html
Normal file
79
handlers/form.html
Normal file
@@ -0,0 +1,79 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ladder</title>
|
||||
<link rel="stylesheet" href="/styles.css">
|
||||
</head>
|
||||
|
||||
<body class="antialiased text-slate-500 dark:text-slate-400 bg-white dark:bg-slate-900">
|
||||
<div class="grid grid-cols-1 gap-4 max-w-3xl mx-auto pt-10">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="250" viewBox="0 0 512 512">
|
||||
<path fill="#7AA7D1" d="M262.074 485.246C254.809 485.265 247.407 485.534 240.165 484.99L226.178 483.306C119.737 468.826 34.1354 383.43 25.3176 274.714C24.3655 262.975 23.5876 253.161 24.3295 241.148C31.4284 126.212 123.985 31.919 238.633 24.1259L250.022 23.8366C258.02 23.8001 266.212 23.491 274.183 24.1306C320.519 27.8489 366.348 45.9743 402.232 75.4548L416.996 88.2751C444.342 114.373 464.257 146.819 475.911 182.72L480.415 197.211C486.174 219.054 488.67 242.773 487.436 265.259L486.416 275.75C478.783 352.041 436.405 418.1 369.36 455.394L355.463 462.875C326.247 477.031 294.517 484.631 262.074 485.246ZM253.547 72.4475C161.905 73.0454 83.5901 144.289 73.0095 234.5C69.9101 260.926 74.7763 292.594 83.9003 317.156C104.53 372.691 153.9 416.616 211.281 430.903C226.663 434.733 242.223 436.307 258.044 436.227C353.394 435.507 430.296 361.835 438.445 267.978C439.794 252.442 438.591 236.759 435.59 221.5C419.554 139.955 353.067 79.4187 269.856 72.7052C264.479 72.2714 258.981 72.423 253.586 72.4127L253.547 72.4475Z"/>
|
||||
<path fill="#7AA7D1" d="M153.196 310.121L133.153 285.021C140.83 283.798 148.978 285.092 156.741 284.353L156.637 277.725L124.406 278.002C123.298 277.325 122.856 276.187 122.058 275.193L116.089 267.862C110.469 260.975 103.827 254.843 98.6026 247.669C103.918 246.839 105.248 246.537 111.14 246.523L129.093 246.327C130.152 238.785 128.62 240.843 122.138 240.758C111.929 240.623 110.659 242.014 105.004 234.661L97.9953 225.654C94.8172 221.729 91.2219 218.104 88.2631 214.005C84.1351 208.286 90.1658 209.504 94.601 209.489L236.752 209.545C257.761 209.569 268.184 211.009 285.766 221.678L285.835 206.051C285.837 197.542 286.201 189.141 284.549 180.748C280.22 158.757 260.541 143.877 240.897 135.739C238.055 134.561 232.259 133.654 235.575 129.851C244.784 119.288 263.680 111.990 277.085 111.105C288.697 109.828 301.096 113.537 311.75 117.703C360.649 136.827 393.225 183.042 398.561 234.866C402.204 270.253 391.733 308.356 367.999 335.1C332.832 374.727 269.877 384.883 223.294 360.397C206.156 351.388 183.673 333.299 175.08 316.6C173.511 313.551 174.005 313.555 170.443 313.52L160.641 313.449C158.957 313.435 156.263 314.031 155.122 312.487L153.196 310.121Z"/>
|
||||
</svg>
|
||||
<header>
|
||||
<h1 class="text-center text-3xl sm:text-4xl font-extrabold text-slate-900 tracking-tight dark:text-slate-200">ladddddddder</h1>
|
||||
</header>
|
||||
<form id="inputForm" method="get" class="mx-4 relative">
|
||||
<div>
|
||||
<input type="text" id="inputField" placeholder="Proxy Search" name="inputField" class="w-full text-sm leading-6 text-slate-400 rounded-md ring-1 ring-slate-900/10 shadow-sm py-1.5 pl-2 pr-3 hover:ring-slate-300 dark:bg-slate-800 dark:highlight-white/5 dark:hover:bg-slate-700" required autofocus>
|
||||
<button id="clearButton" type="button" aria-label="Clear Search" title="Clear Search" class="hidden absolute inset-y-0 right-0 items-center pr-2 hover:text-slate-400 hover:dark:text-slate-300" tabindex="-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round""><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<footer class="mt-10 mx-4 text-center text-slate-600 dark:text-slate-400">
|
||||
<p>
|
||||
Code Licensed Under GPL v3.0 |
|
||||
<a href="https://github.com/everywall/ladder" class="hover:text-blue-500 hover:underline underline-offset-2 transition-colors duration-300">Source</a> |
|
||||
<a href="https://github.com/everywall/ladder/releases" class="hover:text-blue-500 hover:underline underline-offset-2 transition-colors duration-300">VERSION</a>
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('inputForm').addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
let url = document.getElementById('inputField').value;
|
||||
if (url.indexOf('http') === -1) {
|
||||
url = 'https://' + url;
|
||||
}
|
||||
window.location.href = '/' + url;
|
||||
return false;
|
||||
});
|
||||
document.getElementById('inputField').addEventListener('input', function() {
|
||||
const clearButton = document.getElementById('clearButton');
|
||||
if (this.value.trim().length > 0) {
|
||||
clearButton.style.display = 'block';
|
||||
} else {
|
||||
clearButton.style.display = 'none';
|
||||
}
|
||||
});
|
||||
document.getElementById('clearButton').addEventListener('click', function() {
|
||||
document.getElementById('inputField').value = '';
|
||||
this.style.display = 'none';
|
||||
document.getElementById('inputField').focus();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@media (prefers-color-scheme: light) {
|
||||
body {
|
||||
background-color: #ffffff;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #1a202c;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -6,52 +6,214 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var (
|
||||
UserAgent = getenv("USER_AGENT", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)")
|
||||
ForwardedFor = getenv("X_FORWARDED_FOR", "66.249.66.1")
|
||||
rulesSet = loadRules()
|
||||
allowedDomains = strings.Split(os.Getenv("ALLOWED_DOMAINS"), ",")
|
||||
)
|
||||
|
||||
// extracts a URL from the request ctx. If the URL in the request
|
||||
// is a relative path, it reconstructs the full URL using the referer header.
|
||||
func extractUrl(c *fiber.Ctx) (string, error) {
|
||||
// try to extract url-encoded
|
||||
reqUrl, err := url.QueryUnescape(c.Params("*"))
|
||||
if err != nil {
|
||||
// fallback
|
||||
reqUrl = c.Params("*")
|
||||
}
|
||||
|
||||
// Extract the actual path from req ctx
|
||||
urlQuery, err := url.Parse(reqUrl)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error parsing request URL '%s': %v", reqUrl, err)
|
||||
}
|
||||
|
||||
isRelativePath := urlQuery.Scheme == ""
|
||||
|
||||
// eg: https://localhost:8080/images/foobar.jpg -> https://realsite.com/images/foobar.jpg
|
||||
if isRelativePath {
|
||||
// Parse the referer URL from the request header.
|
||||
refererUrl, err := url.Parse(c.Get("referer"))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error parsing referer URL from req: '%s': %v", reqUrl, err)
|
||||
}
|
||||
|
||||
// Extract the real url from referer path
|
||||
realUrl, err := url.Parse(strings.TrimPrefix(refererUrl.Path, "/"))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error parsing real URL from referer '%s': %v", refererUrl.Path, err)
|
||||
}
|
||||
|
||||
// reconstruct the full URL using the referer's scheme, host, and the relative path / queries
|
||||
fullUrl := &url.URL{
|
||||
Scheme: realUrl.Scheme,
|
||||
Host: realUrl.Host,
|
||||
Path: urlQuery.Path,
|
||||
RawQuery: urlQuery.RawQuery,
|
||||
}
|
||||
|
||||
if os.Getenv("LOG_URLS") == "true" {
|
||||
log.Printf("modified relative URL: '%s' -> '%s'", reqUrl, fullUrl.String())
|
||||
}
|
||||
return fullUrl.String(), nil
|
||||
|
||||
}
|
||||
|
||||
// default behavior:
|
||||
// eg: https://localhost:8080/https://realsite.com/images/foobar.jpg -> https://realsite.com/images/foobar.jpg
|
||||
return urlQuery.String(), nil
|
||||
|
||||
}
|
||||
|
||||
func ProxySite(c *fiber.Ctx) error {
|
||||
// Get the url from the URL
|
||||
urlQuery := c.Params("*")
|
||||
|
||||
u, err := url.Parse(urlQuery)
|
||||
url, err := extractUrl(c)
|
||||
if err != nil {
|
||||
log.Println("ERROR", err)
|
||||
c.SendStatus(500)
|
||||
log.Println("ERROR In URL extraction:", err)
|
||||
}
|
||||
|
||||
queries := c.Queries()
|
||||
body, _, resp, err := fetchSite(url, queries)
|
||||
if err != nil {
|
||||
log.Println("ERROR:", err)
|
||||
c.SendStatus(fiber.StatusInternalServerError)
|
||||
return c.SendString(err.Error())
|
||||
}
|
||||
|
||||
log.Println(u.String())
|
||||
c.Set("Content-Type", resp.Header.Get("Content-Type"))
|
||||
c.Set("Content-Security-Policy", resp.Header.Get("Content-Security-Policy"))
|
||||
|
||||
return c.SendString(body)
|
||||
}
|
||||
|
||||
func modifyURL(uri string, rule Rule) (string, error) {
|
||||
newUrl, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, urlMod := range rule.UrlMods.Domain {
|
||||
re := regexp.MustCompile(urlMod.Match)
|
||||
newUrl.Host = re.ReplaceAllString(newUrl.Host, urlMod.Replace)
|
||||
}
|
||||
|
||||
for _, urlMod := range rule.UrlMods.Path {
|
||||
re := regexp.MustCompile(urlMod.Match)
|
||||
newUrl.Path = re.ReplaceAllString(newUrl.Path, urlMod.Replace)
|
||||
}
|
||||
|
||||
v := newUrl.Query()
|
||||
for _, query := range rule.UrlMods.Query {
|
||||
if query.Value == "" {
|
||||
v.Del(query.Key)
|
||||
continue
|
||||
}
|
||||
v.Set(query.Key, query.Value)
|
||||
}
|
||||
newUrl.RawQuery = v.Encode()
|
||||
|
||||
if rule.GoogleCache {
|
||||
newUrl, err = url.Parse("https://webcache.googleusercontent.com/search?q=cache:" + newUrl.String())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return newUrl.String(), nil
|
||||
}
|
||||
|
||||
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 len(allowedDomains) > 0 && !StringInSlice(u.Host, allowedDomains) {
|
||||
return "", nil, nil, fmt.Errorf("domain not allowed. %s not in %s", u.Host, allowedDomains)
|
||||
}
|
||||
|
||||
if os.Getenv("LOG_URLS") == "true" {
|
||||
log.Println(u.String() + urlQuery)
|
||||
}
|
||||
|
||||
// Modify the URI according to ruleset
|
||||
rule := fetchRule(u.Host, u.Path)
|
||||
url, err := modifyURL(u.String()+urlQuery, rule)
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
|
||||
// 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)
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
|
||||
if rule.Headers.UserAgent != "" {
|
||||
req.Header.Set("User-Agent", rule.Headers.UserAgent)
|
||||
} else {
|
||||
req.Header.Set("User-Agent", UserAgent)
|
||||
}
|
||||
|
||||
if rule.Headers.XForwardedFor != "" {
|
||||
if rule.Headers.XForwardedFor != "none" {
|
||||
req.Header.Set("X-Forwarded-For", rule.Headers.XForwardedFor)
|
||||
}
|
||||
} else {
|
||||
req.Header.Set("X-Forwarded-For", ForwardedFor)
|
||||
}
|
||||
|
||||
if rule.Headers.Referer != "" {
|
||||
if rule.Headers.Referer != "none" {
|
||||
req.Header.Set("Referer", rule.Headers.Referer)
|
||||
}
|
||||
} else {
|
||||
req.Header.Set("Referer", u.String())
|
||||
}
|
||||
|
||||
if rule.Headers.Cookie != "" {
|
||||
req.Header.Set("Cookie", rule.Headers.Cookie)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return c.SendString(err.Error())
|
||||
return "", nil, nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
bodyB, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Println("ERROR", err)
|
||||
c.SendStatus(500)
|
||||
return c.SendString(err.Error())
|
||||
return "", nil, nil, err
|
||||
}
|
||||
|
||||
body := rewriteHtml(bodyB, u)
|
||||
c.Set("Content-Type", resp.Header.Get("Content-Type"))
|
||||
return c.SendString(body)
|
||||
if rule.Headers.CSP != "" {
|
||||
log.Println(rule.Headers.CSP)
|
||||
resp.Header.Set("Content-Security-Policy", rule.Headers.CSP)
|
||||
}
|
||||
|
||||
func rewriteHtml(bodyB []byte, u *url.URL) string {
|
||||
//log.Print("rule", rule) TODO: Add a debug mode to print the rule
|
||||
body := rewriteHtml(bodyB, u, rule)
|
||||
return body, req, resp, nil
|
||||
}
|
||||
|
||||
func rewriteHtml(bodyB []byte, u *url.URL, rule Rule) string {
|
||||
// Rewrite the HTML
|
||||
body := string(bodyB)
|
||||
|
||||
@@ -63,7 +225,7 @@ func rewriteHtml(bodyB []byte, u *url.URL) string {
|
||||
// scripts
|
||||
scriptPattern := `<script\s+([^>]*\s+)?src="(/)([^"]*)"`
|
||||
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, "href=\"/", "href=\"/https://"+u.Host+"/")
|
||||
@@ -71,5 +233,132 @@ func rewriteHtml(bodyB []byte, u *url.URL) string {
|
||||
body = strings.ReplaceAll(body, "url(/", "url(/https://"+u.Host+"/")
|
||||
body = strings.ReplaceAll(body, "href=\"https://"+u.Host, "href=\"/https://"+u.Host+"/")
|
||||
|
||||
if os.Getenv("RULESET") != "" {
|
||||
body = applyRules(body, rule)
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
func getenv(key, fallback string) string {
|
||||
value := os.Getenv(key)
|
||||
if len(value) == 0 {
|
||||
return fallback
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func loadRules() RuleSet {
|
||||
rulesUrl := os.Getenv("RULESET")
|
||||
if rulesUrl == "" {
|
||||
RulesList := RuleSet{}
|
||||
return RulesList
|
||||
}
|
||||
log.Println("Loading rules")
|
||||
|
||||
var ruleSet RuleSet
|
||||
if strings.HasPrefix(rulesUrl, "http") {
|
||||
|
||||
resp, err := http.Get(rulesUrl)
|
||||
if err != nil {
|
||||
log.Println("ERROR:", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
log.Println("ERROR:", resp.StatusCode, rulesUrl)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Println("ERROR:", err)
|
||||
}
|
||||
yaml.Unmarshal(body, &ruleSet)
|
||||
|
||||
if err != nil {
|
||||
log.Println("ERROR:", err)
|
||||
}
|
||||
} else {
|
||||
yamlFile, err := os.ReadFile(rulesUrl)
|
||||
if err != nil {
|
||||
log.Println("ERROR:", err)
|
||||
}
|
||||
yaml.Unmarshal(yamlFile, &ruleSet)
|
||||
}
|
||||
|
||||
domains := []string{}
|
||||
for _, rule := range ruleSet {
|
||||
|
||||
domains = append(domains, rule.Domain)
|
||||
domains = append(domains, rule.Domains...)
|
||||
if os.Getenv("ALLOWED_DOMAINS_RULESET") == "true" {
|
||||
allowedDomains = append(allowedDomains, domains...)
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("Loaded ", len(ruleSet), " rules for", len(domains), "Domains")
|
||||
return ruleSet
|
||||
}
|
||||
|
||||
func fetchRule(domain string, path string) Rule {
|
||||
if len(rulesSet) == 0 {
|
||||
return Rule{}
|
||||
}
|
||||
rule := Rule{}
|
||||
for _, rule := range rulesSet {
|
||||
domains := rule.Domains
|
||||
if rule.Domain != "" {
|
||||
domains = append(domains, rule.Domain)
|
||||
}
|
||||
for _, ruleDomain := range domains {
|
||||
if ruleDomain == domain || strings.HasSuffix(domain, ruleDomain) {
|
||||
if len(rule.Paths) > 0 && !StringInSlice(path, rule.Paths) {
|
||||
continue
|
||||
}
|
||||
// return first match
|
||||
return rule
|
||||
}
|
||||
}
|
||||
}
|
||||
return rule
|
||||
}
|
||||
|
||||
func applyRules(body string, rule Rule) string {
|
||||
if len(rulesSet) == 0 {
|
||||
return body
|
||||
}
|
||||
|
||||
for _, regexRule := range rule.RegexRules {
|
||||
re := regexp.MustCompile(regexRule.Match)
|
||||
body = re.ReplaceAllString(body, regexRule.Replace)
|
||||
}
|
||||
for _, injection := range rule.Injections {
|
||||
doc, err := goquery.NewDocumentFromReader(strings.NewReader(body))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if injection.Replace != "" {
|
||||
doc.Find(injection.Position).ReplaceWithHtml(injection.Replace)
|
||||
}
|
||||
if injection.Append != "" {
|
||||
doc.Find(injection.Position).AppendHtml(injection.Append)
|
||||
}
|
||||
if injection.Prepend != "" {
|
||||
doc.Find(injection.Position).PrependHtml(injection.Prepend)
|
||||
}
|
||||
body, err = doc.Html()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func StringInSlice(s string, list []string) bool {
|
||||
for _, x := range list {
|
||||
if strings.HasPrefix(s, x) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
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, Rule{})
|
||||
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
|
||||
23
handlers/ruleset.go
Normal file
23
handlers/ruleset.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func Ruleset(c *fiber.Ctx) error {
|
||||
if os.Getenv("EXPOSE_RULESET") == "false" {
|
||||
c.SendStatus(fiber.StatusForbidden)
|
||||
return c.SendString("Rules Disabled")
|
||||
}
|
||||
|
||||
body, err := yaml.Marshal(rulesSet)
|
||||
if err != nil {
|
||||
c.SendStatus(fiber.StatusInternalServerError)
|
||||
return c.SendString(err.Error())
|
||||
}
|
||||
|
||||
return c.SendString(string(body))
|
||||
}
|
||||
40
handlers/types.go
Normal file
40
handlers/types.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package handlers
|
||||
|
||||
type Regex struct {
|
||||
Match string `yaml:"match"`
|
||||
Replace string `yaml:"replace"`
|
||||
}
|
||||
type KV struct {
|
||||
Key string `yaml:"key"`
|
||||
Value string `yaml:"value"`
|
||||
}
|
||||
|
||||
type RuleSet []Rule
|
||||
|
||||
type Rule struct {
|
||||
Domain string `yaml:"domain,omitempty"`
|
||||
Domains []string `yaml:"domains,omitempty"`
|
||||
Paths []string `yaml:"paths,omitempty"`
|
||||
Headers struct {
|
||||
UserAgent string `yaml:"user-agent,omitempty"`
|
||||
XForwardedFor string `yaml:"x-forwarded-for,omitempty"`
|
||||
Referer string `yaml:"referer,omitempty"`
|
||||
Cookie string `yaml:"cookie,omitempty"`
|
||||
CSP string `yaml:"content-security-policy,omitempty"`
|
||||
} `yaml:"headers,omitempty"`
|
||||
GoogleCache bool `yaml:"googleCache,omitempty"`
|
||||
RegexRules []Regex `yaml:"regexRules"`
|
||||
|
||||
UrlMods struct {
|
||||
Domain []Regex `yaml:"domain"`
|
||||
Path []Regex `yaml:"path"`
|
||||
Query []KV `yaml:"query"`
|
||||
} `yaml:"urlMods"`
|
||||
|
||||
Injections []struct {
|
||||
Position string `yaml:"position"`
|
||||
Append string `yaml:"append"`
|
||||
Prepend string `yaml:"prepend"`
|
||||
Replace string `yaml:"replace"`
|
||||
} `yaml:"injections"`
|
||||
}
|
||||
6
helm-chart/Chart.yaml
Normal file
6
helm-chart/Chart.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: ladder
|
||||
description: A helm chart to deploy everywall/ladder
|
||||
type: application
|
||||
version: "1.0"
|
||||
appVersion: "v0.0.11"
|
||||
27
helm-chart/README.md
Normal file
27
helm-chart/README.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Helm Chart for deployment of Ladder
|
||||
This folder contains a basic helm chart deployment for the ladder app.
|
||||
|
||||
# Deployment pre-reqs
|
||||
## Values
|
||||
Edit the values to your own preferences, with the only minimum requirement being `ingress.HOST` (line 19) being updated to your intended domain name.
|
||||
|
||||
Other variables in `values.yaml` can be updated as to your preferences, with details on each variable being listed in the main [README.md](/README.md) in the root of this repo.
|
||||
|
||||
## Defaults in K8s
|
||||
No ingress default has been specified.
|
||||
You can set this manually by adding an annotation to the ingress.yaml - if needed.
|
||||
For example, to use Traefik -
|
||||
```yaml
|
||||
metadata:
|
||||
name: ladder-ingress
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: traefik
|
||||
```
|
||||
|
||||
## Helm Install
|
||||
`helm install <name> <location> -n <namespace-name> --create-namespace`
|
||||
`helm install ladder .\ladder\ -n ladder --create-namespace`
|
||||
|
||||
## Helm Upgrade
|
||||
`helm upgrade <name> <location> -n <namespace-name>`
|
||||
`helm upgrade ladder .\ladder\ -n ladder`
|
||||
55
helm-chart/templates/deployment.yaml
Normal file
55
helm-chart/templates/deployment.yaml
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: ladder
|
||||
name: ladder
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ladder
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ladder
|
||||
spec:
|
||||
containers:
|
||||
- image: "{{ .Values.image.RELEASE }}"
|
||||
imagePullPolicy: Always
|
||||
name: ladder
|
||||
resources:
|
||||
limits:
|
||||
cpu: 250m
|
||||
memory: 128Mi
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 128Mi
|
||||
env:
|
||||
- name: PORT
|
||||
value: "{{ .Values.env.PORT }}"
|
||||
- name: PREFORK
|
||||
value: "{{ .Values.env.PREFORK }}"
|
||||
- name: USER_AGENT
|
||||
value: "{{ .Values.env.USER_AGENT }}"
|
||||
- name: X_FORWARDED_FOR
|
||||
value: "{{ .Values.env.X_FORWARDED_FOR }}"
|
||||
- name: USERPASS
|
||||
value: "{{ .Values.env.USERPASS }}"
|
||||
- name: LOG_URLS
|
||||
value: "{{ .Values.env.LOG_URLS }}"
|
||||
- name: DISABLE_FORM
|
||||
value: "{{ .Values.env.DISABLE_FORM }}"
|
||||
- name: FORM_PATH
|
||||
value: "{{ .Values.env.FORM_PATH }}"
|
||||
- name: RULESET
|
||||
value: "{{ .Values.env.RULESET }}"
|
||||
- name: EXPOSE_RULESET
|
||||
value: "{{ .Values.env.EXPOSE_RULESET }}"
|
||||
- name: ALLOWED_DOMAINS
|
||||
value: "{{ .Values.env.ALLOWED_DOMAINS }}"
|
||||
- name: ALLOWED_DOMAINS_RULESET
|
||||
value: "{{ .Values.env.ALLOWED_DOMAINS_RULESET }}"
|
||||
restartPolicy: Always
|
||||
terminationGracePeriodSeconds: 30
|
||||
17
helm-chart/templates/ingress.yaml
Normal file
17
helm-chart/templates/ingress.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: ladder-ingress
|
||||
spec:
|
||||
rules:
|
||||
- host: "{{ .Values.ingress.HOST }}"
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: ladder-service
|
||||
port:
|
||||
number: {{ .Values.ingress.PORT }}
|
||||
14
helm-chart/templates/service.yaml
Normal file
14
helm-chart/templates/service.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: ladder-service
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: ladder
|
||||
ports:
|
||||
- name: http
|
||||
port: {{ .Values.ingress.PORT }}
|
||||
protocol: TCP
|
||||
targetPort: {{ .Values.env.PORT }}
|
||||
20
helm-chart/values.yaml
Normal file
20
helm-chart/values.yaml
Normal file
@@ -0,0 +1,20 @@
|
||||
image:
|
||||
RELEASE: ghcr.io/everywall/ladder:latest
|
||||
|
||||
env:
|
||||
PORT: 8080
|
||||
PREFORK: "false"
|
||||
USER_AGENT: "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
|
||||
X_FORWARDED_FOR:
|
||||
USERPASS: ""
|
||||
LOG_URLS: "true"
|
||||
DISABLE_FORM: "false"
|
||||
FORM_PATH: ""
|
||||
RULESET: "https://raw.githubusercontent.com/everywall/ladder/main/ruleset.yaml"
|
||||
EXPOSE_RULESET: "true"
|
||||
ALLOWED_DOMAINS: ""
|
||||
ALLOWED_DOMAINS_RULESET: "false"
|
||||
|
||||
ingress:
|
||||
HOST: "ladder.domain.com"
|
||||
PORT: 80
|
||||
9
package.json
Normal file
9
package.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"scripts": {
|
||||
"build": "pnpx tailwindcss -i ./styles/input.css -o ./styles/output.css --build && pnpx minify ./styles/output.css > ./cmd/styles.css"
|
||||
},
|
||||
"devDependencies": {
|
||||
"minify": "^10.5.2",
|
||||
"tailwindcss": "^3.3.5"
|
||||
}
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Ladder</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
|
||||
</head>
|
||||
<style>
|
||||
body {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
text-transform: uppercase;
|
||||
font-size: 70px;
|
||||
font-weight: 600;
|
||||
color: #fdfdfe;
|
||||
text-shadow: 0px 0px 5px #7AA7D1, 0px 0px 10px #7AA7D1, 0px 0px 10px #7AA7D1,
|
||||
0px 0px 20px #7AA7D1;
|
||||
}
|
||||
.logo-title {
|
||||
font-family: 'Arial', sans-serif;
|
||||
font-size: 2rem;
|
||||
color: #fff;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.logo {
|
||||
text-align: center;
|
||||
}
|
||||
.github-corner {
|
||||
animation: octocat-wave 560ms ease-in-out;
|
||||
position:absolute;
|
||||
top:0;
|
||||
right:0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="https://github.com/kubero-dev/ladder">
|
||||
<div class="github-corner" aria-label="View source on GitHub">
|
||||
<svg
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
version="1.1"
|
||||
width="146"
|
||||
height="146"
|
||||
id="svg2">
|
||||
<defs
|
||||
id="defs8">
|
||||
<filter
|
||||
height="1.096"
|
||||
y="-0.048"
|
||||
width="1.096"
|
||||
x="-0.048"
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter6">
|
||||
<feGaussianBlur
|
||||
stdDeviation="3"
|
||||
id="feGaussianBlur4" />
|
||||
</filter>
|
||||
</defs>
|
||||
<path
|
||||
d="M 152,140 6,-6 H 48 L 152,98 Z"
|
||||
style="opacity:0.8;filter:url(#filter6)"
|
||||
id="path10" />
|
||||
<path
|
||||
d="M 146,134 12,0 h 42 l 92,92 z"
|
||||
style="fill:#007200"
|
||||
id="path12" />
|
||||
<g
|
||||
aria-label="Fork me on GitHub"
|
||||
transform="rotate(45)"
|
||||
style="font-family:Collegiate;fill:#ffffff"
|
||||
id="g42">
|
||||
<path
|
||||
d="m 53.643,-19.486 c 0,0.688 -0.016,1.2 -0.064,1.504 h 2.08 c -0.048,-0.32 -0.064,-0.8 -0.064,-1.424 v -3.344 h 1.76 c 0.416,0 0.736,0.016 0.944,0.048 v -1.76 c -0.24,0.032 -0.592,0.048 -1.088,0.048 h -1.616 v -2.496 h 1.936 c 0.56,0 0.944,0.016 1.184,0.048 v -1.792 h -5.136 c 0.048,0.272 0.064,0.784 0.064,1.504 z"
|
||||
id="path14" />
|
||||
<path
|
||||
d="m 62.424,-17.87 c 1.008,0 1.776,-0.368 2.272,-1.088 0.432,-0.624 0.656,-1.472 0.656,-2.544 0,-2.416 -0.976,-3.616 -2.928,-3.616 -1.968,0 -2.96,1.2 -2.96,3.616 0,1.072 0.224,1.936 0.656,2.56 0.512,0.72 1.28,1.072 2.304,1.072 z m -0.016,-5.68 c 0.496,0 0.816,0.24 0.976,0.704 0.096,0.272 0.144,0.72 0.144,1.344 0,0.64 -0.048,1.088 -0.144,1.36 -0.16,0.464 -0.48,0.688 -0.976,0.688 -0.496,0 -0.816,-0.24 -0.976,-0.704 -0.096,-0.272 -0.144,-0.72 -0.144,-1.344 0,-0.624 0.048,-1.072 0.144,-1.344 0.16,-0.464 0.48,-0.704 0.976,-0.704 z"
|
||||
id="path16" />
|
||||
<path
|
||||
d="m 68.293,-17.982 c -0.032,-0.24 -0.048,-0.64 -0.048,-1.184 v -3.888 c 0.352,-0.304 0.672,-0.464 0.976,-0.464 0.224,0 0.48,0.096 0.752,0.288 v -1.808 c -0.224,-0.08 -0.432,-0.128 -0.624,-0.128 -0.448,0 -0.832,0.192 -1.152,0.56 v -0.48 h -1.744 c 0.032,0.192 0.048,0.544 0.048,1.04 v 4.976 c 0,0.512 -0.016,0.88 -0.048,1.088 z"
|
||||
id="path18" />
|
||||
<path
|
||||
d="m 72.857,-17.982 c -0.032,-0.24 -0.048,-0.64 -0.048,-1.184 v -2.448 l 1.472,2.816 c 0.208,0.384 0.32,0.656 0.368,0.816 h 1.872 l -2.352,-4.416 2.144,-2.752 h -2.064 c -0.08,0.176 -0.192,0.352 -0.352,0.56 l -1.088,1.44 v -4.496 c 0,-0.464 0.016,-0.8 0.048,-1.008 h -1.824 c 0.032,0.192 0.048,0.544 0.048,1.04 v 8.544 c 0,0.512 -0.016,0.88 -0.048,1.088 z"
|
||||
id="path20" />
|
||||
<path
|
||||
d="m 85.08,-24.478 c -0.384,-0.432 -0.896,-0.656 -1.52,-0.656 -0.416,0 -0.864,0.192 -1.328,0.56 v -0.512 l -1.76,-0.016 c 0.032,0.176 0.048,0.544 0.048,1.12 v 4.992 c 0,0.496 -0.016,0.832 -0.048,1.008 h 1.856 c 0,-0.064 -0.048,-0.64 -0.048,-1.008 v -3.984 c 0.304,-0.288 0.608,-0.432 0.928,-0.432 0.656,0 0.864,0.416 0.864,1.76 l -0.016,2.16 c 0,0.656 -0.032,1.168 -0.08,1.504 h 1.92 c -0.048,-0.256 -0.064,-0.752 -0.064,-1.472 v -2.192 c 0,-0.56 -0.048,-1.056 -0.144,-1.504 0.208,-0.176 0.544,-0.256 0.976,-0.256 0.64,0 0.96,0.592 0.96,1.76 v 2.16 c 0,0.656 -0.032,1.168 -0.08,1.504 h 1.904 c -0.048,-0.256 -0.064,-0.752 -0.064,-1.472 v -2.192 c 0,-0.96 -0.176,-1.744 -0.512,-2.368 -0.432,-0.752 -1.056,-1.12 -1.888,-1.12 -0.736,0 -1.376,0.224 -1.904,0.656 z"
|
||||
id="path22" />
|
||||
<path
|
||||
d="m 95.905,-20.99 c 0.032,-0.304 0.048,-0.624 0.048,-0.992 0,-0.944 -0.224,-1.696 -0.656,-2.256 -0.464,-0.592 -1.136,-0.896 -2.016,-0.896 -0.896,0 -1.6,0.368 -2.112,1.088 -0.464,0.656 -0.688,1.456 -0.688,2.432 0,1.136 0.272,2.048 0.832,2.704 0.576,0.72 1.392,1.072 2.448,1.072 0.496,0 1.056,-0.128 1.712,-0.368 v -1.712 c -0.464,0.304 -1.008,0.464 -1.6,0.464 -0.944,0 -1.44,-0.512 -1.52,-1.536 z m -2.576,-2.672 c 0.64,0 0.96,0.4 0.976,1.216 h -1.968 c 0.048,-0.816 0.368,-1.216 0.992,-1.216 z"
|
||||
id="path24" />
|
||||
<use
|
||||
xlink:href="#path16"
|
||||
transform="translate(40.438)"
|
||||
id="use26" />
|
||||
<path
|
||||
d="m 110.187,-25.15 c -0.496,0 -0.992,0.208 -1.472,0.64 v -0.576 h -1.76 c 0.032,0.176 0.048,0.56 0.048,1.184 v 4.912 c 0,0.496 -0.016,0.832 -0.048,1.008 h 1.856 c 0,-0.064 -0.048,-0.64 -0.048,-1.008 v -3.936 c 0.368,-0.352 0.736,-0.528 1.088,-0.528 0.784,0 1.168,0.608 1.152,1.808 l -0.016,2.16 c -0.016,0.752 -0.032,1.264 -0.064,1.504 h 1.92 c -0.048,-0.256 -0.064,-0.752 -0.064,-1.472 v -2.192 c 0,-0.944 -0.192,-1.744 -0.592,-2.384 -0.464,-0.752 -1.136,-1.136 -2,-1.12 z"
|
||||
id="path28" />
|
||||
<path
|
||||
d="m 123.877,-17.982 c 0.144,0.016 0.256,0.016 0.336,0 0,-0.192 -0.064,-0.768 -0.064,-1.36 v -1.856 c 0,-0.56 0.016,-1.056 0.064,-1.52 h -1.952 c 0.032,0.704 0.048,1.12 0.032,1.248 0,1.28 -0.512,1.92 -1.552,1.92 -1.264,0 -1.904,-1.264 -1.904,-3.776 0,-2.48 0.784,-3.728 2.352,-3.728 0.752,0 1.472,0.288 2.16,0.88 v -1.824 c -0.608,-0.528 -1.328,-0.8 -2.16,-0.8 -2.896,0 -4.416,1.904 -4.416,5.424 0,3.696 1.328,5.552 3.968,5.552 0.592,0 1.12,-0.128 1.584,-0.368 0.368,-0.208 0.624,-0.432 0.768,-0.688 z"
|
||||
id="path30" />
|
||||
<path
|
||||
d="m 126.49,-26.334 c 0.592,0 1.104,-0.544 1.104,-1.184 0,-0.656 -0.512,-1.2 -1.104,-1.2 -0.624,0 -1.12,0.544 -1.12,1.2 0,0.64 0.496,1.184 1.12,1.184 z m 0.896,8.352 c -0.016,-0.24 -0.032,-0.64 -0.032,-1.184 v -4.912 c 0,-0.464 0.016,-0.8 0.032,-1.008 h -1.808 c 0.016,0.192 0.032,0.544 0.032,1.04 v 4.976 c 0,0.512 -0.016,0.88 -0.032,1.088 z"
|
||||
id="path32" />
|
||||
<path
|
||||
d="m 130.783,-25.742 c 0,-0.256 0.016,-0.48 0.048,-0.688 h -1.856 c 0.032,0.176 0.048,0.416 0.048,0.72 v 0.624 h -0.784 v 1.552 c 0.224,-0.032 0.4,-0.048 0.544,-0.048 l 0.24,0.016 v 0.032 h -0.016 v 2.864 c 0,0.896 0.112,1.552 0.336,1.968 0.304,0.56 0.832,0.832 1.616,0.832 0.56,0 1.024,-0.112 1.424,-0.32 v -1.6 c -0.272,0.176 -0.56,0.272 -0.896,0.272 -0.464,0 -0.704,-0.352 -0.704,-1.072 v -2.976 h 0.688 c 0.256,0 0.592,0.032 0.704,0.032 v -1.552 h -1.392 z"
|
||||
id="path34" />
|
||||
<path
|
||||
d="m 140.259,-27.678 c 0,-0.416 0.016,-0.736 0.064,-0.976 h -2.096 c 0.048,0.24 0.064,0.688 0.064,1.344 v 2.8 h -2.912 v -3.024 c 0,-0.48 0.016,-0.848 0.064,-1.12 h -2.08 c 0.048,0.256 0.064,0.624 0.064,1.12 v 8.432 c 0,0.496 -0.016,0.864 -0.064,1.12 h 2.08 c -0.048,-0.24 -0.064,-0.656 -0.064,-1.232 v -3.552 h 2.912 v 3.568 c 0,0.528 -0.016,0.944 -0.064,1.216 h 2.096 c -0.048,-0.24 -0.064,-0.624 -0.064,-1.12 v -3.664 h 0.528 v -1.744 h -0.528 z"
|
||||
id="path36" />
|
||||
<path
|
||||
d="m 144.402,-17.918 c 0.56,0 1.072,-0.208 1.568,-0.64 v 0.576 h 1.744 c -0.016,-0.176 -0.032,-0.576 -0.032,-1.2 v -4.896 c 0,-0.496 0.016,-0.832 0.032,-1.008 h -1.856 c 0,0.048 0.064,0.64 0.064,1.008 v 3.936 c -0.368,0.352 -0.704,0.528 -1.008,0.528 -0.432,0 -0.72,-0.16 -0.88,-0.496 -0.144,-0.272 -0.208,-0.704 -0.208,-1.312 l 0.016,-2.16 c 0.016,-0.768 0.032,-1.264 0.064,-1.504 h -1.92 c 0.048,0.256 0.064,0.752 0.064,1.472 v 2.192 c 0,0.976 0.16,1.776 0.48,2.384 0.4,0.752 1.024,1.12 1.872,1.12 z"
|
||||
id="path38" />
|
||||
<path
|
||||
d="m 152.31,-17.934 c 0.848,0 1.536,-0.416 2.048,-1.232 0.432,-0.704 0.64,-1.536 0.64,-2.48 0,-0.928 -0.208,-1.712 -0.608,-2.368 -0.48,-0.752 -1.136,-1.12 -1.984,-1.12 -0.464,0 -0.944,0.16 -1.44,0.464 v -2.784 c 0,-0.608 0.016,-1.008 0.032,-1.2 h -1.824 c 0.032,0.176 0.048,0.576 0.048,1.2 v 8.464 c 0,0.496 -0.016,0.832 -0.048,1.008 h 1.696 v -0.576 c 0.384,0.416 0.864,0.624 1.44,0.624 z m -0.24,-5.52 c 0.736,0 1.104,0.608 1.104,1.808 0,0.496 -0.08,0.928 -0.256,1.296 -0.208,0.464 -0.528,0.688 -0.944,0.688 -0.336,0 -0.672,-0.16 -1.008,-0.496 v -2.768 c 0.384,-0.352 0.752,-0.528 1.104,-0.528 z"
|
||||
id="path40" />
|
||||
</g>
|
||||
<path
|
||||
d="m 52,0 94,94 M 14,0 146,132"
|
||||
style="fill:none;stroke:#ffffff;stroke-dasharray:2, 1;stroke-opacity:0.95"
|
||||
id="path44" />
|
||||
</svg>
|
||||
</div>
|
||||
</a>
|
||||
<div class="container">
|
||||
<div class="logo">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="250" height="250" viewBox="0 0 512 512">
|
||||
<path fill="#7AA7D1" d="M262.074 485.246C254.809 485.265 247.407 485.534 240.165 484.99L226.178 483.306C119.737 468.826 34.1354 383.43 25.3176 274.714C24.3655 262.975 23.5876 253.161 24.3295 241.148C31.4284 126.212 123.985 31.919 238.633 24.1259L250.022 23.8366C258.02 23.8001 266.212 23.491 274.183 24.1306C320.519 27.8489 366.348 45.9743 402.232 75.4548L416.996 88.2751C444.342 114.373 464.257 146.819 475.911 182.72L480.415 197.211C486.174 219.054 488.67 242.773 487.436 265.259L486.416 275.75C478.783 352.041 436.405 418.1 369.36 455.394L355.463 462.875C326.247 477.031 294.517 484.631 262.074 485.246ZM253.547 72.4475C161.905 73.0454 83.5901 144.289 73.0095 234.5C69.9101 260.926 74.7763 292.594 83.9003 317.156C104.53 372.691 153.9 416.616 211.281 430.903C226.663 434.733 242.223 436.307 258.044 436.227C353.394 435.507 430.296 361.835 438.445 267.978C439.794 252.442 438.591 236.759 435.59 221.5C419.554 139.955 353.067 79.4187 269.856 72.7052C264.479 72.2714 258.981 72.423 253.586 72.4127L253.547 72.4475Z"/>
|
||||
<path fill="#7AA7D1" d="M153.196 310.121L133.153 285.021C140.83 283.798 148.978 285.092 156.741 284.353L156.637 277.725L124.406 278.002C123.298 277.325 122.856 276.187 122.058 275.193L116.089 267.862C110.469 260.975 103.827 254.843 98.6026 247.669C103.918 246.839 105.248 246.537 111.14 246.523L129.093 246.327C130.152 238.785 128.62 240.843 122.138 240.758C111.929 240.623 110.659 242.014 105.004 234.661L97.9953 225.654C94.8172 221.729 91.2219 218.104 88.2631 214.005C84.1351 208.286 90.1658 209.504 94.601 209.489L236.752 209.545C257.761 209.569 268.184 211.009 285.766 221.678L285.835 206.051C285.837 197.542 286.201 189.141 284.549 180.748C280.22 158.757 260.541 143.877 240.897 135.739C238.055 134.561 232.259 133.654 235.575 129.851C244.784 119.288 263.68 111.99 277.085 111.105C288.697 109.828 301.096 113.537 311.75 117.703C360.649 136.827 393.225 183.042 398.561 234.866C402.204 270.253 391.733 308.356 367.999 335.1C332.832 374.727 269.877 384.883 223.294 360.397C206.156 351.388 183.673 333.299 175.08 316.6C173.511 313.551 174.005 313.555 170.443 313.52L160.641 313.449C158.957 313.435 156.263 314.031 155.122 312.487L153.196 310.121Z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<header>
|
||||
<h1 class="center-align logo-title">ladddddddder</h1>
|
||||
</header>
|
||||
<form id="inputForm" class="col s12" method="get">
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
<input type="text" id="inputField" name="inputField" class="validate" required>
|
||||
<label for="inputField">URL</label>
|
||||
</div>
|
||||
<!--
|
||||
<div class="input-field col s2">
|
||||
<button class="btn waves-effect waves-light" type="submit" name="action">Submit
|
||||
<i class="material-icons right">go</i>
|
||||
</button>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
M.AutoInit();
|
||||
});
|
||||
document.getElementById('inputForm').addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
const inputValue = document.getElementById('inputField').value;
|
||||
window.location.href = '/' + inputValue;
|
||||
return false;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
194
ruleset.yaml
Normal file
194
ruleset.yaml
Normal file
@@ -0,0 +1,194 @@
|
||||
- domain: example.com
|
||||
domains:
|
||||
- www.beispiel.de
|
||||
googleCache: true
|
||||
headers:
|
||||
x-forwarded-for: none
|
||||
referer: none
|
||||
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36
|
||||
cookie: privacy=1
|
||||
regexRules:
|
||||
- match: <script\s+([^>]*\s+)?src="(/)([^"]*)"
|
||||
replace: <script $1 script="/https://www.example.com/$3"
|
||||
injections:
|
||||
- position: head # Position where to inject the code
|
||||
append: |
|
||||
<script>
|
||||
window.localStorage.clear();
|
||||
console.log("test");
|
||||
alert("Hello!");
|
||||
</script>
|
||||
- position: h1
|
||||
replace: |
|
||||
<h1>An example with a ladder ;-)</h1>
|
||||
- domain: www.americanbanker.com
|
||||
paths:
|
||||
- /news
|
||||
injections:
|
||||
- position: head
|
||||
append: |
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const inlineGate = document.querySelector('.inline-gate');
|
||||
if (inlineGate) {
|
||||
inlineGate.classList.remove('inline-gate');
|
||||
const inlineGated = document.querySelectorAll('.inline-gated');
|
||||
for (const elem of inlineGated) { elem.classList.remove('inline-gated'); }
|
||||
}
|
||||
});
|
||||
</script>
|
||||
- domain: www.nzz.ch
|
||||
paths:
|
||||
- /international
|
||||
- /sport
|
||||
- /wirtschaft
|
||||
- /technologie
|
||||
- /feuilleton
|
||||
- /zuerich
|
||||
- /wissenschaft
|
||||
- /gesellschaft
|
||||
- /panorama
|
||||
- /mobilitaet
|
||||
- /reisen
|
||||
- /meinung
|
||||
- /finanze
|
||||
injections:
|
||||
- position: head
|
||||
append: |
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const paywall = document.querySelector('.dynamic-regwall');
|
||||
removeDOMElement(paywall)
|
||||
});
|
||||
</script>
|
||||
- domains:
|
||||
- www.architecturaldigest.com
|
||||
- www.bonappetit.com
|
||||
- www.cntraveler.com
|
||||
- www.epicurious.com
|
||||
- www.gq.com
|
||||
- www.newyorker.com
|
||||
- www.vanityfair.com
|
||||
- www.vogue.com
|
||||
- www.wired.com
|
||||
injections:
|
||||
- position: head
|
||||
append: |
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const banners = document.querySelectorAll('.paywall-bar, div[class^="MessageBannerWrapper-"');
|
||||
banners.forEach(el => { el.remove(); });
|
||||
});
|
||||
</script>
|
||||
- domains:
|
||||
- www.nytimes.com
|
||||
- www.time.com
|
||||
headers:
|
||||
ueser-agent: Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
|
||||
cookie: nyt-a=; nyt-gdpr=0; nyt-geo=DE; nyt-privacy=1
|
||||
referer: https://www.google.com/
|
||||
injections:
|
||||
- position: head
|
||||
append: |
|
||||
<script>
|
||||
window.localStorage.clear();
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const banners = document.querySelectorAll('div[data-testid="inline-message"], div[id^="ad-"], div[id^="leaderboard-"], div.expanded-dock, div.pz-ad-box, div[id="top-wrapper"], div[id="bottom-wrapper"]');
|
||||
banners.forEach(el => { el.remove(); });
|
||||
});
|
||||
</script>
|
||||
- domains:
|
||||
- www.thestar.com
|
||||
- www.niagarafallsreview.ca
|
||||
- www.stcatharinesstandard.ca
|
||||
- www.thepeterboroughexaminer.com
|
||||
- www.therecord.com
|
||||
- www.thespec.com
|
||||
- www.wellandtribune.ca
|
||||
injections:
|
||||
- position: head
|
||||
append: |
|
||||
<script>
|
||||
window.localStorage.clear();
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const paywall = document.querySelectorAll('div.subscriber-offers');
|
||||
paywall.forEach(el => { el.remove(); });
|
||||
const subscriber_only = document.querySelectorAll('div.subscriber-only');
|
||||
for (const elem of subscriber_only) {
|
||||
if (elem.classList.contains('encrypted-content') && dompurify_loaded) {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString('<div>' + DOMPurify.sanitize(unscramble(elem.innerText)) + '</div>', 'text/html');
|
||||
const content_new = doc.querySelector('div');
|
||||
elem.parentNode.replaceChild(content_new, elem);
|
||||
}
|
||||
elem.removeAttribute('style');
|
||||
elem.removeAttribute('class');
|
||||
}
|
||||
const banners = document.querySelectorAll('div.subscription-required, div.redacted-overlay, div.subscriber-hide, div.tnt-ads-container');
|
||||
banners.forEach(el => { el.remove(); });
|
||||
const ads = document.querySelectorAll('div.tnt-ads-container, div[class*="adLabelWrapper"]');
|
||||
ads.forEach(el => { el.remove(); });
|
||||
const recommendations = document.querySelectorAll('div[id^="tncms-region-article"]');
|
||||
recommendations.forEach(el => { el.remove(); });
|
||||
});
|
||||
</script>
|
||||
- domain: www.usatoday.com
|
||||
injections:
|
||||
- position: head
|
||||
append: |
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const banners = document.querySelectorAll('div.roadblock-container, .gnt_nb, [aria-label="advertisement"], div[id="main-frame-error"]');
|
||||
banners.forEach(el => { el.remove(); });
|
||||
});
|
||||
</script>
|
||||
- domain: www.washingtonpost.com
|
||||
injections:
|
||||
- position: head
|
||||
append: |
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
let paywall = document.querySelectorAll('div[data-qa$="-ad"], div[id="leaderboard-wrapper"], div[data-qa="subscribe-promo"]');
|
||||
paywall.forEach(el => { el.remove(); });
|
||||
const images = document.querySelectorAll('img');
|
||||
images.forEach(image => { image.parentElement.style.filter = ''; });
|
||||
const headimage = document.querySelectorAll('div .aspect-custom');
|
||||
headimage.forEach(image => { image.style.filter = ''; });
|
||||
});
|
||||
</script>
|
||||
- domain: medium.com
|
||||
headers:
|
||||
referer: https://t.co/x?amp=1
|
||||
x-forwarded-for: none
|
||||
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36
|
||||
content-security-policy: script-src 'self';
|
||||
cookie:
|
||||
- domain: tagesspiegel.de
|
||||
headers:
|
||||
content-security-policy: script-src 'self';
|
||||
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36
|
||||
urlMods:
|
||||
query:
|
||||
- key: amp
|
||||
value: 1
|
||||
- domain: www.ft.com
|
||||
headers:
|
||||
referer: https://t.co/x?amp=1
|
||||
injections:
|
||||
- position: head
|
||||
append: |
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const styleTags = document.querySelectorAll('link[rel="stylesheet"]');
|
||||
styleTags.forEach(el => {
|
||||
const href = el.getAttribute('href').substring(1);
|
||||
const updatedHref = href.replace(/(https?:\/\/.+?)\/{2,}/, '$1/');
|
||||
el.setAttribute('href', updatedHref);
|
||||
});
|
||||
setTimeout(() => {
|
||||
const cookie = document.querySelectorAll('.o-cookie-message, .js-article-ribbon, .o-ads, .o-banner, .o-message, .article__content-sign-up');
|
||||
cookie.forEach(el => { el.remove(); });
|
||||
}, 1000);
|
||||
})
|
||||
</script>
|
||||
|
||||
3
styles/input.css
Normal file
3
styles/input.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
9
tailwind.config.js
Normal file
9
tailwind.config.js
Normal file
@@ -0,0 +1,9 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./handlers/**/*.html"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user