mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 02:15:48 +02:00
feat(devcontainer): multi node
This commit is contained in:
parent
c85a570396
commit
b090564a34
14 changed files with 150 additions and 49 deletions
|
@ -13,15 +13,7 @@ RUN apt-get update && \
|
||||||
\
|
\
|
||||||
# Update package information and install Nginx
|
# Update package information and install Nginx
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y --no-install-recommends nginx && \
|
apt-get install -y --no-install-recommends nginx inotify-tools file && \
|
||||||
\
|
|
||||||
# Install the latest Node.js via NodeSource setup script
|
|
||||||
curl -fsSL https://deb.nodesource.com/setup_current.x | bash - && \
|
|
||||||
apt-get update && \
|
|
||||||
apt-get install -y nodejs && \
|
|
||||||
\
|
|
||||||
# Install pnpm globally using npm
|
|
||||||
npm install -g pnpm && \
|
|
||||||
\
|
\
|
||||||
# Automatically retrieve the latest stable Go version and install it,
|
# Automatically retrieve the latest stable Go version and install it,
|
||||||
# download the appropriate binary based on system architecture (amd64 or arm64)
|
# download the appropriate binary based on system architecture (amd64 or arm64)
|
||||||
|
|
|
@ -10,7 +10,8 @@
|
||||||
"features": {
|
"features": {
|
||||||
"ghcr.io/devcontainers/features/common-utils:2": {
|
"ghcr.io/devcontainers/features/common-utils:2": {
|
||||||
"installOhMyZsh": true
|
"installOhMyZsh": true
|
||||||
}
|
},
|
||||||
|
"ghcr.io/devcontainers/features/node:1": {}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
|
@ -27,7 +28,8 @@
|
||||||
"antfu.unocss",
|
"antfu.unocss",
|
||||||
"github.copilot",
|
"github.copilot",
|
||||||
"golang.go",
|
"golang.go",
|
||||||
"vue.volar"
|
"vue.volar",
|
||||||
|
"ms-azuretools.vscode-docker"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,15 +1,31 @@
|
||||||
services:
|
services:
|
||||||
nginx-ui:
|
nginx-ui:
|
||||||
build: .
|
build: .
|
||||||
|
image: nginx-ui-dev
|
||||||
|
container_name: nginx-ui
|
||||||
volumes:
|
volumes:
|
||||||
- ../..:/workspaces:cached
|
- ../..:/workspaces:cached
|
||||||
- ./go-path:/root/go
|
- ./go-path:/root/go
|
||||||
- ./nginx:/etc/nginx
|
- ./data/nginx:/etc/nginx
|
||||||
command: sleep infinity
|
command: sleep infinity
|
||||||
environment:
|
environment:
|
||||||
- NGINX_UI_CERT_CA_DIR=https://pebble:14000/dir
|
- NGINX_UI_CERT_CA_DIR=https://pebble:14000/dir
|
||||||
networks:
|
networks:
|
||||||
nginxui:
|
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:
|
pebble:
|
||||||
image: ghcr.io/letsencrypt/pebble:latest
|
image: ghcr.io/letsencrypt/pebble:latest
|
||||||
volumes:
|
volumes:
|
||||||
|
|
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
|
|
@ -9,11 +9,7 @@ if ! grep -q "zsh-autosuggestions" ~/.zshrc; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# init nginx config dir
|
# init nginx config dir
|
||||||
if [ "$(ls -A /etc/nginx)" = "" ]; then
|
./.devcontainer/init-nginx.sh
|
||||||
echo "Initialing Nginx config dir"
|
|
||||||
cp -rp /etc/nginx.orig/* /etc/nginx/
|
|
||||||
echo "Initialed Nginx config dir"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# install app dependencies
|
# install app dependencies
|
||||||
echo "Installing app dependencies"
|
echo "Installing app dependencies"
|
||||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -15,5 +15,5 @@ app/.status_hash
|
||||||
.idea/deployment.xml
|
.idea/deployment.xml
|
||||||
.idea/webServers.xml
|
.idea/webServers.xml
|
||||||
.devcontainer/go-path
|
.devcontainer/go-path
|
||||||
.devcontainer/nginx
|
.devcontainer/data
|
||||||
.devcontainer/casdoor.pem
|
.devcontainer/casdoor.pem
|
||||||
|
|
|
@ -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]) {
|
||||||
|
go analytic.RestartRetrieveNodesStatus()
|
||||||
env, err := envQuery.FirstByID(id)
|
}).Destroy()
|
||||||
if err != nil {
|
|
||||||
api.ErrHandler(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = envQuery.DeleteByID(env.ID)
|
|
||||||
if err != nil {
|
|
||||||
api.ErrHandler(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
go analytic.RestartRetrieveNodesStatus()
|
|
||||||
|
|
||||||
c.JSON(http.StatusNoContent, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadEnvironmentFromSettings(c *gin.Context) {
|
func LoadEnvironmentFromSettings(c *gin.Context) {
|
||||||
|
|
|
@ -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',
|
||||||
|
|
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) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue