mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-12 19:05:55 +02:00
Merge pull request #841 from 0xJacky/feat/devcontainer
feat: use devconatiner
This commit is contained in:
commit
d62f719697
47 changed files with 890 additions and 185 deletions
|
@ -7,7 +7,7 @@ tmp_dir = "tmp"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
# Just plain old shell command. You could use `make` as well.
|
# Just plain old shell command. You could use `make` as well.
|
||||||
cmd = "CGO_ENABLED=1 go build -tags=jsoniter -ldflags=\"-X 'github.com/0xJacky/Nginx-UI/settings.buildTime=$(date +%s)'\" -v -o ./tmp/main ."
|
cmd = "CGO_ENABLED=1 go build -tags=jsoniter,unembed -ldflags=\"-X 'github.com/0xJacky/Nginx-UI/settings.buildTime=$(date +%s)'\" -v -o ./tmp/main ."
|
||||||
# Binary file yields from `cmd`.
|
# Binary file yields from `cmd`.
|
||||||
bin = "tmp/main"
|
bin = "tmp/main"
|
||||||
# Customize binary.
|
# Customize binary.
|
||||||
|
|
49
.devcontainer/Dockerfile
Normal file
49
.devcontainer/Dockerfile
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
FROM mcr.microsoft.com/devcontainers/base:jammy
|
||||||
|
|
||||||
|
# Combine installation steps for Nginx and Go to avoid repetitive update/cleanup commands
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends curl gnupg2 ca-certificates lsb-release ubuntu-keyring jq && \
|
||||||
|
\
|
||||||
|
# Configure the Nginx repository
|
||||||
|
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor > /usr/share/keyrings/nginx-archive-keyring.gpg && \
|
||||||
|
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/mainline/ubuntu $(lsb_release -cs) nginx" \
|
||||||
|
> /etc/apt/sources.list.d/nginx.list && \
|
||||||
|
printf "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \
|
||||||
|
> /etc/apt/preferences.d/99nginx && \
|
||||||
|
\
|
||||||
|
# Update package information and install Nginx
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends nginx inotify-tools file && \
|
||||||
|
\
|
||||||
|
# Automatically retrieve the latest stable Go version and install it,
|
||||||
|
# download the appropriate binary based on system architecture (amd64 or arm64)
|
||||||
|
GO_VERSION=$(curl -sSL "https://golang.org/dl/?mode=json" | \
|
||||||
|
jq -r 'map(select(.stable)) | .[0].version' | sed 's/^go//') && \
|
||||||
|
ARCH=$(dpkg --print-architecture) && \
|
||||||
|
if [ "$ARCH" = "arm64" ]; then \
|
||||||
|
GO_ARCH=linux-arm64; \
|
||||||
|
else \
|
||||||
|
GO_ARCH=linux-amd64; \
|
||||||
|
fi && \
|
||||||
|
echo "Installing Go version: ${GO_VERSION} for architecture: ${GO_ARCH}" && \
|
||||||
|
curl -sSL "https://golang.org/dl/go${GO_VERSION}.${GO_ARCH}.tar.gz" -o go.tar.gz && \
|
||||||
|
rm -rf /usr/local/go && \
|
||||||
|
tar -C /usr/local -xzf go.tar.gz && \
|
||||||
|
rm go.tar.gz && \
|
||||||
|
\
|
||||||
|
# Remove jq and clean up to reduce image size
|
||||||
|
apt-get remove -y jq && \
|
||||||
|
apt-get autoremove -y && \
|
||||||
|
apt-get clean && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN cp -rp /etc/nginx /etc/nginx.orig
|
||||||
|
|
||||||
|
# Set PATH to include Go installation and default go install binary location
|
||||||
|
ENV PATH="/usr/local/go/bin:/root/go/bin:${PATH}"
|
||||||
|
|
||||||
|
# Install air with go install (requires Go 1.23 or higher)
|
||||||
|
RUN go install github.com/air-verse/air@latest
|
||||||
|
|
||||||
|
# set zsh as default shell
|
||||||
|
RUN chsh -s $(which zsh)
|
44
.devcontainer/devcontainer.json
Normal file
44
.devcontainer/devcontainer.json
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||||
|
// README at: https://github.com/devcontainers/templates/tree/main/src/ubuntu
|
||||||
|
{
|
||||||
|
"name": "Ubuntu",
|
||||||
|
"dockerComposeFile": "docker-compose.yml",
|
||||||
|
"service": "nginx-ui",
|
||||||
|
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
||||||
|
"shutdownAction": "stopCompose",
|
||||||
|
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||||
|
"features": {
|
||||||
|
"ghcr.io/devcontainers/features/common-utils:2": {
|
||||||
|
"installOhMyZsh": true
|
||||||
|
},
|
||||||
|
"ghcr.io/devcontainers/features/node:1": {}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
|
// "forwardPorts": [],
|
||||||
|
|
||||||
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
|
// "postCreateCommand": "",
|
||||||
|
|
||||||
|
// Configure tool-specific properties.
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"extensions": [
|
||||||
|
"antfu.iconify",
|
||||||
|
"antfu.unocss",
|
||||||
|
"github.copilot",
|
||||||
|
"golang.go",
|
||||||
|
"vue.volar",
|
||||||
|
"ms-azuretools.vscode-docker"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||||
|
"remoteUser": "root",
|
||||||
|
"overrideCommand": false,
|
||||||
|
"postStartCommand": "./.devcontainer/start.sh",
|
||||||
|
"mounts": [
|
||||||
|
"source=${localEnv:HOME}/.ssh,target=/root/.ssh,type=bind,consistency=cached"
|
||||||
|
]
|
||||||
|
}
|
57
.devcontainer/docker-compose.yml
Normal file
57
.devcontainer/docker-compose.yml
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
services:
|
||||||
|
nginx-ui:
|
||||||
|
build: .
|
||||||
|
image: nginx-ui-dev
|
||||||
|
container_name: nginx-ui
|
||||||
|
volumes:
|
||||||
|
- ../..:/workspaces:cached
|
||||||
|
- ./go-path:/root/go
|
||||||
|
- ./data/nginx:/etc/nginx
|
||||||
|
command: sleep infinity
|
||||||
|
environment:
|
||||||
|
- NGINX_UI_CERT_CA_DIR=https://pebble:14000/dir
|
||||||
|
networks:
|
||||||
|
nginxui:
|
||||||
|
nginx-ui-2:
|
||||||
|
image: nginx-ui-dev
|
||||||
|
container_name: nginx-ui-2
|
||||||
|
volumes:
|
||||||
|
- ../..:/workspaces:cached
|
||||||
|
- ./data/nginx-ui-2/nginx:/etc/nginx
|
||||||
|
- ./data/nginx-ui-2/nginx-ui:/etc/nginx-ui
|
||||||
|
working_dir: /workspaces/nginx-ui
|
||||||
|
command: ./.devcontainer/node-supervisor.sh
|
||||||
|
depends_on:
|
||||||
|
- nginx-ui
|
||||||
|
networks:
|
||||||
|
nginxui:
|
||||||
|
|
||||||
|
pebble:
|
||||||
|
image: ghcr.io/letsencrypt/pebble:latest
|
||||||
|
volumes:
|
||||||
|
- ./pebble-test:/test
|
||||||
|
command: -config /test/config/pebble-config.json -strict -dnsserver challtestsrv:8053
|
||||||
|
ports:
|
||||||
|
- 14000:14000 # HTTPS ACME API
|
||||||
|
- 15000:15000 # HTTPS Management API
|
||||||
|
environment:
|
||||||
|
- PEBBLE_VA_NOSLEEP=1
|
||||||
|
- PEBBLE_VA_ALWAYS_VALID=1
|
||||||
|
networks:
|
||||||
|
nginxui:
|
||||||
|
challtestsrv:
|
||||||
|
image: ghcr.io/letsencrypt/pebble-challtestsrv:latest
|
||||||
|
command: -defaultIPv6 "" -defaultIPv4 challtestsrv
|
||||||
|
ports:
|
||||||
|
- 8055:8055 # HTTP Management API
|
||||||
|
networks:
|
||||||
|
nginxui:
|
||||||
|
casdoor:
|
||||||
|
image: casbin/casdoor-all-in-one
|
||||||
|
ports:
|
||||||
|
- 8001:8000
|
||||||
|
networks:
|
||||||
|
- nginxui
|
||||||
|
|
||||||
|
networks:
|
||||||
|
nginxui:
|
6
.devcontainer/init-nginx.sh
Executable file
6
.devcontainer/init-nginx.sh
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
# init nginx config dir
|
||||||
|
if [ "$(ls -A /etc/nginx)" = "" ]; then
|
||||||
|
echo "Initialing Nginx config dir"
|
||||||
|
cp -rp /etc/nginx.orig/* /etc/nginx/
|
||||||
|
echo "Initialed Nginx config dir"
|
||||||
|
fi
|
87
.devcontainer/node-supervisor.sh
Executable file
87
.devcontainer/node-supervisor.sh
Executable file
|
@ -0,0 +1,87 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Configurable variables
|
||||||
|
SOURCE_FILE=/workspaces/nginx-ui/tmp/main
|
||||||
|
TARGET_PATH=/usr/local/bin/nginx-ui
|
||||||
|
CONFIG_FILE=/etc/nginx-ui/app.ini
|
||||||
|
|
||||||
|
# init nginx
|
||||||
|
./.devcontainer/init-nginx.sh
|
||||||
|
|
||||||
|
LOG_PREFIX="[Supervisor]"
|
||||||
|
|
||||||
|
# Debug initial state
|
||||||
|
echo "$LOG_PREFIX Starting supervisor with:"
|
||||||
|
echo "$LOG_PREFIX SOURCE_FILE: $SOURCE_FILE"
|
||||||
|
echo "$LOG_PREFIX TARGET_PATH: $TARGET_PATH"
|
||||||
|
echo "$LOG_PREFIX CONFIG_FILE: $CONFIG_FILE"
|
||||||
|
|
||||||
|
# Wait for initial file creation
|
||||||
|
while [[ ! -f "$SOURCE_FILE" ]]; do
|
||||||
|
echo "$LOG_PREFIX Waiting for $SOURCE_FILE to be created..."
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
# Initial copy and start
|
||||||
|
echo "$LOG_PREFIX Initial file detected, starting service..."
|
||||||
|
cp -fv "$SOURCE_FILE" "$TARGET_PATH"
|
||||||
|
chmod +x "$TARGET_PATH"
|
||||||
|
pkill -x nginx-ui || echo "$LOG_PREFIX No existing process to kill"
|
||||||
|
nohup "$TARGET_PATH" -config "$CONFIG_FILE" > /proc/1/fd/1 2>&1 &
|
||||||
|
|
||||||
|
# Use proper field separation for inotify output
|
||||||
|
inotifywait -m -e close_write,moved_to,create,delete \
|
||||||
|
--format "%T|%w%f|%e" \
|
||||||
|
--timefmt "%F-%H:%M:%S" \
|
||||||
|
"$(dirname "$SOURCE_FILE")" |
|
||||||
|
while IFS='|' read -r TIME FILE EVENT; do
|
||||||
|
echo "$LOG_PREFIX [${TIME}] Event: ${EVENT} - ${FILE}"
|
||||||
|
|
||||||
|
# Handle atomic save operations
|
||||||
|
if [[ "$FILE" =~ .*-tmp-umask$ ]] || [[ "$EVENT" == "DELETE" ]]; then
|
||||||
|
echo "$LOG_PREFIX Detected build intermediate file, checking main..."
|
||||||
|
sleep 0.3 # Allow atomic replace completion
|
||||||
|
|
||||||
|
if [[ -f "$SOURCE_FILE" ]]; then
|
||||||
|
echo "$LOG_PREFIX Valid main file detected after build"
|
||||||
|
FILE="$SOURCE_FILE"
|
||||||
|
else
|
||||||
|
echo "$LOG_PREFIX Main file missing after build operation"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$FILE" == "$SOURCE_FILE" ]]; then
|
||||||
|
# Stability checks
|
||||||
|
echo "$LOG_PREFIX File metadata:"
|
||||||
|
ls -l "$FILE"
|
||||||
|
file "$FILE"
|
||||||
|
|
||||||
|
# Wait for file stability with retries
|
||||||
|
retries=5
|
||||||
|
while ((retries-- > 0)); do
|
||||||
|
if file "$FILE" | grep -q "executable"; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo "$LOG_PREFIX Waiting for valid executable (${retries} retries left)..."
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
if ((retries <= 0)); then
|
||||||
|
echo "$LOG_PREFIX ERROR: File validation failed after 5 retries"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy and restart service
|
||||||
|
echo "$LOG_PREFIX Updating service..."
|
||||||
|
cp -fv "$FILE" "$TARGET_PATH"
|
||||||
|
chmod +x "$TARGET_PATH"
|
||||||
|
|
||||||
|
echo "$LOG_PREFIX Killing existing process..."
|
||||||
|
pkill -x nginx-ui || echo "$LOG_PREFIX No process to kill"
|
||||||
|
|
||||||
|
echo "$LOG_PREFIX Starting new process..."
|
||||||
|
nohup "$TARGET_PATH" -config "$CONFIG_FILE" > /proc/1/fd/1 2>&1 &
|
||||||
|
echo "$LOG_PREFIX Restart complete. New PID: $(pgrep nginx-ui)"
|
||||||
|
fi
|
||||||
|
done
|
25
.devcontainer/pebble-test/certs/README.md
Normal file
25
.devcontainer/pebble-test/certs/README.md
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# certs/
|
||||||
|
|
||||||
|
This directory contains a CA certificate (`pebble.minica.pem`) and a private key
|
||||||
|
(`pebble.minica.key.pem`) that are used to issue a end-entity certificate (See
|
||||||
|
`certs/localhost`) for the Pebble HTTPS server.
|
||||||
|
|
||||||
|
To get your **testing code** to use Pebble without HTTPS errors you should
|
||||||
|
configure your ACME client to trust the `pebble.minica.pem` CA certificate. Your
|
||||||
|
ACME client should offer a runtime option to specify a list of root CAs that you
|
||||||
|
can configure to include the `pebble.minica.pem` file.
|
||||||
|
|
||||||
|
**Do not** add this CA certificate to the system trust store or in production
|
||||||
|
code!!! The CA's private key is **public** and anyone can use it to issue
|
||||||
|
certificates that will be trusted by a system with the Pebble CA in the trust
|
||||||
|
store.
|
||||||
|
|
||||||
|
To re-create all of the Pebble certificates run:
|
||||||
|
|
||||||
|
minica -ca-cert pebble.minica.pem \
|
||||||
|
-ca-key pebble.minica.key.pem \
|
||||||
|
-domains localhost,pebble \
|
||||||
|
-ip-addresses 127.0.0.1
|
||||||
|
|
||||||
|
From the `test/certs/` directory after [installing
|
||||||
|
MiniCA](https://github.com/jsha/minica#installation)
|
5
.devcontainer/pebble-test/certs/localhost/README.md
Normal file
5
.devcontainer/pebble-test/certs/localhost/README.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# certs/localhost
|
||||||
|
|
||||||
|
This directory contains an end-entity (leaf) certificate (`cert.pem`) and
|
||||||
|
a private key (`key.pem`) for the Pebble HTTPS server. It includes `127.0.0.1`
|
||||||
|
as an IP address SAN, and `[localhost, pebble]` as DNS SANs.
|
19
.devcontainer/pebble-test/certs/localhost/cert.pem
Normal file
19
.devcontainer/pebble-test/certs/localhost/cert.pem
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDGzCCAgOgAwIBAgIIbEfayDFsBtwwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
|
||||||
|
AxMVbWluaWNhIHJvb3QgY2EgMjRlMmRiMCAXDTE3MTIwNjE5NDIxMFoYDzIxMDcx
|
||||||
|
MjA2MTk0MjEwWjAUMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB
|
||||||
|
AQUAA4IBDwAwggEKAoIBAQCbFMW3DXXdErvQf2lCZ0qz0DGEWadDoF0O2neM5mVa
|
||||||
|
VQ7QGW0xc5Qwvn3Tl62C0JtwLpF0pG2BICIN+DHdVaIUwkf77iBS2doH1I3waE1I
|
||||||
|
8GkV9JrYmFY+j0dA1SwBmqUZNXhLNwZGq1a91nFSI59DZNy/JciqxoPX2K++ojU2
|
||||||
|
FPpuXe2t51NmXMsszpa+TDqF/IeskA9A/ws6UIh4Mzhghx7oay2/qqj2IIPjAmJj
|
||||||
|
i73kdUvtEry3wmlkBvtVH50+FscS9WmPC5h3lDTk5nbzSAXKuFusotuqy3XTgY5B
|
||||||
|
PiRAwkZbEY43JNfqenQPHo7mNTt29i+NVVrBsnAa5ovrAgMBAAGjYzBhMA4GA1Ud
|
||||||
|
DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0T
|
||||||
|
AQH/BAIwADAiBgNVHREEGzAZgglsb2NhbGhvc3SCBnBlYmJsZYcEfwAAATANBgkq
|
||||||
|
hkiG9w0BAQsFAAOCAQEAYIkXff8H28KS0KyLHtbbSOGU4sujHHVwiVXSATACsNAE
|
||||||
|
D0Qa8hdtTQ6AUqA6/n8/u1tk0O4rPE/cTpsM3IJFX9S3rZMRsguBP7BSr1Lq/XAB
|
||||||
|
7JP/CNHt+Z9aKCKcg11wIX9/B9F7pyKM3TdKgOpqXGV6TMuLjg5PlYWI/07lVGFW
|
||||||
|
/mSJDRs8bSCFmbRtEqc4lpwlrpz+kTTnX6G7JDLfLWYw/xXVqwFfdengcDTHCc8K
|
||||||
|
wtgGq/Gu6vcoBxIO3jaca+OIkMfxxXmGrcNdseuUCa3RMZ8Qy03DqGu6Y6XQyK4B
|
||||||
|
W8zIG6H9SVKkAznM2yfYhW8v2ktcaZ95/OBHY97ZIw==
|
||||||
|
-----END CERTIFICATE-----
|
27
.devcontainer/pebble-test/certs/localhost/key.pem
Normal file
27
.devcontainer/pebble-test/certs/localhost/key.pem
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEAmxTFtw113RK70H9pQmdKs9AxhFmnQ6BdDtp3jOZlWlUO0Blt
|
||||||
|
MXOUML5905etgtCbcC6RdKRtgSAiDfgx3VWiFMJH++4gUtnaB9SN8GhNSPBpFfSa
|
||||||
|
2JhWPo9HQNUsAZqlGTV4SzcGRqtWvdZxUiOfQ2TcvyXIqsaD19ivvqI1NhT6bl3t
|
||||||
|
redTZlzLLM6Wvkw6hfyHrJAPQP8LOlCIeDM4YIce6Gstv6qo9iCD4wJiY4u95HVL
|
||||||
|
7RK8t8JpZAb7VR+dPhbHEvVpjwuYd5Q05OZ280gFyrhbrKLbqst104GOQT4kQMJG
|
||||||
|
WxGONyTX6np0Dx6O5jU7dvYvjVVawbJwGuaL6wIDAQABAoIBAGW9W/S6lO+DIcoo
|
||||||
|
PHL+9sg+tq2gb5ZzN3nOI45BfI6lrMEjXTqLG9ZasovFP2TJ3J/dPTnrwZdr8Et/
|
||||||
|
357YViwORVFnKLeSCnMGpFPq6YEHj7mCrq+YSURjlRhYgbVPsi52oMOfhrOIJrEG
|
||||||
|
ZXPAwPRi0Ftqu1omQEqz8qA7JHOkjB2p0i2Xc/uOSJccCmUDMlksRYz8zFe8wHuD
|
||||||
|
XvUL2k23n2pBZ6wiez6Xjr0wUQ4ESI02x7PmYgA3aqF2Q6ECDwHhjVeQmAuypMF6
|
||||||
|
IaTjIJkWdZCW96pPaK1t+5nTNZ+Mg7tpJ/PRE4BkJvqcfHEOOl6wAE8gSk5uVApY
|
||||||
|
ZRKGmGkCgYEAzF9iRXYo7A/UphL11bR0gqxB6qnQl54iLhqS/E6CVNcmwJ2d9pF8
|
||||||
|
5HTfSo1/lOXT3hGV8gizN2S5RmWBrc9HBZ+dNrVo7FYeeBiHu+opbX1X/C1HC0m1
|
||||||
|
wJNsyoXeqD1OFc1WbDpHz5iv4IOXzYdOdKiYEcTv5JkqE7jomqBLQk8CgYEAwkG/
|
||||||
|
rnwr4ThUo/DG5oH+l0LVnHkrJY+BUSI33g3eQ3eM0MSbfJXGT7snh5puJW0oXP7Z
|
||||||
|
Gw88nK3Vnz2nTPesiwtO2OkUVgrIgWryIvKHaqrYnapZHuM+io30jbZOVaVTMR9c
|
||||||
|
X/7/d5/evwXuP7p2DIdZKQKKFgROm1XnhNqVgaUCgYBD/ogHbCR5RVsOVciMbRlG
|
||||||
|
UGEt3YmUp/vfMuAsKUKbT2mJM+dWHVlb+LZBa4pC06QFgfxNJi/aAhzSGvtmBEww
|
||||||
|
xsXbaceauZwxgJfIIUPfNZCMSdQVIVTi2Smcx6UofBz6i/Jw14MEwlvhamaa7qVf
|
||||||
|
kqflYYwelga1wRNCPopLaQKBgQCWsZqZKQqBNMm0Q9yIhN+TR+2d7QFjqeePoRPl
|
||||||
|
1qxNejhq25ojE607vNv1ff9kWUGuoqSZMUC76r6FQba/JoNbefI4otd7x/GzM9uS
|
||||||
|
8MHMJazU4okwROkHYwgLxxkNp6rZuJJYheB4VDTfyyH/ng5lubmY7rdgTQcNyZ5I
|
||||||
|
majRYQKBgAMKJ3RlII0qvAfNFZr4Y2bNIq+60Z+Qu2W5xokIHCFNly3W1XDDKGFe
|
||||||
|
CCPHSvQljinke3P9gPt2HVdXxcnku9VkTti+JygxuLkVg7E0/SWwrWfGsaMJs+84
|
||||||
|
fK+mTZay2d3v24r9WKEKwLykngYPyZw5+BdWU0E+xx5lGUd3U4gG
|
||||||
|
-----END RSA PRIVATE KEY-----
|
27
.devcontainer/pebble-test/certs/pebble.minica.key.pem
Normal file
27
.devcontainer/pebble-test/certs/pebble.minica.key.pem
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpAIBAAKCAQEAuVoGTaFSWp3Y+N5JC8lOdL8wmWpaM73UaNzhYiqA7ZqijzVk
|
||||||
|
TTtoQvQFDcUwyXKOdWHONrv1ld3z224Us504jjlbZwI5uoquCOZ2WJbRhmXrRgzk
|
||||||
|
Fq+/MtoFmPkhtO/DLjjtocgyIirVXN8Yl2APvB5brvRfCm6kktYeecsWfW/O3ikf
|
||||||
|
gdM7tmocwQiBypiloHOjdd5e2g8cWNw+rqvILSUVNLaLpsi23cxnLqVb424wz9dZ
|
||||||
|
5dO0REg1gSxtf4N5LSb6iGuAVoFNhzIeKzQ+svDg9x8tx/DGOghJS/jDgmxSY1qo
|
||||||
|
bTsXhcmWVfat5GJ5PQgLkCSjBBrjeBlOrc4VtQIDAQABAoIBAQCAoRoou6C0ZEDU
|
||||||
|
DScyN8TrvlcS0LzClaWYFFmRT5/jxOG1cr8l3elwNXpgYQ2Hb6mvim2ajHxVQg/e
|
||||||
|
oxlYwO4jvWhSJzg63c0DPjS5LAlCNO6+0Wlk2RheSPGDhLlAoPeZ10YKdS1dis5B
|
||||||
|
Qk4Fl1O0IHlOBCcEzV4GzPOfYDI+X6/f4xY7qz1s+CgoIxjIeiG+1/WpZQpYhobY
|
||||||
|
7CfSDdYDKtksXi7iQkc5earUAHBqZ1gQTq6e5LVm9AjRzENhMctFgcPs5zOjp2ak
|
||||||
|
PluixrA8LTAfu9wQzvxDkPl0UarZVxCerw6nlAziILpQ+U6PtoPZj49VpntTc+cq
|
||||||
|
1qjzkbhBAoGBANElJmFWY2X6LgBpszeqt0ZOSbkFg2bC0wHCJrMlRzUMEn83w9e8
|
||||||
|
Z2Fqml9eCC5qxJcyxWDVQeoAX6090m0qgP8xNmGdafcVic2cUlrqtkqhhst2OHCO
|
||||||
|
MCQEB7cdsjiidNNrOgLbQ3i1bYID8BVLf/TDhEbRgvTewDaz6XPdoSIRAoGBAOLg
|
||||||
|
RuOec5gn50SrVycx8BLFO8AXjXojpZb1Xg26V5miz1IavSfDcgae/699ppSz+UWi
|
||||||
|
jGMFr/PokY2JxDVs3PyQLu7ahMzyFHr16Agvp5g5kq056XV+uI/HhqLHOWSQ09DS
|
||||||
|
1Vrj7FOYpKRzge3/AC7ty9Vr35uMiebpm4/CLFVlAoGALnsIJZfSbWaFdLgJCXUa
|
||||||
|
WDir77/G7T6dMIXanfPJ+IMfVUCqeLa5bxAHEOzP+qjl2giBjzy18nB00warTnGk
|
||||||
|
y5I/WMBoPW5++sAkGWqSatGtKGi0sGcZUdfHcy3ZXvbT6eyprtrWCuyfUsbXQ5RM
|
||||||
|
8rPFIQwNA6jBpSak2ohF+FECgYEAn+6IKncNd6pRfnfmdSvf1+uPxkcUJZCxb2xC
|
||||||
|
xByjGhvKWE+fHkPJwt8c0SIbZuJEC5Gds0RUF/XPfV4roZm/Yo9ldl02lp7kTxXA
|
||||||
|
XtzxIP8c5d5YM8qD4l8+Csu0Kq9pkeC+JFddxkRpc8A1TIehInPhZ+6mb6mvoMb3
|
||||||
|
MW0pAX0CgYATT74RYuIYWZvx0TK4ZXIKTw2i6HObLF63Y6UwyPXXdEVie/ToYRNH
|
||||||
|
JIxE1weVpHvnHZvVD6D3yGk39ZsCIt31VvKpatWXlWBm875MbBc6kuIGsYT+mSSj
|
||||||
|
y9TXaE89E5zfL27nZe15QLJ+Xw8Io6PMLZ/jtC5TYoEixSZ9J8v6HA==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
19
.devcontainer/pebble-test/certs/pebble.minica.pem
Normal file
19
.devcontainer/pebble-test/certs/pebble.minica.pem
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDCTCCAfGgAwIBAgIIJOLbes8sTr4wDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
|
||||||
|
AxMVbWluaWNhIHJvb3QgY2EgMjRlMmRiMCAXDTE3MTIwNjE5NDIxMFoYDzIxMTcx
|
||||||
|
MjA2MTk0MjEwWjAgMR4wHAYDVQQDExVtaW5pY2Egcm9vdCBjYSAyNGUyZGIwggEi
|
||||||
|
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5WgZNoVJandj43kkLyU50vzCZ
|
||||||
|
alozvdRo3OFiKoDtmqKPNWRNO2hC9AUNxTDJco51Yc42u/WV3fPbbhSznTiOOVtn
|
||||||
|
Ajm6iq4I5nZYltGGZetGDOQWr78y2gWY+SG078MuOO2hyDIiKtVc3xiXYA+8Hluu
|
||||||
|
9F8KbqSS1h55yxZ9b87eKR+B0zu2ahzBCIHKmKWgc6N13l7aDxxY3D6uq8gtJRU0
|
||||||
|
toumyLbdzGcupVvjbjDP11nl07RESDWBLG1/g3ktJvqIa4BWgU2HMh4rND6y8OD3
|
||||||
|
Hy3H8MY6CElL+MOCbFJjWqhtOxeFyZZV9q3kYnk9CAuQJKMEGuN4GU6tzhW1AgMB
|
||||||
|
AAGjRTBDMA4GA1UdDwEB/wQEAwIChDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
|
||||||
|
BQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsFAAOCAQEAF85v
|
||||||
|
d40HK1ouDAtWeO1PbnWfGEmC5Xa478s9ddOd9Clvp2McYzNlAFfM7kdcj6xeiNhF
|
||||||
|
WPIfaGAi/QdURSL/6C1KsVDqlFBlTs9zYfh2g0UXGvJtj1maeih7zxFLvet+fqll
|
||||||
|
xseM4P9EVJaQxwuK/F78YBt0tCNfivC6JNZMgxKF59h0FBpH70ytUSHXdz7FKwix
|
||||||
|
Mfn3qEb9BXSk0Q3prNV5sOV3vgjEtB4THfDxSz9z3+DepVnW3vbbqwEbkXdk3j82
|
||||||
|
2muVldgOUgTwK8eT+XdofVdntzU/kzygSAtAQwLJfn51fS1GvEcYGBc1bDryIqmF
|
||||||
|
p9BI7gVKtWSZYegicA==
|
||||||
|
-----END CERTIFICATE-----
|
26
.devcontainer/pebble-test/config/load-generator-config.json
Normal file
26
.devcontainer/pebble-test/config/load-generator-config.json
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"plan": {
|
||||||
|
"actions": [
|
||||||
|
"newAccount",
|
||||||
|
"newOrder",
|
||||||
|
"fulfillOrder",
|
||||||
|
"finalizeOrder"
|
||||||
|
],
|
||||||
|
"rate": 10,
|
||||||
|
"runtime": "10s",
|
||||||
|
"rateDelta": "1/10s"
|
||||||
|
},
|
||||||
|
"directoryURL": "https://localhost:14000/dir",
|
||||||
|
"domainBase": "com",
|
||||||
|
"challengeStrategy": "random",
|
||||||
|
"httpOneAddrs": [":5002"],
|
||||||
|
"tlsAlpnOneAddrs": [":5001"],
|
||||||
|
"dnsAddrs": [":8053"],
|
||||||
|
"fakeDNS": "127.0.0.1",
|
||||||
|
"regKeySize": 2048,
|
||||||
|
"certKeySize": 2048,
|
||||||
|
"regEmail": "loadtesting@letsencrypt.org",
|
||||||
|
"maxRegs": 20,
|
||||||
|
"maxNamesPerCert": 20,
|
||||||
|
"dontSaveState": true
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"pebble": {
|
||||||
|
"listenAddress": "0.0.0.0:14000",
|
||||||
|
"managementListenAddress": "0.0.0.0:15000",
|
||||||
|
"certificate": "/test/certs/localhost/cert.pem",
|
||||||
|
"privateKey": "/test/certs/localhost/key.pem",
|
||||||
|
"httpPort": 5002,
|
||||||
|
"tlsPort": 5001,
|
||||||
|
"ocspResponderURL": "",
|
||||||
|
"retryAfter": {
|
||||||
|
"authz": 3,
|
||||||
|
"order": 5
|
||||||
|
},
|
||||||
|
"externalAccountBindingRequired": true,
|
||||||
|
"externalAccountMACKeys": {
|
||||||
|
"kid-1": "zWNDZM6eQGHWpSRTPal5eIUYFTu7EajVIoguysqZ9wG44nMEtx3MUAsUDkMTQ12W",
|
||||||
|
"kid-2": "b10lLJs8l1GPIzsLP0s6pMt8O0XVGnfTaCeROxQM0BIt2XrJMDHJZBM5NuQmQJQH",
|
||||||
|
"kid-3": "HjudV5qnbreN-n9WyFSH-t4HXuEx_XFen45zuxY-G1h6fr74V3cUM_dVlwQZBWmc"
|
||||||
|
},
|
||||||
|
"certificateValidityPeriod": 157766400
|
||||||
|
}
|
||||||
|
}
|
27
.devcontainer/pebble-test/config/pebble-config.json
Normal file
27
.devcontainer/pebble-test/config/pebble-config.json
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"pebble": {
|
||||||
|
"listenAddress": "0.0.0.0:14000",
|
||||||
|
"managementListenAddress": "0.0.0.0:15000",
|
||||||
|
"certificate": "/test/certs/localhost/cert.pem",
|
||||||
|
"privateKey": "/test/certs/localhost/key.pem",
|
||||||
|
"httpPort": 5002,
|
||||||
|
"tlsPort": 5001,
|
||||||
|
"ocspResponderURL": "",
|
||||||
|
"externalAccountBindingRequired": false,
|
||||||
|
"domainBlocklist": ["blocked-domain.example"],
|
||||||
|
"retryAfter": {
|
||||||
|
"authz": 3,
|
||||||
|
"order": 5
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"default": {
|
||||||
|
"description": "The profile you know and love",
|
||||||
|
"validityPeriod": 7776000
|
||||||
|
},
|
||||||
|
"shortlived": {
|
||||||
|
"description": "A short-lived cert profile, without actual enforcement",
|
||||||
|
"validityPeriod": 518400
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
.devcontainer/start.sh
Executable file
22
.devcontainer/start.sh
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# install zsh-autosuggestions
|
||||||
|
git clone https://github.com/zsh-users/zsh-autosuggestions ~/.oh-my-zsh/custom/plugins/zsh-autosuggestions
|
||||||
|
|
||||||
|
if ! grep -q "zsh-autosuggestions" ~/.zshrc; then
|
||||||
|
# add zsh-autosuggestions to plugins list
|
||||||
|
sed -i "/^plugins=(/s/)/ zsh-autosuggestions)/" ~/.zshrc
|
||||||
|
fi
|
||||||
|
|
||||||
|
# init nginx config dir
|
||||||
|
./.devcontainer/init-nginx.sh
|
||||||
|
|
||||||
|
# install app dependencies
|
||||||
|
echo "Installing app dependencies"
|
||||||
|
cd app && pnpm install -f
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# install docs dependencies
|
||||||
|
echo "Installing docs dependencies"
|
||||||
|
cd docs && pnpm install -f
|
||||||
|
cd ..
|
|
@ -2,3 +2,6 @@
|
||||||
app/node_modules
|
app/node_modules
|
||||||
.idea
|
.idea
|
||||||
tmp
|
tmp
|
||||||
|
|
||||||
|
.devcontainer
|
||||||
|
.vscode
|
||||||
|
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -12,6 +12,8 @@ nginx-ui
|
||||||
resources/development/nginx
|
resources/development/nginx
|
||||||
app/.env
|
app/.env
|
||||||
app/.status_hash
|
app/.status_hash
|
||||||
casdoor.pub
|
|
||||||
.idea/deployment.xml
|
.idea/deployment.xml
|
||||||
.idea/webServers.xml
|
.idea/webServers.xml
|
||||||
|
.devcontainer/go-path
|
||||||
|
.devcontainer/data
|
||||||
|
.devcontainer/casdoor.pem
|
||||||
|
|
10
.idea/nginx-ui.iml
generated
10
.idea/nginx-ui.iml
generated
|
@ -1,6 +1,14 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<module type="WEB_MODULE" version="4">
|
<module type="WEB_MODULE" version="4">
|
||||||
<component name="Go" enabled="true" />
|
<component name="Go" enabled="true">
|
||||||
|
<buildTags>
|
||||||
|
<option name="customFlags">
|
||||||
|
<array>
|
||||||
|
<option value="unembed" />
|
||||||
|
</array>
|
||||||
|
</option>
|
||||||
|
</buildTags>
|
||||||
|
</component>
|
||||||
<component name="NewModuleRootManager">
|
<component name="NewModuleRootManager">
|
||||||
<content url="file://$MODULE_DIR$" />
|
<content url="file://$MODULE_DIR$" />
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
|
|
48
.vscode/tasks.json
vendored
Normal file
48
.vscode/tasks.json
vendored
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Start Backend",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "air",
|
||||||
|
"isBackground": true,
|
||||||
|
"presentation": {
|
||||||
|
"panel": "new"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Start Frontend",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "cd app && pnpm dev",
|
||||||
|
"isBackground": true,
|
||||||
|
"presentation": {
|
||||||
|
"panel": "new"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Start Documentation",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "cd docs && pnpm docs:dev",
|
||||||
|
"isBackground": true,
|
||||||
|
"presentation": {
|
||||||
|
"panel": "new"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Start All Services",
|
||||||
|
"dependsOrder": "parallel",
|
||||||
|
"dependsOn": [
|
||||||
|
"Start Backend",
|
||||||
|
"Start Frontend",
|
||||||
|
"Start Documentation"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -4,6 +4,10 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/0xJacky/Nginx-UI/api"
|
"github.com/0xJacky/Nginx-UI/api"
|
||||||
"github.com/0xJacky/Nginx-UI/internal/analytic"
|
"github.com/0xJacky/Nginx-UI/internal/analytic"
|
||||||
"github.com/0xJacky/Nginx-UI/internal/cluster"
|
"github.com/0xJacky/Nginx-UI/internal/cluster"
|
||||||
|
@ -14,9 +18,6 @@ import (
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
"github.com/uozi-tech/cosy"
|
"github.com/uozi-tech/cosy"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetEnvironment(c *gin.Context) {
|
func GetEnvironment(c *gin.Context) {
|
||||||
|
@ -151,23 +152,10 @@ func EditEnvironment(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteEnvironment(c *gin.Context) {
|
func DeleteEnvironment(c *gin.Context) {
|
||||||
id := cast.ToUint64(c.Param("id"))
|
cosy.Core[model.Environment](c).
|
||||||
envQuery := query.Environment
|
ExecutedHook(func(c *cosy.Ctx[model.Environment]) {
|
||||||
|
|
||||||
env, err := envQuery.FirstByID(id)
|
|
||||||
if err != nil {
|
|
||||||
api.ErrHandler(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = envQuery.DeleteByID(env.ID)
|
|
||||||
if err != nil {
|
|
||||||
api.ErrHandler(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
go analytic.RestartRetrieveNodesStatus()
|
go analytic.RestartRetrieveNodesStatus()
|
||||||
|
}).Destroy()
|
||||||
c.JSON(http.StatusNoContent, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadEnvironmentFromSettings(c *gin.Context) {
|
func LoadEnvironmentFromSettings(c *gin.Context) {
|
||||||
|
|
2
app/.env
2
app/.env
|
@ -1 +1 @@
|
||||||
VITE_PROXY_TARGET=http://127.0.0.1:9001
|
VITE_PROXY_TARGET=http://127.0.0.1:9000
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//go:build !unembed
|
||||||
|
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -6,3 +8,5 @@ import (
|
||||||
|
|
||||||
//go:embed i18n.json dist/* dist/*/* src/language/* src/language/*/*
|
//go:embed i18n.json dist/* dist/*/* src/language/* src/language/*/*
|
||||||
var DistFS embed.FS
|
var DistFS embed.FS
|
||||||
|
|
||||||
|
var VersionPath = "dist/version.json"
|
||||||
|
|
10
app/app_unembed.go
Normal file
10
app/app_unembed.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
//go:build unembed
|
||||||
|
|
||||||
|
package app
|
||||||
|
|
||||||
|
import "embed"
|
||||||
|
|
||||||
|
//go:embed i18n.json src/language/* src/language/*/* src/version.json
|
||||||
|
var DistFS embed.FS
|
||||||
|
|
||||||
|
var VersionPath = "src/version.json"
|
|
@ -4,7 +4,6 @@ import gettext from '@/gettext'
|
||||||
import { useSettingsStore } from '@/pinia'
|
import { useSettingsStore } from '@/pinia'
|
||||||
import { theme } from 'ant-design-vue'
|
import { theme } from 'ant-design-vue'
|
||||||
import en_US from 'ant-design-vue/es/locale/en_US'
|
import en_US from 'ant-design-vue/es/locale/en_US'
|
||||||
|
|
||||||
import zh_CN from 'ant-design-vue/es/locale/zh_CN'
|
import zh_CN from 'ant-design-vue/es/locale/zh_CN'
|
||||||
import zh_TW from 'ant-design-vue/es/locale/zh_TW'
|
import zh_TW from 'ant-design-vue/es/locale/zh_TW'
|
||||||
// This starter template is using Vue 3 <script setup> SFCs
|
// This starter template is using Vue 3 <script setup> SFCs
|
||||||
|
|
|
@ -181,7 +181,7 @@ function handleBatchUpdated() {
|
||||||
v-if="!disableAdd && !inTrash"
|
v-if="!disableAdd && !inTrash"
|
||||||
type="link"
|
type="link"
|
||||||
size="small"
|
size="small"
|
||||||
@click="add"
|
@click="add()"
|
||||||
>
|
>
|
||||||
{{ $gettext('Add') }}
|
{{ $gettext('Add') }}
|
||||||
</AButton>
|
</AButton>
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"version":"2.0.0-rc.1","build_id":1,"total_build":375}
|
{"version":"2.0.0-rc.1","build_id":7,"total_build":381}
|
|
@ -6,6 +6,7 @@ import BatchUpgrader from '@/views/environment/BatchUpgrader.vue'
|
||||||
import envColumns from '@/views/environment/envColumns'
|
import envColumns from '@/views/environment/envColumns'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
const curd = ref()
|
const curd = ref()
|
||||||
function loadFromSettings() {
|
function loadFromSettings() {
|
||||||
environment.load_from_settings().then(() => {
|
environment.load_from_settings().then(() => {
|
||||||
|
@ -20,6 +21,18 @@ const refUpgrader = ref()
|
||||||
function batchUpgrade() {
|
function batchUpgrade() {
|
||||||
refUpgrader.value.open(selectedNodeIds, selectedNodes)
|
refUpgrader.value.open(selectedNodeIds, selectedNodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const inTrash = computed(() => {
|
||||||
|
return route.query.trash === 'true'
|
||||||
|
})
|
||||||
|
|
||||||
|
// const timer = setInterval(() => {
|
||||||
|
// curd.value.get_list()
|
||||||
|
// }, 10000)
|
||||||
|
|
||||||
|
// onUnmounted(() => {
|
||||||
|
// clearInterval(timer)
|
||||||
|
// })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -43,7 +56,7 @@ function batchUpgrade() {
|
||||||
|
|
||||||
<BatchUpgrader ref="refUpgrader" />
|
<BatchUpgrader ref="refUpgrader" />
|
||||||
|
|
||||||
<FooterToolBar>
|
<FooterToolBar v-if="!inTrash">
|
||||||
<ATooltip
|
<ATooltip
|
||||||
:title="$gettext('Please select at least one node to upgrade')"
|
:title="$gettext('Please select at least one node to upgrade')"
|
||||||
placement="topLeft"
|
placement="topLeft"
|
||||||
|
|
|
@ -26,12 +26,12 @@ const columns: Column[] = [{
|
||||||
placeholder: () => 'https://10.0.0.1:9000',
|
placeholder: () => 'https://10.0.0.1:9000',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
width: 300,
|
width: 260,
|
||||||
}, {
|
}, {
|
||||||
title: () => $gettext('Version'),
|
title: () => $gettext('Version'),
|
||||||
dataIndex: 'version',
|
dataIndex: 'version',
|
||||||
pithy: true,
|
pithy: true,
|
||||||
width: 150,
|
width: 120,
|
||||||
}, {
|
}, {
|
||||||
title: () => 'NodeSecret',
|
title: () => 'NodeSecret',
|
||||||
dataIndex: 'token',
|
dataIndex: 'token',
|
||||||
|
@ -65,7 +65,7 @@ const columns: Column[] = [{
|
||||||
},
|
},
|
||||||
sorter: true,
|
sorter: true,
|
||||||
pithy: true,
|
pithy: true,
|
||||||
width: 200,
|
width: 120,
|
||||||
}, {
|
}, {
|
||||||
title: () => $gettext('Enabled'),
|
title: () => $gettext('Enabled'),
|
||||||
dataIndex: 'enabled',
|
dataIndex: 'enabled',
|
||||||
|
@ -85,7 +85,7 @@ const columns: Column[] = [{
|
||||||
},
|
},
|
||||||
sorter: true,
|
sorter: true,
|
||||||
pithy: true,
|
pithy: true,
|
||||||
width: 150,
|
width: 120,
|
||||||
}, {
|
}, {
|
||||||
title: () => $gettext('Updated at'),
|
title: () => $gettext('Updated at'),
|
||||||
dataIndex: 'updated_at',
|
dataIndex: 'updated_at',
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import {LocaleSpecificConfig, DefaultTheme} from 'vitepress'
|
import { LocaleSpecificConfig, DefaultTheme } from 'vitepress'
|
||||||
import {demoUrl} from './common'
|
import { demoUrl } from './common'
|
||||||
|
|
||||||
export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
|
export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
|
||||||
themeConfig: {
|
themeConfig: {
|
||||||
nav: [
|
nav: [
|
||||||
{text: 'Home', link: '/'},
|
{ text: 'Home', link: '/' },
|
||||||
{text: 'Guide', link: '/guide/about'},
|
{ text: 'Guide', link: '/guide/about' },
|
||||||
{text: 'Demo', link: demoUrl}
|
{ text: 'Demo', link: demoUrl }
|
||||||
],
|
],
|
||||||
|
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
@ -15,55 +15,56 @@ export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
|
||||||
text: 'Introduction',
|
text: 'Introduction',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
items: [
|
items: [
|
||||||
{text: 'What is Nginx UI?', link: '/guide/about'},
|
{ text: 'What is Nginx UI?', link: '/guide/about' },
|
||||||
{text: 'Getting Started', link: '/guide/getting-started'},
|
{ text: 'Getting Started', link: '/guide/getting-started' },
|
||||||
{text: 'Install Script', link: '/guide/install-script-linux'}
|
{ text: 'Install Script', link: '/guide/install-script-linux' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Development',
|
text: 'Development',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
items: [
|
items: [
|
||||||
{text: 'Build', link: '/guide/build'},
|
{ text: 'Devcontainer', link: '/guide/devcontainer' },
|
||||||
{text: 'Project Structure', link: '/guide/project-structure'},
|
{ text: 'Build', link: '/guide/build' },
|
||||||
{text: 'Config Template', link: '/guide/nginx-ui-template'},
|
{ text: 'Project Structure', link: '/guide/project-structure' },
|
||||||
{text: 'Contributing', link: '/guide/contributing'}
|
{ text: 'Config Template', link: '/guide/nginx-ui-template' },
|
||||||
|
{ text: 'Contributing', link: '/guide/contributing' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Configuration',
|
text: 'Configuration',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
items: [
|
items: [
|
||||||
{text: 'App', link: '/guide/config-app'},
|
{ text: 'App', link: '/guide/config-app' },
|
||||||
{text: 'Server', link: '/guide/config-server'},
|
{ text: 'Server', link: '/guide/config-server' },
|
||||||
{text: 'Database', link: '/guide/config-database'},
|
{ text: 'Database', link: '/guide/config-database' },
|
||||||
{text: 'Auth', link: '/guide/config-auth'},
|
{ text: 'Auth', link: '/guide/config-auth' },
|
||||||
{text: 'Casdoor', link: '/guide/config-casdoor'},
|
{ text: 'Casdoor', link: '/guide/config-casdoor' },
|
||||||
{text: 'Cert', link: '/guide/config-cert'},
|
{ text: 'Cert', link: '/guide/config-cert' },
|
||||||
{text: 'Cluster', link: '/guide/config-cluster'},
|
{ text: 'Cluster', link: '/guide/config-cluster' },
|
||||||
{text: 'Crypto', link: '/guide/config-crypto'},
|
{ text: 'Crypto', link: '/guide/config-crypto' },
|
||||||
{text: 'Http', link: '/guide/config-http'},
|
{ text: 'Http', link: '/guide/config-http' },
|
||||||
{text: 'Logrotate', link: '/guide/config-logrotate'},
|
{ text: 'Logrotate', link: '/guide/config-logrotate' },
|
||||||
{text: 'Nginx', link: '/guide/config-nginx'},
|
{ text: 'Nginx', link: '/guide/config-nginx' },
|
||||||
{text: 'Node', link: '/guide/config-node'},
|
{ text: 'Node', link: '/guide/config-node' },
|
||||||
{text: 'Open AI', link: '/guide/config-openai'},
|
{ text: 'Open AI', link: '/guide/config-openai' },
|
||||||
{text: 'Terminal', link: '/guide/config-terminal'},
|
{ text: 'Terminal', link: '/guide/config-terminal' },
|
||||||
{text: 'Webauthn', link: '/guide/config-webauthn'}
|
{ text: 'Webauthn', link: '/guide/config-webauthn' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Environment Variables',
|
text: 'Environment Variables',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
items: [
|
items: [
|
||||||
{text: 'Reference', link: '/guide/env'},
|
{ text: 'Reference', link: '/guide/env' },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Appendix',
|
text: 'Appendix',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
items: [
|
items: [
|
||||||
{text: 'Nginx Proxy Example', link: '/guide/nginx-proxy-example'},
|
{ text: 'Nginx Proxy Example', link: '/guide/nginx-proxy-example' },
|
||||||
{text: 'License', link: '/guide/license'}
|
{ text: 'License', link: '/guide/license' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { defineConfig } from 'vitepress'
|
import { defineConfig } from 'vitepress'
|
||||||
import {projectUrl, editLinkPattern} from './common'
|
import { projectUrl, editLinkPattern } from './common'
|
||||||
|
|
||||||
export const commitRef = process.env.COMMIT_REF ?
|
export const commitRef = process.env.COMMIT_REF ?
|
||||||
`<a href="${projectUrl}/commit/${process.env.COMMIT_REF}">` + process.env.COMMIT_REF.slice(0, 8) + '</a>':
|
`<a href="${projectUrl}/commit/${process.env.COMMIT_REF}">` + process.env.COMMIT_REF.slice(0, 8) + '</a>' :
|
||||||
'dev'
|
'dev'
|
||||||
|
|
||||||
function thisYear() {
|
function thisYear() {
|
||||||
|
@ -37,7 +37,13 @@ export const sharedConfig = defineConfig({
|
||||||
},
|
},
|
||||||
|
|
||||||
socialLinks: [
|
socialLinks: [
|
||||||
{icon: 'github', link: projectUrl}
|
{ icon: 'github', link: projectUrl }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
vite: {
|
||||||
|
server: {
|
||||||
|
port: Number.parseInt(process.env.VITE_PORT ?? '3003')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import {LocaleSpecificConfig, DefaultTheme} from 'vitepress'
|
import { LocaleSpecificConfig, DefaultTheme } from 'vitepress'
|
||||||
import {demoUrl, editLinkPattern} from './common'
|
import { demoUrl, editLinkPattern } from './common'
|
||||||
|
|
||||||
export const zhCNConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
|
export const zhCNConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
|
||||||
themeConfig: {
|
themeConfig: {
|
||||||
nav: [
|
nav: [
|
||||||
{text: '首页', link: '/zh_CN/'},
|
{ text: '首页', link: '/zh_CN/' },
|
||||||
{text: '手册', link: '/zh_CN/guide/about'},
|
{ text: '手册', link: '/zh_CN/guide/about' },
|
||||||
{text: '演示', link: demoUrl}
|
{ text: '演示', link: demoUrl }
|
||||||
],
|
],
|
||||||
|
|
||||||
editLink: {
|
editLink: {
|
||||||
|
@ -20,55 +20,56 @@ export const zhCNConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
|
||||||
text: '介绍',
|
text: '介绍',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
items: [
|
items: [
|
||||||
{text: '何为 Nginx UI?', link: '/zh_CN/guide/about'},
|
{ text: '何为 Nginx UI?', link: '/zh_CN/guide/about' },
|
||||||
{text: '即刻开始', link: '/zh_CN/guide/getting-started'},
|
{ text: '即刻开始', link: '/zh_CN/guide/getting-started' },
|
||||||
{text: '安装脚本', link: '/zh_CN/guide/install-script-linux'}
|
{ text: '安装脚本', link: '/zh_CN/guide/install-script-linux' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '开发',
|
text: '开发',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
items: [
|
items: [
|
||||||
{text: '构建', link: '/zh_CN/guide/build'},
|
{ text: '开发容器', link: '/zh_CN/guide/devcontainer' },
|
||||||
{text: '项目结构', link: '/zh_CN/guide/project-structure'},
|
{ text: '构建', link: '/zh_CN/guide/build' },
|
||||||
{text: '配置模板', link: '/zh_CN/guide/nginx-ui-template'},
|
{ text: '项目结构', link: '/zh_CN/guide/project-structure' },
|
||||||
{text: '贡献代码', link: '/zh_CN/guide/contributing'}
|
{ text: '配置模板', link: '/zh_CN/guide/nginx-ui-template' },
|
||||||
|
{ text: '贡献代码', link: '/zh_CN/guide/contributing' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '配置',
|
text: '配置',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
items: [
|
items: [
|
||||||
{text: 'App', link: '/zh_CN/guide/config-app'},
|
{ text: 'App', link: '/zh_CN/guide/config-app' },
|
||||||
{text: 'Server', link: '/zh_CN/guide/config-server'},
|
{ text: 'Server', link: '/zh_CN/guide/config-server' },
|
||||||
{text: 'Database', link: '/zh_CN/guide/config-database'},
|
{ text: 'Database', link: '/zh_CN/guide/config-database' },
|
||||||
{text: 'Auth', link: '/zh_CN/guide/config-auth'},
|
{ text: 'Auth', link: '/zh_CN/guide/config-auth' },
|
||||||
{text: 'Casdoor', link: '/zh_CN/guide/config-casdoor'},
|
{ text: 'Casdoor', link: '/zh_CN/guide/config-casdoor' },
|
||||||
{text: 'Cert', link: '/zh_CN/guide/config-cert'},
|
{ text: 'Cert', link: '/zh_CN/guide/config-cert' },
|
||||||
{text: 'Cluster', link: '/zh_CN/guide/config-cluster'},
|
{ text: 'Cluster', link: '/zh_CN/guide/config-cluster' },
|
||||||
{text: 'Crypto', link: '/zh_CN/guide/config-crypto'},
|
{ text: 'Crypto', link: '/zh_CN/guide/config-crypto' },
|
||||||
{text: 'Http', link: '/zh_CN/guide/config-http'},
|
{ text: 'Http', link: '/zh_CN/guide/config-http' },
|
||||||
{text: 'Logrotate', link: '/zh_CN/guide/config-logrotate'},
|
{ text: 'Logrotate', link: '/zh_CN/guide/config-logrotate' },
|
||||||
{text: 'Nginx', link: '/zh_CN/guide/config-nginx'},
|
{ text: 'Nginx', link: '/zh_CN/guide/config-nginx' },
|
||||||
{text: 'Node', link: '/zh_CN/guide/config-node'},
|
{ text: 'Node', link: '/zh_CN/guide/config-node' },
|
||||||
{text: 'Open AI', link: '/zh_CN/guide/config-openai'},
|
{ text: 'Open AI', link: '/zh_CN/guide/config-openai' },
|
||||||
{text: 'Terminal', link: '/zh_CN/guide/config-terminal'},
|
{ text: 'Terminal', link: '/zh_CN/guide/config-terminal' },
|
||||||
{text: 'Webauthn', link: '/zh_CN/guide/config-webauthn'}
|
{ text: 'Webauthn', link: '/zh_CN/guide/config-webauthn' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '环境变量',
|
text: '环境变量',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
items: [
|
items: [
|
||||||
{text: '参考手册', link: '/zh_CN/guide/env'},
|
{ text: '参考手册', link: '/zh_CN/guide/env' },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '附录',
|
text: '附录',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
items: [
|
items: [
|
||||||
{text: 'Nginx 代理示例', link: '/zh_CN/guide/nginx-proxy-example'},
|
{ text: 'Nginx 代理示例', link: '/zh_CN/guide/nginx-proxy-example' },
|
||||||
{text: '开源协议', link: '/zh_CN/guide/license'}
|
{ text: '开源协议', link: '/zh_CN/guide/license' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import {LocaleSpecificConfig, DefaultTheme} from 'vitepress'
|
import { LocaleSpecificConfig, DefaultTheme } from 'vitepress'
|
||||||
import {demoUrl, editLinkPattern} from './common'
|
import { demoUrl, editLinkPattern } from './common'
|
||||||
|
|
||||||
export const zhTWConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
|
export const zhTWConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
|
||||||
themeConfig: {
|
themeConfig: {
|
||||||
nav: [
|
nav: [
|
||||||
{text: '首頁', link: '/zh_TW/'},
|
{ text: '首頁', link: '/zh_TW/' },
|
||||||
{text: '手冊', link: '/zh_TW/guide/about'},
|
{ text: '手冊', link: '/zh_TW/guide/about' },
|
||||||
{text: '演示', link: demoUrl}
|
{ text: '演示', link: demoUrl }
|
||||||
],
|
],
|
||||||
|
|
||||||
editLink: {
|
editLink: {
|
||||||
|
@ -20,55 +20,56 @@ export const zhTWConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
|
||||||
text: '介紹',
|
text: '介紹',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
items: [
|
items: [
|
||||||
{text: '何為 Nginx UI?', link: '/zh_TW/guide/about'},
|
{ text: '何為 Nginx UI?', link: '/zh_TW/guide/about' },
|
||||||
{text: '即刻開始', link: '/zh_TW/guide/getting-started'},
|
{ text: '即刻開始', link: '/zh_TW/guide/getting-started' },
|
||||||
{text: '安裝指令碼', link: '/zh_TW/guide/install-script-linux'}
|
{ text: '安裝指令碼', link: '/zh_TW/guide/install-script-linux' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '開發',
|
text: '開發',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
items: [
|
items: [
|
||||||
{text: '構建', link: '/zh_TW/guide/build'},
|
{ text: '開發容器', link: '/zh_TW/guide/devcontainer' },
|
||||||
{text: '專案結構', link: '/zh_TW/guide/project-structure'},
|
{ text: '構建', link: '/zh_TW/guide/build' },
|
||||||
{text: '配置模板', link: '/zh_TW/guide/nginx-ui-template'},
|
{ text: '專案結構', link: '/zh_TW/guide/project-structure' },
|
||||||
{text: '貢獻程式碼', link: '/zh_TW/guide/contributing'}
|
{ text: '配置模板', link: '/zh_TW/guide/nginx-ui-template' },
|
||||||
|
{ text: '貢獻程式碼', link: '/zh_TW/guide/contributing' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '配置',
|
text: '配置',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
items: [
|
items: [
|
||||||
{text: 'App', link: '/zh_TW/guide/config-app'},
|
{ text: 'App', link: '/zh_TW/guide/config-app' },
|
||||||
{text: 'Server', link: '/zh_TW/guide/config-server'},
|
{ text: 'Server', link: '/zh_TW/guide/config-server' },
|
||||||
{text: 'Database', link: '/zh_TW/guide/config-database'},
|
{ text: 'Database', link: '/zh_TW/guide/config-database' },
|
||||||
{text: 'Auth', link: '/zh_TW/guide/config-auth'},
|
{ text: 'Auth', link: '/zh_TW/guide/config-auth' },
|
||||||
{text: 'Casdoor', link: '/zh_TW/guide/config-casdoor'},
|
{ text: 'Casdoor', link: '/zh_TW/guide/config-casdoor' },
|
||||||
{text: 'Cert', link: '/zh_TW/guide/config-cert'},
|
{ text: 'Cert', link: '/zh_TW/guide/config-cert' },
|
||||||
{text: 'Cluster', link: '/zh_TW/guide/config-cluster'},
|
{ text: 'Cluster', link: '/zh_TW/guide/config-cluster' },
|
||||||
{text: 'Crypto', link: '/zh_TW/guide/config-crypto'},
|
{ text: 'Crypto', link: '/zh_TW/guide/config-crypto' },
|
||||||
{text: 'Http', link: '/zh_TW/guide/config-http'},
|
{ text: 'Http', link: '/zh_TW/guide/config-http' },
|
||||||
{text: 'Logrotate', link: '/zh_TW/guide/config-logrotate'},
|
{ text: 'Logrotate', link: '/zh_TW/guide/config-logrotate' },
|
||||||
{text: 'Nginx', link: '/zh_TW/guide/config-nginx'},
|
{ text: 'Nginx', link: '/zh_TW/guide/config-nginx' },
|
||||||
{text: 'Node', link: '/zh_TW/guide/config-node'},
|
{ text: 'Node', link: '/zh_TW/guide/config-node' },
|
||||||
{text: 'Open AI', link: '/zh_TW/guide/config-openai'},
|
{ text: 'Open AI', link: '/zh_TW/guide/config-openai' },
|
||||||
{text: 'Terminal', link: '/zh_TW/guide/config-terminal'},
|
{ text: 'Terminal', link: '/zh_TW/guide/config-terminal' },
|
||||||
{text: 'Webauthn', link: '/zh_TW/guide/config-webauthn'}
|
{ text: 'Webauthn', link: '/zh_TW/guide/config-webauthn' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '環境變量',
|
text: '環境變量',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
items: [
|
items: [
|
||||||
{text: '參考手冊', link: '/zh_TW/guide/env'},
|
{ text: '參考手冊', link: '/zh_TW/guide/env' },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '附錄',
|
text: '附錄',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
items: [
|
items: [
|
||||||
{text: 'Nginx 代理示例', link: '/zh_TW/guide/nginx-proxy-example'},
|
{ text: 'Nginx 代理示例', link: '/zh_TW/guide/nginx-proxy-example' },
|
||||||
{text: '開源協議', link: '/zh_TW/guide/license'}
|
{ text: '開源協議', link: '/zh_TW/guide/license' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
50
docs/guide/devcontainer.md
Normal file
50
docs/guide/devcontainer.md
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
# Devcontainer
|
||||||
|
|
||||||
|
You'll need to set up a development environment if you want to develop on this project.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Docker
|
||||||
|
- VSCode (Cursor)
|
||||||
|
- Git
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
1. Open the Command Palette in VSCode (Cursor)
|
||||||
|
- Mac: `Cmd`+`Shift`+`P`
|
||||||
|
- Windows: `Ctrl`+`Shift`+`P`
|
||||||
|
2. Search for `Dev Containers: Rebuild and Reopen in Container` and click on it
|
||||||
|
3. Wait for the container to start
|
||||||
|
4. Open the Command Palette in VSCode (Cursor)
|
||||||
|
- Mac: `Cmd`+`Shift`+`P`
|
||||||
|
- Windows: `Ctrl`+`Shift`+`P`
|
||||||
|
5. Select Tasks: Run Task -> Start all services
|
||||||
|
6. Wait for the services to start
|
||||||
|
|
||||||
|
## Ports
|
||||||
|
|
||||||
|
| Port | Service |
|
||||||
|
|-------|------------------|
|
||||||
|
| 3002 | App |
|
||||||
|
| 3003 | Documentation |
|
||||||
|
| 9000 | API Backend |
|
||||||
|
|
||||||
|
|
||||||
|
## Services
|
||||||
|
|
||||||
|
- nginx-ui
|
||||||
|
- nginx-ui-2
|
||||||
|
- casdoor
|
||||||
|
- chaltestsrv
|
||||||
|
- pebble
|
||||||
|
|
||||||
|
## Multi-node development
|
||||||
|
|
||||||
|
Add the following enviroment in the main node:
|
||||||
|
|
||||||
|
```
|
||||||
|
name: nginx-ui-2
|
||||||
|
url: http://nginx-ui-2
|
||||||
|
token: nginx-ui-2
|
||||||
|
```
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "nginx-ui-docs",
|
"name": "nginx-ui-docs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"docs:dev": "vitepress dev",
|
"docs:dev": "vitepress dev --host",
|
||||||
"docs:build": "vitepress build",
|
"docs:build": "vitepress build",
|
||||||
"docs:preview": "vitepress preview"
|
"docs:preview": "vitepress preview"
|
||||||
},
|
},
|
||||||
|
|
48
docs/zh_CN/guide/devcontainer.md
Normal file
48
docs/zh_CN/guide/devcontainer.md
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
# 开发容器
|
||||||
|
|
||||||
|
如果您想参与本项目开发,需要设置开发环境。
|
||||||
|
|
||||||
|
## 前提条件
|
||||||
|
|
||||||
|
- Docker
|
||||||
|
- VSCode (Cursor)
|
||||||
|
- Git
|
||||||
|
|
||||||
|
## 设置步骤
|
||||||
|
|
||||||
|
1. 在 VSCode (Cursor) 中打开命令面板
|
||||||
|
- Mac: `Cmd`+`Shift`+`P`
|
||||||
|
- Windows: `Ctrl`+`Shift`+`P`
|
||||||
|
2. 搜索 `Dev Containers: 重新生成并重新打开容器` 并点击
|
||||||
|
3. 等待容器启动
|
||||||
|
4. 再次打开命令面板
|
||||||
|
- Mac: `Cmd`+`Shift`+`P`
|
||||||
|
- Windows: `Ctrl`+`Shift`+`P`
|
||||||
|
5. 选择 任务: 运行任务 -> 启动所有服务
|
||||||
|
6. 等待所有服务启动完成
|
||||||
|
|
||||||
|
## 端口映射
|
||||||
|
|
||||||
|
| 端口 | 服务 |
|
||||||
|
|-------|-------------------|
|
||||||
|
| 3002 | 主应用 |
|
||||||
|
| 3003 | 文档 |
|
||||||
|
| 9000 | API 后端 |
|
||||||
|
|
||||||
|
## 服务列表
|
||||||
|
|
||||||
|
- nginx-ui
|
||||||
|
- nginx-ui-2
|
||||||
|
- casdoor
|
||||||
|
- chaltestsrv
|
||||||
|
- pebble
|
||||||
|
|
||||||
|
## 多节点开发
|
||||||
|
|
||||||
|
在主节点中添加以下环境配置:
|
||||||
|
|
||||||
|
```
|
||||||
|
name: nginx-ui-2
|
||||||
|
url: http://nginx-ui-2
|
||||||
|
token: nginx-ui-2
|
||||||
|
```
|
48
docs/zh_TW/guide/devcontainer.md
Normal file
48
docs/zh_TW/guide/devcontainer.md
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
# 開發容器
|
||||||
|
|
||||||
|
如果您想參與本專案開發,需要設定開發環境。
|
||||||
|
|
||||||
|
## 必要條件
|
||||||
|
|
||||||
|
- Docker
|
||||||
|
- VSCode (Cursor)
|
||||||
|
- Git
|
||||||
|
|
||||||
|
## 設定步驟
|
||||||
|
|
||||||
|
1. 在 VSCode (Cursor) 中開啟指令面板
|
||||||
|
- Mac: `Cmd`+`Shift`+`P`
|
||||||
|
- Windows: `Ctrl`+`Shift`+`P`
|
||||||
|
2. 搜尋 `Dev Containers: 重新產生並重新開啟容器` 並點擊
|
||||||
|
3. 等待容器啟動
|
||||||
|
4. 再次開啟指令面板
|
||||||
|
- Mac: `Cmd`+`Shift`+`P`
|
||||||
|
- Windows: `Ctrl`+`Shift`+`P`
|
||||||
|
5. 選擇 任務: 執行任務 -> 啟動所有服務
|
||||||
|
6. 等待所有服務啟動完成
|
||||||
|
|
||||||
|
## 連接埠映射
|
||||||
|
|
||||||
|
| 連接埠 | 服務 |
|
||||||
|
|-------|-------------------|
|
||||||
|
| 3002 | 主應用 |
|
||||||
|
| 3003 | 文件 |
|
||||||
|
| 9000 | API 後端 |
|
||||||
|
|
||||||
|
## 服務清單
|
||||||
|
|
||||||
|
- nginx-ui
|
||||||
|
- nginx-ui-2
|
||||||
|
- casdoor
|
||||||
|
- chaltestsrv
|
||||||
|
- pebble
|
||||||
|
|
||||||
|
## 多節點開發
|
||||||
|
|
||||||
|
在主節點中新增以下環境設定:
|
||||||
|
|
||||||
|
```
|
||||||
|
name: nginx-ui-2
|
||||||
|
url: http://nginx-ui-2
|
||||||
|
token: nginx-ui-2
|
||||||
|
```
|
29
internal/middleware/embed.go
Normal file
29
internal/middleware/embed.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
//go:build !unembed
|
||||||
|
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/0xJacky/Nginx-UI/app"
|
||||||
|
"github.com/gin-contrib/static"
|
||||||
|
"github.com/uozi-tech/cosy/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MustFs(dir string) (serverFileSystem static.ServeFileSystem) {
|
||||||
|
|
||||||
|
sub, err := fs.Sub(app.DistFS, path.Join("dist", dir))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
serverFileSystem = ServerFileSystemType{
|
||||||
|
http.FS(sub),
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -2,16 +2,14 @@ package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"github.com/0xJacky/Nginx-UI/app"
|
|
||||||
"github.com/0xJacky/Nginx-UI/internal/user"
|
|
||||||
"github.com/0xJacky/Nginx-UI/settings"
|
|
||||||
"github.com/gin-contrib/static"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/uozi-tech/cosy/logger"
|
|
||||||
"io/fs"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/0xJacky/Nginx-UI/internal/user"
|
||||||
|
"github.com/0xJacky/Nginx-UI/settings"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/uozi-tech/cosy/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AuthRequired() gin.HandlerFunc {
|
func AuthRequired() gin.HandlerFunc {
|
||||||
|
@ -72,22 +70,6 @@ func (f ServerFileSystemType) Exists(prefix string, _path string) bool {
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func MustFs(dir string) (serverFileSystem static.ServeFileSystem) {
|
|
||||||
|
|
||||||
sub, err := fs.Sub(app.DistFS, path.Join("dist", dir))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
serverFileSystem = ServerFileSystemType{
|
|
||||||
http.FS(sub),
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func CacheJs() gin.HandlerFunc {
|
func CacheJs() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
if strings.Contains(c.Request.URL.String(), "js") {
|
if strings.Contains(c.Request.URL.String(), "js") {
|
||||||
|
|
|
@ -2,11 +2,12 @@ package upgrader
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/0xJacky/Nginx-UI/app"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/0xJacky/Nginx-UI/app"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RuntimeInfo struct {
|
type RuntimeInfo struct {
|
||||||
|
@ -43,7 +44,7 @@ func GetRuntimeInfo() (r RuntimeInfo, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCurrentVersion() (c CurVersion, err error) {
|
func GetCurrentVersion() (c CurVersion, err error) {
|
||||||
verJson, err := app.DistFS.ReadFile("dist/version.json")
|
verJson, err := app.DistFS.ReadFile(app.VersionPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.Wrap(err, "service.GetCurrentVersion ReadFile err")
|
err = errors.Wrap(err, "service.GetCurrentVersion ReadFile err")
|
||||||
return
|
return
|
||||||
|
|
7
main.go
7
main.go
|
@ -1,22 +1,23 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/0xJacky/Nginx-UI/internal/kernel"
|
"github.com/0xJacky/Nginx-UI/internal/kernel"
|
||||||
"github.com/0xJacky/Nginx-UI/model"
|
"github.com/0xJacky/Nginx-UI/model"
|
||||||
"github.com/0xJacky/Nginx-UI/router"
|
"github.com/0xJacky/Nginx-UI/router"
|
||||||
"github.com/0xJacky/Nginx-UI/settings"
|
"github.com/0xJacky/Nginx-UI/settings"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/jpillora/overseer"
|
"github.com/jpillora/overseer"
|
||||||
"errors"
|
|
||||||
"github.com/uozi-tech/cosy"
|
"github.com/uozi-tech/cosy"
|
||||||
cKernel "github.com/uozi-tech/cosy/kernel"
|
cKernel "github.com/uozi-tech/cosy/kernel"
|
||||||
"github.com/uozi-tech/cosy/logger"
|
"github.com/uozi-tech/cosy/logger"
|
||||||
cRouter "github.com/uozi-tech/cosy/router"
|
cRouter "github.com/uozi-tech/cosy/router"
|
||||||
cSettings "github.com/uozi-tech/cosy/settings"
|
cSettings "github.com/uozi-tech/cosy/settings"
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Program(confPath string) func(state overseer.State) {
|
func Program(confPath string) func(state overseer.State) {
|
||||||
|
|
|
@ -10,7 +10,7 @@ type Environment struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
Enabled bool `json:"enabled" gorm:"default:true"`
|
Enabled bool `json:"enabled" gorm:"default:false"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Environment) GetUrl(uri string) (decodedUri string, err error) {
|
func (e *Environment) GetUrl(uri string) (decodedUri string, err error) {
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
if [ "$(ls -A /etc/nginx)" = "" ]; then
|
|
||||||
echo "Initialing Nginx config dir"
|
|
||||||
cp -rp /usr/etc/nginx/* /etc/nginx/
|
|
||||||
echo "Initialed Nginx config dir"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "export PATH=$PATH:/usr/local/go/bin:$(go env GOPATH)/bin" >> ~/.profile
|
|
||||||
source ~/.profile
|
|
||||||
|
|
||||||
nginx
|
|
||||||
cd /app && air
|
|
|
@ -1,9 +0,0 @@
|
||||||
# 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
|
|
||||||
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted universe multiverse
|
|
||||||
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted universe multiverse
|
|
||||||
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
|
|
||||||
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
|
|
||||||
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
|
|
||||||
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
|
|
||||||
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-security main restricted universe multiverse
|
|
||||||
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-security main restricted universe multiverse
|
|
|
@ -1,6 +1,8 @@
|
||||||
package router
|
package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/0xJacky/Nginx-UI/api/analytic"
|
"github.com/0xJacky/Nginx-UI/api/analytic"
|
||||||
"github.com/0xJacky/Nginx-UI/api/certificate"
|
"github.com/0xJacky/Nginx-UI/api/certificate"
|
||||||
"github.com/0xJacky/Nginx-UI/api/cluster"
|
"github.com/0xJacky/Nginx-UI/api/cluster"
|
||||||
|
@ -19,19 +21,14 @@ import (
|
||||||
"github.com/0xJacky/Nginx-UI/api/upstream"
|
"github.com/0xJacky/Nginx-UI/api/upstream"
|
||||||
"github.com/0xJacky/Nginx-UI/api/user"
|
"github.com/0xJacky/Nginx-UI/api/user"
|
||||||
"github.com/0xJacky/Nginx-UI/internal/middleware"
|
"github.com/0xJacky/Nginx-UI/internal/middleware"
|
||||||
"github.com/gin-contrib/static"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/uozi-tech/cosy"
|
"github.com/uozi-tech/cosy"
|
||||||
"net/http"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitRouter() {
|
func InitRouter() {
|
||||||
r := cosy.GetEngine()
|
r := cosy.GetEngine()
|
||||||
r.Use(
|
|
||||||
middleware.CacheJs(),
|
initEmbedRoute(r)
|
||||||
middleware.IPWhiteList(),
|
|
||||||
static.Serve("/", middleware.MustFs("")),
|
|
||||||
)
|
|
||||||
|
|
||||||
r.NoRoute(func(c *gin.Context) {
|
r.NoRoute(func(c *gin.Context) {
|
||||||
c.JSON(http.StatusNotFound, gin.H{
|
c.JSON(http.StatusNotFound, gin.H{
|
||||||
|
|
17
router/routers_embed.go
Normal file
17
router/routers_embed.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
//go:build !unembed
|
||||||
|
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/0xJacky/Nginx-UI/internal/middleware"
|
||||||
|
"github.com/gin-contrib/static"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initEmbedRoute(r *gin.Engine) {
|
||||||
|
r.Use(
|
||||||
|
middleware.CacheJs(),
|
||||||
|
middleware.IPWhiteList(),
|
||||||
|
static.Serve("/", middleware.MustFs("")),
|
||||||
|
)
|
||||||
|
}
|
8
router/routers_unembed.go
Normal file
8
router/routers_unembed.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
//go:build unembed
|
||||||
|
|
||||||
|
package router
|
||||||
|
|
||||||
|
import "github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
func initEmbedRoute(r *gin.Engine) {
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue