Refactor installer menus and update project paths
All checks were successful
Docker Cloud Build / Build & Publish Image (push) Successful in 1m7s

This commit is contained in:
xtcnet 2026-03-19 12:56:05 +07:00
parent 14c4e1ee5c
commit fd7398be9f
4 changed files with 508 additions and 74 deletions

View file

@ -10,7 +10,7 @@ WireGuard management module and some project-specific admin features.
Canonical repository: Canonical repository:
- `https://src.d3v.ac/xtcnet/D3V-Server` - `https://src.d3v.ac/d3v/D3V-Server`
Treat that repository as the source of truth for this project. Treat that repository as the source of truth for this project.
If another upstream is mentioned, assume it is a historical ancestor or reference, If another upstream is mentioned, assume it is a historical ancestor or reference,

View file

@ -1,10 +1,10 @@
# D3V-NPMWG — xGat3 + WireGuard VPN # D3V Gateway — Reverse Proxy + WireGuard VPN
A powerful, all-in-one Docker container that combines **xGat3** (reverse proxy with SSL) and **WireGuard VPN** management in a single web interface. A lightweight all-in-one Docker deployment that combines reverse proxy management with SSL and **WireGuard VPN** in a single web interface.
## Features ## Features
### xGat3 ### Gateway
- Reverse proxy management with SSL (Let's Encrypt) - Reverse proxy management with SSL (Let's Encrypt)
- Proxy hosts, redirection hosts, streams, and 404 hosts - Proxy hosts, redirection hosts, streams, and 404 hosts
- Access control lists and audit logging - Access control lists and audit logging
@ -29,24 +29,30 @@ A powerful, all-in-one Docker container that combines **xGat3** (reverse proxy w
## Quick Start ## Quick Start
```bash ```bash
curl -sSL https://src.d3v.ac/xtcnet/D3V-Server/raw/branch/master/install.sh -o install.sh curl -sSL https://src.d3v.ac/d3v/D3V-Server/raw/branch/master/install.sh -o install.sh
chmod +x install.sh chmod +x install.sh
sudo ./install.sh sudo ./install.sh
``` ```
**Menu options:** **Main menu:**
- `1` Install D3V-NPMWG - `1` Gateway
- `2` Uninstall D3V-NPMWG - `2` Blog
- `3` Uninstall D3V-NPMWG + Docker (Purge) - `3` Forgejo
- `4` Status / Logs / Health Check
- `5` Exit
**Gateway submenu:**
- `1` Install Gateway
- `2` Uninstall Gateway
- `3` Uninstall Gateway + Docker (Purge)
- `4` Reset Admin Password - `4` Reset Admin Password
- `5` Update D3V-NPMWG - `5` Update Gateway
- `6` Manage Custom Stream Ports - `6` Manage Custom Stream Ports
- `7` Toggle Admin Port 81 (Block/Unblock) - `7` Toggle Admin Port 81 (Block/Unblock)
- `8` Forgejo → Install / Uninstall / Update / Install Runner / Uninstall Runner
You can also run commands directly: You can also run commands directly:
```bash ```bash
sudo ./install.sh {install|uninstall|purge|reset|update|manage-ports|toggle-port|forgejo} sudo ./install.sh {gateway|gateway-install|gateway-uninstall|gateway-purge|gateway-reset|gateway-update|manage-ports|toggle-port|blog|blog-install|blog-update|blog-uninstall|forgejo|runner-update|ops}
``` ```
--- ---
@ -56,7 +62,7 @@ sudo ./install.sh {install|uninstall|purge|reset|update|manage-ports|toggle-port
```yaml ```yaml
services: services:
d3v-npmwg: d3v-npmwg:
image: src.d3v.ac/xtcnet/d3v-server:latest image: src.d3v.ac/d3v/d3v-server:latest
container_name: d3v-npmwg container_name: d3v-npmwg
restart: unless-stopped restart: unless-stopped
cap_add: cap_add:
@ -107,16 +113,16 @@ services:
## Building from Source ## Building from Source
```bash ```bash
git clone https://src.d3v.ac/xtcnet/D3V-Server.git git clone https://src.d3v.ac/d3v/D3V-Server.git
cd D3V-Server cd D3V-Server
cd frontend && yarn install && yarn locale-compile && yarn build && cd .. cd frontend && yarn install && yarn locale-compile && yarn build && cd ..
docker build -t d3v-npmwg -f docker/Dockerfile . docker build -t d3v-gateway -f docker/Dockerfile .
``` ```
### CI/CD ### CI/CD
Pushes to `master` that touch `backend/`, `frontend/`, or `docker/` automatically build and push the Docker image to `src.d3v.ac/xtcnet/d3v-server:latest` via Forgejo Actions. Pushes to `master` that touch `backend/`, `frontend/`, or `docker/` automatically build and push the Docker image to `src.d3v.ac/d3v/d3v-server:latest` via Forgejo Actions.
--- ---
@ -129,7 +135,7 @@ Pushes to `master` that touch `backend/`, `frontend/`, or `docker/` automaticall
## Credits ## Credits
- [xGat3](https://github.com/NginxProxyManager/nginx-proxy-manager) — Original proxy manager - [Nginx Proxy Manager](https://github.com/NginxProxyManager/nginx-proxy-manager) — Original proxy manager
- [wg-easy](https://github.com/wg-easy/wg-easy) — WireGuard management inspiration - [wg-easy](https://github.com/wg-easy/wg-easy) — WireGuard management inspiration
## License ## License

View file

@ -76,7 +76,7 @@ ENTRYPOINT [ "/init" ]
LABEL org.label-schema.schema-version="1.0" \ LABEL org.label-schema.schema-version="1.0" \
org.label-schema.license="MIT" \ org.label-schema.license="MIT" \
org.label-schema.name="d3v-npmwg" \ org.label-schema.name="d3v-gateway" \
org.label-schema.description="xGat3 : xGat3 + WireGuard VPN Manager" \ org.label-schema.description="D3V Gateway: Reverse Proxy + WireGuard VPN Manager" \
org.label-schema.url="https://github.com/xtcnet/D3V-NPMWG" \ org.label-schema.url="https://src.d3v.ac/d3v/D3V-Server" \
org.label-schema.cmd="docker run --rm -ti --cap-add=NET_ADMIN --cap-add=SYS_MODULE d3v-npmwg:latest" org.label-schema.cmd="docker run --rm -ti --cap-add=NET_ADMIN --cap-add=SYS_MODULE src.d3v.ac/d3v/d3v-server:latest"

View file

@ -2,15 +2,20 @@
set -e set -e
# ============================================================ # ============================================================
# D3V-NPMWG Installer for Ubuntu/Debian # D3V Gateway Installer for Ubuntu/Debian
# xGat3 + WireGuard VPN # Reverse Proxy + WireGuard VPN
# https://src.d3v.ac/xtcnet/D3V-Server # https://src.d3v.ac/d3v/D3V-Server
# ============================================================ # ============================================================
INSTALL_DIR="/opt/d3v-npmwg" INSTALL_DIR="/opt/d3v-npmwg"
COMPOSE_FILE="${INSTALL_DIR}/docker-compose.yml" COMPOSE_FILE="${INSTALL_DIR}/docker-compose.yml"
CONTAINER_NAME="d3v-npmwg" CONTAINER_NAME="d3v-npmwg"
IMAGE_NAME="src.d3v.ac/xtcnet/d3v-server:latest" IMAGE_NAME="src.d3v.ac/d3v/d3v-server:latest"
BLOG_INSTALL_DIR="/opt/blog"
BLOG_COMPOSE_FILE="${BLOG_INSTALL_DIR}/docker-compose.yml"
BLOG_CONTAINER_NAME="d3v-blog"
BLOG_IMAGE="nginx:alpine"
FORGEJO_INSTALL_DIR="/opt/forgejo" FORGEJO_INSTALL_DIR="/opt/forgejo"
FORGEJO_COMPOSE_FILE="${FORGEJO_INSTALL_DIR}/docker-compose.yml" FORGEJO_COMPOSE_FILE="${FORGEJO_INSTALL_DIR}/docker-compose.yml"
@ -39,7 +44,7 @@ log_err() { echo -e "${RED}[✗]${NC} $1"; }
separator() { echo -e "${GREEN}=================================================================${NC}"; } separator() { echo -e "${GREEN}=================================================================${NC}"; }
self_update() { self_update() {
local remote_url="https://src.d3v.ac/xtcnet/D3V-Server/raw/branch/master/install.sh" local remote_url="https://src.d3v.ac/d3v/D3V-Server/raw/branch/master/install.sh"
local tmp local tmp
tmp=$(mktemp) || return tmp=$(mktemp) || return
if curl -fsSL -m 10 "$remote_url" -o "$tmp" 2>/dev/null; then if curl -fsSL -m 10 "$remote_url" -o "$tmp" 2>/dev/null; then
@ -255,19 +260,19 @@ ensure_docker_network() {
} }
# ----------------------------------------------------------- # -----------------------------------------------------------
# 2. Install D3V-NPMWG # 2. Install Gateway
# ----------------------------------------------------------- # -----------------------------------------------------------
do_install() { do_install() {
require_root require_root
if [ -d "$INSTALL_DIR" ]; then if [ -d "$INSTALL_DIR" ]; then
log_warn "D3V-NPMWG is already installed at ${INSTALL_DIR}." log_warn "Gateway is already installed at ${INSTALL_DIR}."
log_warn "Use the Update option to pull the latest image, or Uninstall first." log_warn "Use the Update option to pull the latest image, or Uninstall first."
return return
fi fi
separator separator
echo -e "${BOLD} D3V-NPMWG Installation${NC}" echo -e "${BOLD} Gateway Installation${NC}"
separator separator
echo "" echo ""
@ -338,7 +343,7 @@ do_install() {
if docker ps --format '{{.Names}}' | grep -q "$CONTAINER_NAME"; then if docker ps --format '{{.Names}}' | grep -q "$CONTAINER_NAME"; then
echo "" echo ""
separator separator
echo -e "${GREEN}${BOLD} D3V-NPMWG INSTALLED SUCCESSFULLY!${NC}" echo -e "${GREEN}${BOLD} GATEWAY INSTALLED SUCCESSFULLY!${NC}"
separator separator
echo -e " ${CYAN}Web Admin UI${NC} : ${BOLD}http://${wg_host}:81${NC}" echo -e " ${CYAN}Web Admin UI${NC} : ${BOLD}http://${wg_host}:81${NC}"
echo -e " ${CYAN}HTTP Proxy${NC} : port 80" echo -e " ${CYAN}HTTP Proxy${NC} : port 80"
@ -354,17 +359,17 @@ do_install() {
} }
# ----------------------------------------------------------- # -----------------------------------------------------------
# 3. Uninstall D3V-NPMWG # 3. Uninstall Gateway
# ----------------------------------------------------------- # -----------------------------------------------------------
do_uninstall() { do_uninstall() {
require_root require_root
if [ ! -d "$INSTALL_DIR" ]; then if [ ! -d "$INSTALL_DIR" ]; then
log_warn "D3V-NPMWG is not installed at ${INSTALL_DIR}." log_warn "Gateway is not installed at ${INSTALL_DIR}."
return return
fi fi
log_warn "This will stop and remove D3V-NPMWG and ALL its data." log_warn "This will stop and remove Gateway and ALL its data."
read -rp "$(echo -e "${RED}Are you sure? (y/N): ${NC}")" confirm read -rp "$(echo -e "${RED}Are you sure? (y/N): ${NC}")" confirm
if [[ ! "$confirm" =~ ^[yY]$ ]]; then if [[ ! "$confirm" =~ ^[yY]$ ]]; then
echo "Cancelled." echo "Cancelled."
@ -379,7 +384,7 @@ do_uninstall() {
log_step "Removing ${INSTALL_DIR}..." log_step "Removing ${INSTALL_DIR}..."
rm -rf "$INSTALL_DIR" rm -rf "$INSTALL_DIR"
log_ok "D3V-NPMWG uninstalled." log_ok "Gateway uninstalled."
} }
# ----------------------------------------------------------- # -----------------------------------------------------------
@ -448,13 +453,13 @@ do_reset_password() {
} }
# ----------------------------------------------------------- # -----------------------------------------------------------
# 6. Update D3V-NPMWG # 6. Update Gateway
# ----------------------------------------------------------- # -----------------------------------------------------------
do_update() { do_update() {
require_root require_root
if [ ! -d "$INSTALL_DIR" ]; then if [ ! -d "$INSTALL_DIR" ]; then
log_err "D3V-NPMWG is not installed. Install it first." log_err "Gateway is not installed. Install it first."
return return
fi fi
@ -481,7 +486,7 @@ do_update() {
log_step "Recreating containers..." log_step "Recreating containers..."
$dc up -d $dc up -d
log_ok "D3V-NPMWG updated." log_ok "Gateway updated."
log_step "Cleaning old images..." log_step "Cleaning old images..."
docker image prune -f > /dev/null 2>&1 docker image prune -f > /dev/null 2>&1
@ -573,7 +578,207 @@ do_toggle_port_81() {
} }
# ----------------------------------------------------------- # -----------------------------------------------------------
# Forgejo — Install / Uninstall / Update # Blog - Install / Uninstall
# -----------------------------------------------------------
do_blog_install() {
require_root
if [ -d "$BLOG_INSTALL_DIR" ]; then
log_warn "Blog is already installed at ${BLOG_INSTALL_DIR}."
log_warn "Uninstall it first if you want to recreate it."
return
fi
separator
echo -e "${BOLD} Hugo Blog Static Host Installation${NC}"
separator
echo ""
install_deps
echo ""
ensure_docker_network
log_step "Creating ${BLOG_INSTALL_DIR}..."
mkdir -p "${BLOG_INSTALL_DIR}/public"
log_ok "Directory created."
if [ ! -f "${BLOG_INSTALL_DIR}/public/index.html" ]; then
log_step "Creating placeholder blog homepage..."
cat > "${BLOG_INSTALL_DIR}/public/index.html" <<HTML
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Blog is ready</title>
<style>
body {
font-family: system-ui, sans-serif;
max-width: 720px;
margin: 60px auto;
padding: 0 20px;
line-height: 1.6;
color: #1f2937;
background: #f9fafb;
}
code {
background: #e5e7eb;
padding: 2px 6px;
border-radius: 4px;
}
</style>
</head>
<body>
<h1>Hugo blog host is installed</h1>
<p>This container is ready to serve your generated Hugo site.</p>
<p>Deploy your blog build output into <code>/opt/blog/public</code>.</p>
<p>Then create a Proxy Host in Nginx Proxy Manager pointing to <code>${BLOG_CONTAINER_NAME}</code> on port <code>80</code>.</p>
</body>
</html>
HTML
log_ok "Placeholder page created."
fi
log_step "Generating blog docker-compose.yml..."
cat > "$BLOG_COMPOSE_FILE" <<YAML
services:
blog:
image: ${BLOG_IMAGE}
container_name: ${BLOG_CONTAINER_NAME}
restart: unless-stopped
volumes:
- ./public:/usr/share/nginx/html:ro
networks:
- d3v-net
networks:
d3v-net:
external: true
name: ${DOCKER_NETWORK}
YAML
log_ok "docker-compose.yml created."
local dc
dc=$(get_compose_cmd)
cd "$BLOG_INSTALL_DIR"
log_step "Pulling blog image..."
$dc pull
log_ok "Image pulled."
log_step "Starting blog container..."
$dc up -d
log_ok "Blog container started."
sleep 3
if docker ps --format '{{.Names}}' | grep -q "^${BLOG_CONTAINER_NAME}$"; then
echo ""
separator
echo -e "${GREEN}${BOLD} BLOG INSTALLED SUCCESSFULLY!${NC}"
separator
echo -e " ${CYAN}Container${NC} : ${BOLD}${BLOG_CONTAINER_NAME}${NC}"
echo -e " ${CYAN}Content Dir${NC}: ${BOLD}${BLOG_INSTALL_DIR}/public${NC}"
echo ""
echo -e " ${BOLD}Next steps${NC}"
echo -e " 1. Deploy your Hugo build output to ${BLOG_INSTALL_DIR}/public"
echo -e " 2. In Nginx Proxy Manager create a Proxy Host:"
echo -e " Domain Names : ${CYAN}blog.yourdomain.com${NC}"
echo -e " Scheme : ${CYAN}http${NC}"
echo -e " Forward Hostname : ${CYAN}${BLOG_CONTAINER_NAME}${NC}"
echo -e " Forward Port : ${CYAN}80${NC}"
echo -e " 3. Request an SSL certificate and enable Force SSL"
separator
else
log_err "Blog container did not start. Check logs:"
echo -e " docker logs ${BLOG_CONTAINER_NAME}"
fi
}
do_blog_uninstall() {
require_root
if [ ! -d "$BLOG_INSTALL_DIR" ]; then
log_warn "Blog is not installed at ${BLOG_INSTALL_DIR}."
return
fi
log_warn "This will stop and remove the static blog host and ALL deployed blog files."
read -rp "$(echo -e "${RED}Are you sure? (y/N): ${NC}")" confirm
if [[ ! "$confirm" =~ ^[yY]$ ]]; then
echo "Cancelled."
return
fi
local dc
dc=$(get_compose_cmd)
cd "$BLOG_INSTALL_DIR" && $dc down -v 2>/dev/null || true
cd /
log_step "Removing ${BLOG_INSTALL_DIR}..."
rm -rf "$BLOG_INSTALL_DIR"
log_ok "Blog uninstalled."
}
do_blog_update() {
require_root
if [ ! -d "$BLOG_INSTALL_DIR" ]; then
log_err "Blog is not installed. Install it first."
return
fi
separator
echo -e "${BOLD} Hugo Blog Static Host Update${NC}"
separator
echo ""
install_deps
ensure_docker_network
local dc
dc=$(get_compose_cmd)
cd "$BLOG_INSTALL_DIR" || return
log_step "Pulling latest blog image..."
$dc pull
log_ok "Image pulled."
log_step "Recreating blog container..."
$dc up -d
log_ok "Blog updated."
log_step "Cleaning old images..."
docker image prune -f > /dev/null 2>&1
log_ok "Done."
}
show_blog_menu() {
while true; do
echo ""
separator
echo -e "${BOLD} Blog Manager${NC}"
separator
echo " 1) Install Blog"
echo " 2) Update Blog"
echo " 3) Uninstall Blog"
echo " 4) Back"
separator
read -rp " Select [1-4]: " choice
echo ""
case "$choice" in
1) do_blog_install ;;
2) do_blog_update ;;
3) do_blog_uninstall ;;
4) return ;;
*) log_err "Invalid option." ;;
esac
done
}
# -----------------------------------------------------------
# Forgejo - Install / Uninstall / Update
# ----------------------------------------------------------- # -----------------------------------------------------------
do_forgejo_install() { do_forgejo_install() {
require_root require_root
@ -594,17 +799,17 @@ do_forgejo_install() {
ensure_docker_network ensure_docker_network
# Ensure D3V-NPMWG is on d3v-net (runtime + persistent in compose) # Ensure Gateway is on d3v-net (runtime + persistent in compose)
if [ -f "$COMPOSE_FILE" ]; then if [ -f "$COMPOSE_FILE" ]; then
if ! grep -q "d3v-net" "$COMPOSE_FILE"; then if ! grep -q "d3v-net" "$COMPOSE_FILE"; then
log_step "Updating D3V-NPMWG compose to join '${DOCKER_NETWORK}'..." log_step "Updating Gateway compose to join '${DOCKER_NETWORK}'..."
local current_wg_host="" local current_wg_host=""
current_wg_host=$(grep -E 'WG_HOST:' "$COMPOSE_FILE" | awk -F'"' '{print $2}') current_wg_host=$(grep -E 'WG_HOST:' "$COMPOSE_FILE" | awk -F'"' '{print $2}')
[ -z "$current_wg_host" ] && current_wg_host=$(detect_public_ip) [ -z "$current_wg_host" ] && current_wg_host=$(detect_public_ip)
cd "$INSTALL_DIR" cd "$INSTALL_DIR"
generate_docker_compose "$current_wg_host" generate_docker_compose "$current_wg_host"
$(get_compose_cmd) up -d --no-recreate 2>/dev/null || true $(get_compose_cmd) up -d --no-recreate 2>/dev/null || true
log_ok "D3V-NPMWG compose updated." log_ok "Gateway compose updated."
fi fi
# Connect running container immediately (no restart needed) # Connect running container immediately (no restart needed)
if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
@ -858,6 +1063,60 @@ do_forgejo_runner_install() {
separator separator
} }
do_forgejo_runner_update() {
require_root
if ! docker ps -a --format '{{.Names}}' | grep -q "^${FORGEJO_RUNNER_CONTAINER}$"; then
log_err "Forgejo Runner is not installed."
return
fi
log_step "Reading current Forgejo Runner configuration..."
local runner_env
runner_env=$(docker inspect -f '{{range .Config.Env}}{{println .}}{{end}}' "${FORGEJO_RUNNER_CONTAINER}" 2>/dev/null || true)
local forgejo_url
local runner_token
local runner_name
local runner_labels
forgejo_url=$(echo "$runner_env" | grep '^GITEA_INSTANCE_URL=' | head -n1 | cut -d= -f2-)
runner_token=$(echo "$runner_env" | grep '^GITEA_RUNNER_REGISTRATION_TOKEN=' | head -n1 | cut -d= -f2-)
runner_name=$(echo "$runner_env" | grep '^GITEA_RUNNER_NAME=' | head -n1 | cut -d= -f2-)
runner_labels=$(echo "$runner_env" | grep '^GITEA_RUNNER_LABELS=' | head -n1 | cut -d= -f2-)
if [ -z "$forgejo_url" ] || [ -z "$runner_token" ]; then
log_err "Could not read the existing Forgejo Runner configuration."
log_err "Uninstall and install the runner again to re-register it."
return
fi
runner_name="${runner_name:-$(hostname)-runner}"
runner_labels="${runner_labels:-ubuntu-latest:docker://catthehacker/ubuntu:act-22.04,ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04}"
log_step "Pulling latest Forgejo Runner image..."
docker pull "${FORGEJO_RUNNER_IMAGE}"
log_ok "Image pulled."
log_step "Recreating Forgejo Runner container..."
docker stop "${FORGEJO_RUNNER_CONTAINER}" 2>/dev/null || true
docker rm "${FORGEJO_RUNNER_CONTAINER}" 2>/dev/null || true
mkdir -p "$FORGEJO_RUNNER_DIR"
docker run -d \
--name "${FORGEJO_RUNNER_CONTAINER}" \
--restart unless-stopped \
-e GITEA_INSTANCE_URL="${forgejo_url}" \
-e GITEA_RUNNER_REGISTRATION_TOKEN="${runner_token}" \
-e GITEA_RUNNER_NAME="${runner_name}" \
-e GITEA_RUNNER_LABELS="${runner_labels}" \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "${FORGEJO_RUNNER_DIR}:/data" \
"${FORGEJO_RUNNER_IMAGE}" > /dev/null
log_ok "Forgejo Runner updated."
}
do_forgejo_runner_uninstall() { do_forgejo_runner_uninstall() {
require_root require_root
@ -889,18 +1148,176 @@ show_forgejo_menu() {
echo " 2) Uninstall Forgejo" echo " 2) Uninstall Forgejo"
echo " 3) Update Forgejo" echo " 3) Update Forgejo"
echo " 4) Install Runner" echo " 4) Install Runner"
echo " 5) Uninstall Runner" echo " 5) Update Runner"
echo " 6) Back" echo " 6) Uninstall Runner"
echo " 7) Back"
separator separator
read -rp " Select [1-6]: " choice read -rp " Select [1-7]: " choice
echo "" echo ""
case "$choice" in case "$choice" in
1) do_forgejo_install ;; 1) do_forgejo_install ;;
2) do_forgejo_uninstall ;; 2) do_forgejo_uninstall ;;
3) do_forgejo_update ;; 3) do_forgejo_update ;;
4) do_forgejo_runner_install ;; 4) do_forgejo_runner_install ;;
5) do_forgejo_runner_uninstall ;; 5) do_forgejo_runner_update ;;
6) return ;; 6) do_forgejo_runner_uninstall ;;
7) return ;;
*) log_err "Invalid option." ;;
esac
done
}
# -----------------------------------------------------------
# Gateway Manager Menu
# -----------------------------------------------------------
show_gateway_menu() {
while true; do
echo ""
separator
echo -e "${BOLD} Gateway Manager${NC}"
separator
echo " 1) Install Gateway"
echo " 2) Uninstall Gateway"
echo " 3) Uninstall Gateway + Docker (Purge)"
echo " 4) Reset Admin Password"
echo " 5) Update Gateway"
echo " 6) Manage Custom Stream Ports"
echo " 7) Toggle Admin Port 81 (Block/Unblock)"
echo " 8) Back"
separator
read -rp " Select [1-8]: " choice
echo ""
case "$choice" in
1) do_install ;;
2) do_uninstall ;;
3) do_purge ;;
4) do_reset_password ;;
5) do_update ;;
6) do_manage_ports ;;
7) do_toggle_port_81 ;;
8) return ;;
*) log_err "Invalid option." ;;
esac
done
}
# -----------------------------------------------------------
# Status / Logs / Health Check
# -----------------------------------------------------------
show_container_status() {
local label="$1"
local name="$2"
separator
echo -e "${BOLD} ${label} Status${NC}"
separator
if docker ps -a --format '{{.Names}}' | grep -q "^${name}$"; then
docker ps -a --filter "name=^${name}$" --format "table {{.Names}}\t{{.Status}}\t{{.Image}}\t{{.Ports}}"
else
log_warn "${label} container is not installed."
fi
}
show_service_logs() {
local label="$1"
local name="$2"
separator
echo -e "${BOLD} ${label} Logs (last 60 lines)${NC}"
separator
if docker ps -a --format '{{.Names}}' | grep -q "^${name}$"; then
docker logs --tail 60 "$name" 2>&1 || true
else
log_warn "${label} container is not installed."
fi
}
run_health_checks() {
separator
echo -e "${BOLD} Health Check Summary${NC}"
separator
log_step "Docker daemon"
if docker info > /dev/null 2>&1; then
log_ok "Docker daemon is running."
else
log_err "Docker daemon is not reachable."
fi
log_step "Shared Docker network (${DOCKER_NETWORK})"
if docker network ls --format '{{.Name}}' | grep -q "^${DOCKER_NETWORK}$"; then
log_ok "Network ${DOCKER_NETWORK} exists."
else
log_warn "Network ${DOCKER_NETWORK} does not exist."
fi
log_step "Gateway container"
if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
log_ok "Gateway is running."
elif docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
log_warn "Gateway exists but is not running."
else
log_warn "Gateway is not installed."
fi
log_step "Blog container"
if docker ps --format '{{.Names}}' | grep -q "^${BLOG_CONTAINER_NAME}$"; then
log_ok "Blog is running."
elif docker ps -a --format '{{.Names}}' | grep -q "^${BLOG_CONTAINER_NAME}$"; then
log_warn "Blog exists but is not running."
else
log_warn "Blog is not installed."
fi
log_step "Forgejo container"
if docker ps --format '{{.Names}}' | grep -q "^${FORGEJO_CONTAINER_NAME}$"; then
log_ok "Forgejo is running."
elif docker ps -a --format '{{.Names}}' | grep -q "^${FORGEJO_CONTAINER_NAME}$"; then
log_warn "Forgejo exists but is not running."
else
log_warn "Forgejo is not installed."
fi
log_step "Forgejo Runner container"
if docker ps --format '{{.Names}}' | grep -q "^${FORGEJO_RUNNER_CONTAINER}$"; then
log_ok "Forgejo Runner is running."
elif docker ps -a --format '{{.Names}}' | grep -q "^${FORGEJO_RUNNER_CONTAINER}$"; then
log_warn "Forgejo Runner exists but is not running."
else
log_warn "Forgejo Runner is not installed."
fi
}
show_operations_menu() {
while true; do
echo ""
separator
echo -e "${BOLD} Status / Logs / Health Check${NC}"
separator
echo " 1) Gateway Status"
echo " 2) Blog Status"
echo " 3) Forgejo Status"
echo " 4) Runner Status"
echo " 5) Gateway Logs"
echo " 6) Blog Logs"
echo " 7) Forgejo Logs"
echo " 8) Runner Logs"
echo " 9) Health Check Summary"
echo " 10) Back"
separator
read -rp " Select [1-10]: " choice
echo ""
case "$choice" in
1) show_container_status "Gateway" "$CONTAINER_NAME" ;;
2) show_container_status "Blog" "$BLOG_CONTAINER_NAME" ;;
3) show_container_status "Forgejo" "$FORGEJO_CONTAINER_NAME" ;;
4) show_container_status "Forgejo Runner" "$FORGEJO_RUNNER_CONTAINER" ;;
5) show_service_logs "Gateway" "$CONTAINER_NAME" ;;
6) show_service_logs "Blog" "$BLOG_CONTAINER_NAME" ;;
7) show_service_logs "Forgejo" "$FORGEJO_CONTAINER_NAME" ;;
8) show_service_logs "Forgejo Runner" "$FORGEJO_RUNNER_CONTAINER" ;;
9) run_health_checks ;;
10) return ;;
*) log_err "Invalid option." ;; *) log_err "Invalid option." ;;
esac esac
done done
@ -913,30 +1330,22 @@ show_menu() {
while true; do while true; do
echo "" echo ""
separator separator
echo -e "${BOLD} D3V-NPMWG Installation Manager${NC}" echo -e "${BOLD} Server Installation Manager${NC}"
separator separator
echo " 1) Install D3V-NPMWG" echo " 1) Gateway"
echo " 2) Uninstall D3V-NPMWG" echo " 2) Blog"
echo " 3) Uninstall D3V-NPMWG + Docker (Purge)" echo " 3) Forgejo"
echo " 4) Reset Admin Password" echo " 4) Status / Logs / Health Check"
echo " 5) Update D3V-NPMWG" echo " 5) Exit"
echo " 6) Manage Custom Stream Ports"
echo " 7) Toggle Admin Port 81 (Block/Unblock)"
echo " 8) Forgejo"
echo " 9) Exit"
separator separator
read -rp " Select [1-9]: " choice read -rp " Select [1-5]: " choice
echo "" echo ""
case "$choice" in case "$choice" in
1) do_install ;; 1) show_gateway_menu ;;
2) do_uninstall ;; 2) show_blog_menu ;;
3) do_purge ;; 3) show_forgejo_menu ;;
4) do_reset_password ;; 4) show_operations_menu ;;
5) do_update ;; 5) echo "Bye!"; exit 0 ;;
6) do_manage_ports ;;
7) do_toggle_port_81 ;;
8) show_forgejo_menu ;;
9) echo "Bye!"; exit 0 ;;
*) log_err "Invalid option." ;; *) log_err "Invalid option." ;;
esac esac
done done
@ -946,14 +1355,26 @@ show_help() {
echo "Usage: $0 [command]" echo "Usage: $0 [command]"
echo "" echo ""
echo "Commands:" echo "Commands:"
echo " install Install D3V-NPMWG and dependencies" echo " install Install Gateway and dependencies"
echo " uninstall Remove D3V-NPMWG (keeps Docker)" echo " uninstall Remove Gateway (keeps Docker)"
echo " purge Remove D3V-NPMWG AND Docker" echo " purge Remove Gateway AND Docker"
echo " reset Reset web admin password" echo " reset Reset web admin password"
echo " update Pull latest image and restart" echo " update Pull latest image and restart"
echo " manage-ports Add or remove custom exposed Stream TCP/UDP ports" echo " manage-ports Add or remove custom exposed Stream TCP/UDP ports"
echo " toggle-port Block or unblock external access to Admin UI (Port 81) using iptables" echo " toggle-port Block or unblock external access to Admin UI (Port 81) using iptables"
echo " gateway Open Gateway submenu"
echo " gateway-install Install Gateway and dependencies"
echo " gateway-uninstall Remove Gateway (keeps Docker)"
echo " gateway-purge Remove Gateway AND Docker"
echo " gateway-reset Reset Gateway admin password"
echo " gateway-update Pull latest Gateway image and restart"
echo " blog Open Blog submenu (install/update/uninstall)"
echo " blog-install Install lightweight static blog hosting for Hugo"
echo " blog-update Update lightweight static blog hosting"
echo " blog-uninstall Remove lightweight static blog hosting"
echo " forgejo Open Forgejo submenu (install/uninstall/update)" echo " forgejo Open Forgejo submenu (install/uninstall/update)"
echo " runner-update Update Forgejo Runner while keeping its configuration"
echo " ops Open Status / Logs / Health Check submenu"
echo " help Show this help" echo " help Show this help"
echo "" echo ""
echo "Run without arguments to open the interactive menu." echo "Run without arguments to open the interactive menu."
@ -968,14 +1389,21 @@ if [ "$#" -eq 0 ]; then
show_menu show_menu
else else
case "$1" in case "$1" in
install) do_install ;; install|gateway-install) do_install ;;
uninstall) do_uninstall ;; uninstall|gateway-uninstall) do_uninstall ;;
purge) do_purge ;; purge|gateway-purge) do_purge ;;
reset) do_reset_password ;; reset|gateway-reset) do_reset_password ;;
update) do_update ;; update|gateway-update) do_update ;;
manage-ports) do_manage_ports ;; manage-ports) do_manage_ports ;;
toggle-port) do_toggle_port_81 ;; toggle-port) do_toggle_port_81 ;;
gateway) show_gateway_menu ;;
blog) show_blog_menu ;;
blog-install) do_blog_install ;;
blog-update) do_blog_update ;;
blog-uninstall) do_blog_uninstall ;;
forgejo) show_forgejo_menu ;; forgejo) show_forgejo_menu ;;
runner-update) do_forgejo_runner_update ;;
ops) show_operations_menu ;;
help|-h|--help) show_help ;; help|-h|--help) show_help ;;
*) *)
log_err "Unknown command: $1" log_err "Unknown command: $1"