mirror of
https://github.com/yusing/godoxy.git
synced 2025-06-09 04:52:35 +02:00
v0.5: readme and dockerfile update, removed old panel sources, added new frontend as submodule
This commit is contained in:
parent
73e481bc96
commit
a5a31a0d63
22 changed files with 42 additions and 718 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "frontend"]
|
||||||
|
path = frontend
|
||||||
|
url = https://github.com/yusing/go-proxy-frontend
|
13
Dockerfile
13
Dockerfile
|
@ -1,9 +1,4 @@
|
||||||
FROM alpine:latest AS codemirror
|
FROM golang:1.22.6-alpine as builder
|
||||||
RUN apk add --no-cache unzip wget make
|
|
||||||
COPY Makefile .
|
|
||||||
RUN make setup-codemirror
|
|
||||||
|
|
||||||
FROM golang:1.22.4-alpine as builder
|
|
||||||
COPY src /src
|
COPY src /src
|
||||||
ENV GOCACHE=/root/.cache/go-build
|
ENV GOCACHE=/root/.cache/go-build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
@ -18,10 +13,8 @@ FROM alpine:latest
|
||||||
LABEL maintainer="yusing@6uo.me"
|
LABEL maintainer="yusing@6uo.me"
|
||||||
|
|
||||||
RUN apk add --no-cache tzdata
|
RUN apk add --no-cache tzdata
|
||||||
RUN mkdir -p /app/templates
|
|
||||||
COPY --from=codemirror templates/codemirror/ /app/templates/codemirror
|
|
||||||
COPY templates/ /app/templates
|
|
||||||
COPY schema/ /app/schema
|
COPY schema/ /app/schema
|
||||||
|
# copy binary
|
||||||
COPY --from=builder /src/go-proxy /app/
|
COPY --from=builder /src/go-proxy /app/
|
||||||
|
|
||||||
RUN chmod +x /app/go-proxy
|
RUN chmod +x /app/go-proxy
|
||||||
|
@ -29,7 +22,7 @@ ENV DOCKER_HOST unix:///var/run/docker.sock
|
||||||
ENV GOPROXY_DEBUG 0
|
ENV GOPROXY_DEBUG 0
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
EXPOSE 8080
|
EXPOSE 8888
|
||||||
EXPOSE 443
|
EXPOSE 443
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
7
Makefile
7
Makefile
|
@ -7,13 +7,6 @@ setup:
|
||||||
[ -f config/config.yml ] || cp config.example.yml config/config.yml
|
[ -f config/config.yml ] || cp config.example.yml config/config.yml
|
||||||
[ -f config/providers.yml ] || touch config/providers.yml
|
[ -f config/providers.yml ] || touch config/providers.yml
|
||||||
|
|
||||||
setup-codemirror:
|
|
||||||
wget https://codemirror.net/5/codemirror.zip
|
|
||||||
unzip codemirror.zip
|
|
||||||
rm codemirror.zip
|
|
||||||
mkdir -p templates
|
|
||||||
mv codemirror-* templates/codemirror
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
mkdir -p bin
|
mkdir -p bin
|
||||||
CGO_ENABLED=0 GOOS=linux go build -pgo=auto -o bin/go-proxy github.com/yusing/go-proxy
|
CGO_ENABLED=0 GOOS=linux go build -pgo=auto -o bin/go-proxy github.com/yusing/go-proxy
|
||||||
|
|
27
README.md
27
README.md
|
@ -5,15 +5,17 @@ A [lightweight](docs/benchmark_result.md), easy-to-use, and efficient reverse pr
|
||||||
**Table of content**
|
**Table of content**
|
||||||
|
|
||||||
<!-- TOC -->
|
<!-- TOC -->
|
||||||
- [Key Points](#key-points)
|
|
||||||
- [Getting Started](#getting-started)
|
- [go-proxy](#go-proxy)
|
||||||
- [Commands](#commands)
|
- [Key Points](#key-points)
|
||||||
- [Environment variables](#environment-variables)
|
- [Getting Started](#getting-started)
|
||||||
- [Use JSON Schema in VSCode](#use-json-schema-in-vscode)
|
- [Commands](#commands)
|
||||||
- [Config File](#config-file)
|
- [Environment variables](#environment-variables)
|
||||||
- [Provider File](#provider-file)
|
- [Use JSON Schema in VSCode](#use-json-schema-in-vscode)
|
||||||
- [Known issues](#known-issues)
|
- [Config File](#config-file)
|
||||||
- [Build it yourself](#build-it-yourself)
|
- [Provider File](#provider-file)
|
||||||
|
- [Known issues](#known-issues)
|
||||||
|
- [Build it yourself](#build-it-yourself)
|
||||||
<!-- /TOC -->
|
<!-- /TOC -->
|
||||||
|
|
||||||
## Key Points
|
## Key Points
|
||||||
|
@ -36,14 +38,11 @@ A [lightweight](docs/benchmark_result.md), easy-to-use, and efficient reverse pr
|
||||||
- A Record: `*.y.z` -> `10.0.10.1`
|
- A Record: `*.y.z` -> `10.0.10.1`
|
||||||
- AAAA Record: `*.y.z` -> `::ffff:a00:a01`
|
- AAAA Record: `*.y.z` -> `::ffff:a00:a01`
|
||||||
|
|
||||||
2. Start `go-proxy`
|
2. Setup `go-proxy` [See here](docs/docker.md)
|
||||||
|
|
||||||
- [Binary / systemd service](docs/binary.md)
|
|
||||||
- [Docker](docs/docker.md)
|
|
||||||
|
|
||||||
3. Configure `go-proxy`
|
3. Configure `go-proxy`
|
||||||
- with text editor (i.e. Visual Studio Code)
|
- with text editor (i.e. Visual Studio Code)
|
||||||
- or with web config editor via `http://ip:8080`
|
- or with web config editor via `http://gp.y.z`
|
||||||
|
|
||||||
[🔼Back to top](#table-of-content)
|
[🔼Back to top](#table-of-content)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,13 @@
|
||||||
services:
|
services:
|
||||||
|
frontend:
|
||||||
|
image: ghcr.io/yusing/go-proxy-frontend:latest
|
||||||
|
container_name: go-proxy-frontend
|
||||||
|
restart: unless-stopped
|
||||||
|
network_mode: host
|
||||||
|
labels:
|
||||||
|
- proxy.*.aliases=gp
|
||||||
|
depends_on:
|
||||||
|
- app
|
||||||
app:
|
app:
|
||||||
image: ghcr.io/yusing/go-proxy:latest
|
image: ghcr.io/yusing/go-proxy:latest
|
||||||
container_name: go-proxy
|
container_name: go-proxy
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
# Getting started with `go-proxy` (binary)
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
1. Install `bash`, `make` and `wget` if not already
|
|
||||||
|
|
||||||
2. Run setup script
|
|
||||||
|
|
||||||
To specitfy a version _(optional)_
|
|
||||||
|
|
||||||
```shell
|
|
||||||
export VERSION=latest # will be resolved into real version number
|
|
||||||
export VERSION=<version>
|
|
||||||
```
|
|
||||||
|
|
||||||
If you don't need web config editor
|
|
||||||
|
|
||||||
```shell
|
|
||||||
export SETUP_CODEMIRROR=0
|
|
||||||
```
|
|
||||||
|
|
||||||
Setup
|
|
||||||
|
|
||||||
```shell
|
|
||||||
wget -qO- https://github.com/yusing/go-proxy/raw/main/setup-binary.sh | sudo bash
|
|
||||||
```
|
|
||||||
|
|
||||||
What it does:
|
|
||||||
|
|
||||||
- Download source file and binary into /opt/go-proxy/$VERSION
|
|
||||||
- Setup `config.yml` and `providers.yml`
|
|
||||||
- Setup `template/codemirror` which is a dependency for web config editor
|
|
||||||
- Create a systemd service (if available) in `/etc/systemd/system/go-proxy.service`
|
|
||||||
- Enable and start `go-proxy` service
|
|
||||||
|
|
||||||
3. Start editing config files in `http://<ip>:8080`
|
|
||||||
|
|
||||||
4. Check logs / status with `systemctl status go-proxy`
|
|
||||||
|
|
||||||
## Setup (alternative method)
|
|
||||||
|
|
||||||
1. Download the latest release and extract somewhere
|
|
||||||
|
|
||||||
2. Run `make setup` and _(optional) `make setup-codemirror`_
|
|
||||||
|
|
||||||
3. Enable HTTPS _(optional)_
|
|
||||||
|
|
||||||
- To use autocert feature
|
|
||||||
|
|
||||||
complete `autocert` in `config/config.yml`
|
|
||||||
|
|
||||||
- To use existing certificate
|
|
||||||
|
|
||||||
Prepare your wildcard (`*.y.z`) SSL cert in `certs/`
|
|
||||||
|
|
||||||
- cert / chain / fullchain: `certs/cert.crt`
|
|
||||||
- private key: `certs/priv.key`
|
|
||||||
|
|
||||||
4. Run the binary `bin/go-proxy`
|
|
133
docs/docker.md
133
docs/docker.md
|
@ -3,21 +3,16 @@
|
||||||
## Table of content
|
## Table of content
|
||||||
|
|
||||||
<!-- TOC -->
|
<!-- TOC -->
|
||||||
- [Table of content](#table-of-content)
|
|
||||||
- [Setup](#setup)
|
- [Docker container guide](#docker-container-guide)
|
||||||
- [Labels](#labels)
|
- [Table of content](#table-of-content)
|
||||||
- [Labels (docker specific)](#labels-docker-specific)
|
- [Setup](#setup)
|
||||||
- [Troubleshooting](#troubleshooting)
|
- [Labels](#labels)
|
||||||
- [Docker compose examples](#docker-compose-examples)
|
- [Troubleshooting](#troubleshooting)
|
||||||
- [Local docker provider in bridge network](#local-docker-provider-in-bridge-network)
|
- [Docker compose examples](#docker-compose-examples)
|
||||||
- [Remote docker provider](#remote-docker-provider)
|
- [Local docker provider in bridge network](#local-docker-provider-in-bridge-network)
|
||||||
- [Explaination](#explaination)
|
- [Proxy setup](#proxy-setup)
|
||||||
- [Remote setup](#remote-setup)
|
- [Services URLs for above examples](#services-urls-for-above-examples)
|
||||||
- [Proxy setup](#proxy-setup)
|
|
||||||
- [Local docker provider in host network](#local-docker-provider-in-host-network)
|
|
||||||
- [Proxy setup](#proxy-setup)
|
|
||||||
- [Services URLs for above examples](#services-urls-for-above-examples)
|
|
||||||
<!-- /TOC -->
|
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
|
@ -124,10 +119,6 @@ _i.e. `proxy.nginx.scheme: http`_
|
||||||
|
|
||||||
- `hide_headers`: comma seperated list of headers to hide
|
- `hide_headers`: comma seperated list of headers to hide
|
||||||
|
|
||||||
[🔼Back to top](#table-of-content)
|
|
||||||
|
|
||||||
**docker only:**
|
|
||||||
|
|
||||||
- `load_balance`: enable load balance
|
- `load_balance`: enable load balance
|
||||||
- allowed: `1`, `true`
|
- allowed: `1`, `true`
|
||||||
|
|
||||||
|
@ -232,84 +223,6 @@ services:
|
||||||
|
|
||||||
[🔼Back to top](#table-of-content)
|
[🔼Back to top](#table-of-content)
|
||||||
|
|
||||||
### Remote docker provider
|
|
||||||
|
|
||||||
#### Explaination
|
|
||||||
|
|
||||||
- Expose container ports to random port in remote host
|
|
||||||
- Use container port with an asterisk sign **(\*)** before to find remote port automatically
|
|
||||||
|
|
||||||
#### Remote setup
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
volumes:
|
|
||||||
adg-work:
|
|
||||||
adg-conf:
|
|
||||||
mc-data:
|
|
||||||
palworld:
|
|
||||||
nginx:
|
|
||||||
services:
|
|
||||||
adg:
|
|
||||||
image: adguard/adguardhome
|
|
||||||
restart: unless-stopped
|
|
||||||
ports: # map container ports
|
|
||||||
- 80
|
|
||||||
- 3000
|
|
||||||
- 53/udp
|
|
||||||
- 53/tcp
|
|
||||||
labels:
|
|
||||||
- proxy.aliases=adg,adg-dns,adg-setup
|
|
||||||
# add an asterisk (*) before to find host port automatically
|
|
||||||
- proxy.adg.port=*80
|
|
||||||
- proxy.adg-setup.port=*3000
|
|
||||||
- proxy.adg-dns.scheme=udp
|
|
||||||
- proxy.adg-dns.port=*53
|
|
||||||
volumes:
|
|
||||||
- adg-work:/opt/adguardhome/work
|
|
||||||
- adg-conf:/opt/adguardhome/conf
|
|
||||||
mc:
|
|
||||||
image: itzg/minecraft-server
|
|
||||||
tty: true
|
|
||||||
stdin_open: true
|
|
||||||
container_name: mc
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- 25565
|
|
||||||
labels:
|
|
||||||
- proxy.mc.scheme=tcp
|
|
||||||
- proxy.mc.port=*25565
|
|
||||||
environment:
|
|
||||||
- EULA=TRUE
|
|
||||||
volumes:
|
|
||||||
- mc-data:/data
|
|
||||||
palworld:
|
|
||||||
image: thijsvanloef/palworld-server-docker:latest
|
|
||||||
restart: unless-stopped
|
|
||||||
container_name: pal
|
|
||||||
stop_grace_period: 30s
|
|
||||||
ports:
|
|
||||||
- 8211/udp
|
|
||||||
- 27015/udp
|
|
||||||
labels:
|
|
||||||
- proxy.aliases=pal1,pal2
|
|
||||||
- proxy.*.scheme=udp
|
|
||||||
- proxy.pal1.port=*8211
|
|
||||||
- proxy.pal2.port=*27015
|
|
||||||
environment: ...
|
|
||||||
volumes:
|
|
||||||
- palworld:/palworld
|
|
||||||
nginx:
|
|
||||||
image: nginx
|
|
||||||
container_name: nginx
|
|
||||||
# for single port container, host port will be found automatically
|
|
||||||
ports:
|
|
||||||
- 80
|
|
||||||
volumes:
|
|
||||||
- nginx:/usr/share/nginx/html
|
|
||||||
```
|
|
||||||
|
|
||||||
[🔼Back to top](#table-of-content)
|
|
||||||
|
|
||||||
#### Proxy setup
|
#### Proxy setup
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -328,32 +241,6 @@ go-proxy:
|
||||||
|
|
||||||
[🔼Back to top](#table-of-content)
|
[🔼Back to top](#table-of-content)
|
||||||
|
|
||||||
### Local docker provider in host network
|
|
||||||
|
|
||||||
Mostly as remote docker setup, see [remote setup](#remote-setup)
|
|
||||||
|
|
||||||
With `GOPROXY_HOST_NETWORK=1` to treat it as remote docker provider
|
|
||||||
|
|
||||||
#### Proxy setup
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
go-proxy:
|
|
||||||
image: ghcr.io/yusing/go-proxy
|
|
||||||
container_name: go-proxy
|
|
||||||
restart: always
|
|
||||||
network_mode: host
|
|
||||||
environment: # this part is needed for local docker in host mode
|
|
||||||
- GOPROXY_HOST_NETWORK=1
|
|
||||||
volumes:
|
|
||||||
- ./config:/app/config
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
||||||
labels:
|
|
||||||
- proxy.aliases=gp
|
|
||||||
- proxy.gp.port=8080
|
|
||||||
```
|
|
||||||
|
|
||||||
[🔼Back to top](#table-of-content)
|
|
||||||
|
|
||||||
### Services URLs for above examples
|
### Services URLs for above examples
|
||||||
|
|
||||||
- `gp.yourdomain.com`: go-proxy web panel
|
- `gp.yourdomain.com`: go-proxy web panel
|
||||||
|
|
1
frontend
Submodule
1
frontend
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 8cdf9eaa10728ca808e479ae0144b5f7a62d9a88
|
Binary file not shown.
Before Width: | Height: | Size: 191 KiB |
Binary file not shown.
Before Width: | Height: | Size: 304 KiB |
|
@ -152,6 +152,7 @@ func (p *Provider) loadRoutes() E.NestedError {
|
||||||
|
|
||||||
errors := E.NewBuilder("errors loading routes from provider %q", p.name)
|
errors := E.NewBuilder("errors loading routes from provider %q", p.name)
|
||||||
entries.EachKV(func(a string, e *M.ProxyEntry) {
|
entries.EachKV(func(a string, e *M.ProxyEntry) {
|
||||||
|
e.Alias = a
|
||||||
r, err := R.NewRoute(e)
|
r, err := R.NewRoute(e)
|
||||||
if err.IsNotNil() {
|
if err.IsNotNil() {
|
||||||
errors.Addf("%s: %w", a, err)
|
errors.Addf("%s: %w", a, err)
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<link href="/codemirror/lib/codemirror.css" rel="stylesheet" />
|
|
||||||
<link href="/codemirror/theme/dracula.css" rel="stylesheet" />
|
|
||||||
<link href="style.css" rel="stylesheet" />
|
|
||||||
<title>Config Editor</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<div class="file-navigation">
|
|
||||||
<h3 class="navigation-header">Config Files</h3>
|
|
||||||
<ul id="file-list">
|
|
||||||
{{- range $_, $cfgFile := .}}
|
|
||||||
<li id="file-{{$cfgFile}}">
|
|
||||||
<a class="unselectable">{{$cfgFile}}</a>
|
|
||||||
</li>
|
|
||||||
{{- end}}
|
|
||||||
<li id="new-file">
|
|
||||||
<a class="unselectable">+</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div id="config-editor"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="/codemirror/lib/codemirror.js"></script>
|
|
||||||
<script src="/codemirror/mode/yaml/yaml.js"></script>
|
|
||||||
<script src="/codemirror/keymap/sublime.js"></script>
|
|
||||||
<script src="/codemirror/addon/comment/comment.js"></script>
|
|
||||||
<script src="index.js" onload="onLoad()"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,114 +0,0 @@
|
||||||
let currentFile = "config.yml";
|
|
||||||
let editorElement = document.getElementById("config-editor");
|
|
||||||
let fileListElement = document.getElementById("file-list");
|
|
||||||
let editor = CodeMirror(editorElement, {
|
|
||||||
lineNumbers: true,
|
|
||||||
mode: "yaml",
|
|
||||||
theme: "dracula",
|
|
||||||
autofocus: true,
|
|
||||||
lineWiseCopyCut: true,
|
|
||||||
keyMap: "sublime",
|
|
||||||
tabSize: 2
|
|
||||||
});
|
|
||||||
|
|
||||||
function setCurrentFile(filename) {
|
|
||||||
let old_nav_item = document.getElementById(`file-${currentFile}`);
|
|
||||||
if (old_nav_item !== null) {
|
|
||||||
old_nav_item.classList.remove("active");
|
|
||||||
}
|
|
||||||
currentFile = filename;
|
|
||||||
document.title = `${currentFile} - Config Editor`;
|
|
||||||
let new_nav_item = document.getElementById(`file-${currentFile}`);
|
|
||||||
if (new_nav_item === null) {
|
|
||||||
new_file_btn = document.getElementById("new-file");
|
|
||||||
file_list = document.getElementById("file-list");
|
|
||||||
new_nav_item = document.createElement("li");
|
|
||||||
new_nav_item.id = `file-${currentFile}`;
|
|
||||||
new_nav_item.innerHTML = `<a class="unselectable">${currentFile}</a>`;
|
|
||||||
file_list.insertBefore(new_nav_item, new_file_btn);
|
|
||||||
}
|
|
||||||
new_nav_item.classList.add("active");
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadFile(filename) {
|
|
||||||
if (filename === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (filename === '+') {
|
|
||||||
newFile();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let req = new XMLHttpRequest();
|
|
||||||
req.open("GET", `/config/${filename}`, true);
|
|
||||||
req.onreadystatechange = function () {
|
|
||||||
if (req.readyState == 4) {
|
|
||||||
if (req.status == 200) {
|
|
||||||
editor.setValue(req.responseText);
|
|
||||||
setCurrentFile(filename);
|
|
||||||
console.log(`loaded ${currentFile}`);
|
|
||||||
} else {
|
|
||||||
let msg = `Failed to load ${filename}: ` + req.responseText;
|
|
||||||
alert(msg);
|
|
||||||
console.log(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
req.send();
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveFile(filename, content) {
|
|
||||||
let req = new XMLHttpRequest();
|
|
||||||
req.open("PUT", `/config/${filename}`, true);
|
|
||||||
req.setRequestHeader("Content-Type", "text/plain");
|
|
||||||
req.send(content);
|
|
||||||
req.onreadystatechange = function () {
|
|
||||||
if (req.readyState == 4) {
|
|
||||||
if (req.status == 200) {
|
|
||||||
alert(req.responseText);
|
|
||||||
} else {
|
|
||||||
alert("Error:\n" + req.responseText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function newFile() {
|
|
||||||
let filename = prompt("Enter filename:");
|
|
||||||
if (filename === undefined || filename === "") {
|
|
||||||
alert("File name cannot be empty");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!filename.endsWith(".yml") && !filename.endsWith(".yaml")) {
|
|
||||||
alert("File name must end with .yml or .yaml");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let files = document.getElementById("file-list").children;
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
|
||||||
if (files[i].id === `file-${filename}`) {
|
|
||||||
alert("File already exists");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
editor.setValue("");
|
|
||||||
setCurrentFile(filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.setSize("100wh", "100vh");
|
|
||||||
editor.setOption("extraKeys", {
|
|
||||||
Tab: function (cm) {
|
|
||||||
const spaces = Array(cm.getOption("indentUnit") + 1).join(" ");
|
|
||||||
cm.replaceSelection(spaces);
|
|
||||||
},
|
|
||||||
"Ctrl-S": function (cm) {
|
|
||||||
saveFile(currentFile, cm.getValue());
|
|
||||||
},
|
|
||||||
});
|
|
||||||
fileListElement.addEventListener("click", function (e) {
|
|
||||||
if (e.target === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
loadFile(e.target.text);
|
|
||||||
});
|
|
||||||
function onLoad() {
|
|
||||||
loadFile(currentFile);
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
html,
|
|
||||||
body {
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
font: 14px !important;
|
|
||||||
font-family: monospace !important;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.navigation-header {
|
|
||||||
color: #f8f8f2 !important;
|
|
||||||
padding-left: 2em;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.file-navigation {
|
|
||||||
width: 250px;
|
|
||||||
height: auto;
|
|
||||||
overflow-y: auto;
|
|
||||||
background: #282a36 !important;
|
|
||||||
}
|
|
||||||
.file-navigation ul {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.file-navigation li {
|
|
||||||
padding-top: 8px;
|
|
||||||
padding-bottom: 8px;
|
|
||||||
}
|
|
||||||
.file-navigation a {
|
|
||||||
color: #f8f8f2 !important;
|
|
||||||
text-decoration: none;
|
|
||||||
padding-left: 4em;
|
|
||||||
padding-right: 4em;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
#new-file {
|
|
||||||
color: #f8f8f2 !important;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.active {
|
|
||||||
font-weight: bold;
|
|
||||||
background: rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
.unselectable {
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-khtml-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
.CodeMirror * {
|
|
||||||
font-size: 14px !important;
|
|
||||||
}
|
|
||||||
.CodeMirror pre {
|
|
||||||
padding-top: 3px;
|
|
||||||
padding-bottom: 3px;
|
|
||||||
}
|
|
||||||
#config-editor {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<link href="style.css" rel="stylesheet" />
|
|
||||||
<title>go-proxy</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script src="main.js"></script>
|
|
||||||
<div id="sidenav" class="sidenav">
|
|
||||||
<a href="javascript:void(0)" class="closebtn" onclick="closeNav()"
|
|
||||||
>×</a
|
|
||||||
>
|
|
||||||
<a href="#" onClick='setContent("/panel")'>Panel</a>
|
|
||||||
<a href="#" onClick='setContent("/config_editor")'>Config Editor</a>
|
|
||||||
</div>
|
|
||||||
<a class="openbtn" id="openbtn" onclick="openNav()">≡</a>
|
|
||||||
<div id="main">
|
|
||||||
<iframe id="content" src="/config_editor" title="panel"></iframe>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,27 +0,0 @@
|
||||||
function contentIFrame() {
|
|
||||||
return document.getElementById("content");
|
|
||||||
}
|
|
||||||
|
|
||||||
function openNavBtn() {
|
|
||||||
return document.getElementById("openbtn");
|
|
||||||
}
|
|
||||||
|
|
||||||
function sideNav() {
|
|
||||||
return document.getElementById("sidenav");
|
|
||||||
}
|
|
||||||
|
|
||||||
function setContent(path) {
|
|
||||||
contentIFrame().attributes.src.value = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
function openNav() {
|
|
||||||
sideNav().style.width = "250px";
|
|
||||||
contentIFrame().style.marginLeft = "250px";
|
|
||||||
openNavBtn().style.display = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeNav() {
|
|
||||||
sideNav().style.width = "0";
|
|
||||||
contentIFrame().style.marginLeft = "0px";
|
|
||||||
openNavBtn().style.display = "inline-block";
|
|
||||||
}
|
|
6
templates/panel/bootstrap.min.css
vendored
6
templates/panel/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
|
@ -1,79 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<link href="bootstrap.min.css" rel="stylesheet" />
|
|
||||||
<link href="style.css" rel="stylesheet" />
|
|
||||||
<title>Route Panel</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="m-3">
|
|
||||||
<script src="index.js" defer></script>
|
|
||||||
<div class="container">
|
|
||||||
<h1 class="text-success">Route Panel</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="table-responsive col-md-auto flex-shrink-1">
|
|
||||||
<table class="table table-striped table-dark caption-top">
|
|
||||||
<caption>
|
|
||||||
HTTP Proxies
|
|
||||||
</caption>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Alias</th>
|
|
||||||
<th>Path</th>
|
|
||||||
<th>Path Mode</th>
|
|
||||||
<th>URL</th>
|
|
||||||
<th>Health</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{{range $alias, $pathPoolMap := .HTTPRoutes.Iterator}} {{range
|
|
||||||
$path, $lbPool := $pathPoolMap.Iterator}} {{range $_, $route :=
|
|
||||||
$lbPool.Iterator}}
|
|
||||||
<tr>
|
|
||||||
<td>{{$alias}}</td>
|
|
||||||
<td>{{$path}}</td>
|
|
||||||
<td>{{$route.PathMode}}</td>
|
|
||||||
<td id="url-cell">{{$route.Url.String}}</td>
|
|
||||||
<td class="align-middle" id="health-cell">
|
|
||||||
<div class="health-circle"></div>
|
|
||||||
</td>
|
|
||||||
<!-- Health column -->
|
|
||||||
</tr>
|
|
||||||
{{end}} {{end}} {{end}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class="table-responsive col-md">
|
|
||||||
<table class="table table-striped table-dark caption-top w-auto">
|
|
||||||
<caption>
|
|
||||||
Streams
|
|
||||||
</caption>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Alias</th>
|
|
||||||
<th>Source</th>
|
|
||||||
<th>Target</th>
|
|
||||||
<th>Health</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{{range $_, $route := .StreamRoutes.Iterator}}
|
|
||||||
<tr>
|
|
||||||
<td>{{$route.Alias}}</td>
|
|
||||||
<td>{{$route.ListeningUrl}}</td>
|
|
||||||
<td id="url-cell">{{$route.TargetUrl}}</td>
|
|
||||||
<td class="align-middle" id="health-cell">
|
|
||||||
<div class="health-circle"></div>
|
|
||||||
</td>
|
|
||||||
<!-- Health column -->
|
|
||||||
</tr>
|
|
||||||
{{end}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,34 +0,0 @@
|
||||||
function checkHealth(url, cell) {
|
|
||||||
var xhttp = new XMLHttpRequest();
|
|
||||||
xhttp.onreadystatechange = function () {
|
|
||||||
if (this.readyState != 4) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.status === 200) {
|
|
||||||
cell.innerHTML = '<div class="health-circle"></div>'; // Green circle for healthy
|
|
||||||
} else {
|
|
||||||
cell.innerHTML =
|
|
||||||
'<div class="health-circle" style="background-color: #dc3545;"></div>'; // Red circle for unhealthy
|
|
||||||
}
|
|
||||||
};
|
|
||||||
url =
|
|
||||||
window.location.origin + "/checkhealth?target=" + encodeURIComponent(url);
|
|
||||||
xhttp.open("HEAD", url, true);
|
|
||||||
xhttp.send();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateHealthStatus() {
|
|
||||||
let rows = document.querySelectorAll("tbody tr");
|
|
||||||
rows.forEach((row) => {
|
|
||||||
let url = row.querySelector("#url-cell").textContent;
|
|
||||||
let cell = row.querySelector("#health-cell"); // Health column cell
|
|
||||||
checkHealth(url, cell);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
|
||||||
updateHealthStatus();
|
|
||||||
|
|
||||||
// Update health status every 5 seconds
|
|
||||||
setInterval(updateHealthStatus, 5000);
|
|
||||||
});
|
|
|
@ -1,43 +0,0 @@
|
||||||
body {
|
|
||||||
background-color: #131516;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-spacing: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr {
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table th:first-child {
|
|
||||||
border-radius: 10px 0 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table th:last-child {
|
|
||||||
border-radius: 0 10px 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
table td:first-of-type {
|
|
||||||
border-top-left-radius: 10px;
|
|
||||||
border-bottom-left-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table td:last-of-type {
|
|
||||||
border-top-right-radius: 10px;
|
|
||||||
border-bottom-right-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table caption {
|
|
||||||
color: antiquewhite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.health-circle {
|
|
||||||
height: 15px;
|
|
||||||
width: 15px;
|
|
||||||
background-color: #28a745;
|
|
||||||
border-radius: 50%;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
html,
|
|
||||||
body {
|
|
||||||
font-family: monospace !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidenav {
|
|
||||||
height: 100%;
|
|
||||||
width: 0;
|
|
||||||
position: fixed;
|
|
||||||
z-index: 1;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
background-color: #111;
|
|
||||||
overflow-x: hidden;
|
|
||||||
padding-top: 32px;
|
|
||||||
transition: 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidenav a {
|
|
||||||
padding: 8px 8px 8px 24px;
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #818181;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.sidenav a:hover {
|
|
||||||
color: #f1f1f1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidenav .closebtn {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 24px;
|
|
||||||
font-size: 24px;
|
|
||||||
margin-left: 42px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.openbtn {
|
|
||||||
z-index: 1;
|
|
||||||
position: absolute;
|
|
||||||
top: 16;
|
|
||||||
left: 16;
|
|
||||||
font: 24px bold monospace;
|
|
||||||
color: #f8f8f2 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#main {
|
|
||||||
transition: margin-left 0.3s;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content {
|
|
||||||
transition: margin-left 0.3s;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
border: none;
|
|
||||||
margin: 0;
|
|
||||||
margin-left: 0px;
|
|
||||||
padding: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
z-index: 0;
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
FROM debian:stable-slim
|
|
||||||
|
|
||||||
RUN apt update && \
|
|
||||||
apt install -y netcat-openbsd && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
RUN printf '#!/bin/bash\nclear; echo "Netcat UDP server started"; nc -u -l 9999; exit' >> /entrypoint.sh
|
|
||||||
RUN chmod +x /entrypoint.sh
|
|
||||||
|
|
||||||
ENTRYPOINT ["/entrypoint.sh"]
|
|
Loading…
Add table
Reference in a new issue