Skip to content

Commit ea90da4

Browse files
authored
Merge pull request #977 from 0xJacky/feat/docker-ui-only
feat: manage nginx in another docker container
2 parents c62fd25 + 4aa42da commit ea90da4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+2363
-572
lines changed

.devcontainer/docker-compose.yml

+23
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ services:
77
- ../..:/workspaces:cached
88
- ./go-path:/root/go
99
- ./data/nginx:/etc/nginx
10+
- /var/run/docker.sock:/var/run/docker.sock
1011
command: sleep infinity
1112
environment:
1213
- NGINX_UI_CERT_CA_DIR=https://pebble:14000/dir
@@ -25,6 +26,28 @@ services:
2526
- nginx-ui
2627
networks:
2728
nginxui:
29+
nginx-ui-3:
30+
image: nginx-ui-dev
31+
container_name: nginx-ui-3
32+
volumes:
33+
- ../..:/workspaces:cached
34+
- ./data/nginx-ui-3/nginx:/etc/nginx
35+
- ./data/nginx-ui-3/nginx-ui:/etc/nginx-ui
36+
- /var/run/docker.sock:/var/run/docker.sock
37+
working_dir: /workspaces/nginx-ui
38+
command: ./.devcontainer/node-supervisor.sh
39+
depends_on:
40+
- nginx-ui
41+
networks:
42+
nginxui:
43+
nginx:
44+
image: nginx-ui-dev
45+
container_name: nginx
46+
volumes:
47+
- ./data/nginx-ui-3/nginx:/etc/nginx
48+
command: sleep infinity
49+
networks:
50+
nginxui:
2851
pebble:
2952
image: ghcr.io/letsencrypt/pebble:latest
3053
volumes:

.devcontainer/init-nginx.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ if [ "$(ls -A /etc/nginx)" = "" ]; then
66
fi
77

88
# start nginx
9-
nginx -g "daemon off;"
9+
nginx

.devcontainer/start.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# install air
44
go install github.com/air-verse/air@latest
55

6-
# install zsh-autosuggestions
6+
install zsh-autosuggestions
77
git clone https://github.com/zsh-users/zsh-autosuggestions ~/.oh-my-zsh/custom/plugins/zsh-autosuggestions
88

99
if ! grep -q "zsh-autosuggestions" ~/.zshrc; then

api/config/add.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,12 @@ func AddConfig(c *gin.Context) {
7474
return
7575
}
7676

77-
output := nginx.Reload()
77+
output, err := nginx.Reload()
78+
if err != nil {
79+
cosy.ErrHandler(c, err)
80+
return
81+
}
82+
7883
if nginx.GetLogLevel(output) >= nginx.Warn {
7984
cosy.ErrHandler(c, cosy.WrapErrorWithParams(config.ErrNginxReloadFailed, output))
8085
return

api/nginx/control.go

+21-4
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,50 @@ import (
55

66
"github.com/0xJacky/Nginx-UI/internal/nginx"
77
"github.com/gin-gonic/gin"
8+
"github.com/uozi-tech/cosy"
89
)
910

11+
// Reload reloads the nginx
1012
func Reload(c *gin.Context) {
11-
output := nginx.Reload()
13+
output, err := nginx.Reload()
14+
if err != nil {
15+
cosy.ErrHandler(c, err)
16+
return
17+
}
1218
c.JSON(http.StatusOK, gin.H{
1319
"message": output,
1420
"level": nginx.GetLogLevel(output),
1521
})
1622
}
1723

18-
func Test(c *gin.Context) {
19-
output := nginx.TestConf()
24+
// TestConfig tests the nginx config
25+
func TestConfig(c *gin.Context) {
26+
output, err := nginx.TestConfig()
27+
if err != nil {
28+
cosy.ErrHandler(c, err)
29+
return
30+
}
2031
c.JSON(http.StatusOK, gin.H{
2132
"message": output,
2233
"level": nginx.GetLogLevel(output),
2334
})
2435
}
2536

37+
// Restart restarts the nginx
2638
func Restart(c *gin.Context) {
2739
c.JSON(http.StatusOK, gin.H{
2840
"message": "ok",
2941
})
3042
go nginx.Restart()
3143
}
3244

45+
// Status returns the status of the nginx
3346
func Status(c *gin.Context) {
34-
lastOutput := nginx.GetLastOutput()
47+
lastOutput, err := nginx.GetLastOutput()
48+
if err != nil {
49+
cosy.ErrHandler(c, err)
50+
return
51+
}
3552

3653
running := nginx.IsNginxRunning()
3754

api/nginx/router.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ func InitRouter(r *gin.RouterGroup) {
1111
r.POST("ngx/format_code", FormatNginxConfig)
1212
r.POST("nginx/reload", Reload)
1313
r.POST("nginx/restart", Restart)
14-
r.POST("nginx/test", Test)
14+
r.POST("nginx/test", TestConfig)
1515
r.GET("nginx/status", Status)
1616
// Get detailed Nginx status information, including connection count, process information, etc. (Issue #850)
1717
r.GET("nginx/detail_status", GetDetailStatus)

api/nginx/status.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
package nginx
55

66
import (
7-
"errors"
87
"net/http"
98
"strings"
109
"time"
@@ -119,10 +118,14 @@ func ToggleStubStatus(c *gin.Context) {
119118
}
120119

121120
// Reload Nginx configuration
122-
reloadOutput := nginx.Reload()
121+
reloadOutput, err := nginx.Reload()
122+
if err != nil {
123+
cosy.ErrHandler(c, err)
124+
return
125+
}
123126
if len(reloadOutput) > 0 && (strings.Contains(strings.ToLower(reloadOutput), "error") ||
124127
strings.Contains(strings.ToLower(reloadOutput), "failed")) {
125-
cosy.ErrHandler(c, errors.New("Reload Nginx failed"))
128+
cosy.ErrHandler(c, cosy.WrapErrorWithParams(nginx.ErrReloadFailed, reloadOutput))
126129
return
127130
}
128131

api/settings/settings.go

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ func GetSettings(c *gin.Context) {
2525
settings.NginxSettings.ErrorLogPath = nginx.GetErrorLogPath()
2626
settings.NginxSettings.ConfigDir = nginx.GetConfPath()
2727
settings.NginxSettings.PIDPath = nginx.GetPIDPath()
28+
settings.NginxSettings.StubStatusPort = settings.NginxSettings.GetStubStatusPort()
2829

2930
if settings.NginxSettings.ReloadCmd == "" {
3031
settings.NginxSettings.ReloadCmd = "nginx -s reload"

app/src/api/settings.ts

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export interface NginxSettings {
6464
reload_cmd: string
6565
restart_cmd: string
6666
stub_status_port: number
67+
container_name: string
6768
}
6869

6970
export interface NodeSettings {
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import Breadcrumb from './Breadcrumb.vue'
2+
3+
export default Breadcrumb

app/src/components/CodeEditor/CodeCompletion.ts

+62-3
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,39 @@ function debug(...args: any[]) {
1212
}
1313
}
1414

15+
// Config file patterns and extensions
16+
const CONFIG_FILE_EXTENSIONS = ['.conf', '.config']
17+
const SENSITIVE_CONTENT_PATTERNS = [
18+
/-----BEGIN [A-Z ]+ PRIVATE KEY-----/,
19+
/-----BEGIN CERTIFICATE-----/,
20+
/apiKey\s*[:=]\s*["'][a-zA-Z0-9]+["']/,
21+
/password\s*[:=]\s*["'][^"']+["']/,
22+
/secret\s*[:=]\s*["'][^"']+["']/,
23+
]
24+
1525
function useCodeCompletion() {
1626
const editorRef = ref<Editor>()
1727
const currentGhostText = ref<string>('')
28+
const isConfigFile = ref<boolean>(false)
1829

1930
const ws = openai.code_completion()
2031

32+
// Check if the current file is a configuration file
33+
function checkIfConfigFile(filename: string, content: string): boolean {
34+
// Check file extension
35+
const hasConfigExtension = CONFIG_FILE_EXTENSIONS.some(ext => filename.toLowerCase().endsWith(ext))
36+
37+
// Check if it's an Nginx configuration file based on common patterns
38+
const hasNginxPatterns = /server\s*\{|location\s*\/|http\s*\{|upstream\s*[\w-]+\s*\{/.test(content)
39+
40+
return hasConfigExtension || hasNginxPatterns
41+
}
42+
43+
// Check if content contains sensitive information that shouldn't be sent
44+
function containsSensitiveContent(content: string): boolean {
45+
return SENSITIVE_CONTENT_PATTERNS.some(pattern => pattern.test(content))
46+
}
47+
2148
function getAISuggestions(code: string, context: string, position: Point, callback: (suggestion: string) => void, language: string = 'nginx', suffix: string = '', requestId: string) {
2249
if (!ws || ws.readyState !== WebSocket.OPEN) {
2350
debug('WebSocket is not open')
@@ -29,6 +56,17 @@ function useCodeCompletion() {
2956
return
3057
}
3158

59+
// Skip if not a config file or contains sensitive content
60+
if (!isConfigFile.value) {
61+
debug('Skipping AI suggestions for non-config file')
62+
return
63+
}
64+
65+
if (containsSensitiveContent(context)) {
66+
debug('Skipping AI suggestions due to sensitive content')
67+
return
68+
}
69+
3270
const message = {
3371
context,
3472
code,
@@ -57,8 +95,20 @@ function useCodeCompletion() {
5795
return
5896
}
5997

98+
if (!isConfigFile.value) {
99+
debug('Skipping ghost text for non-config file')
100+
return
101+
}
102+
60103
try {
61104
const currentText = editorRef.value.getValue()
105+
106+
// Skip if content contains sensitive information
107+
if (containsSensitiveContent(currentText)) {
108+
debug('Skipping ghost text due to sensitive content')
109+
return
110+
}
111+
62112
const cursorPosition = editorRef.value.getCursorPosition()
63113

64114
// Get all text before the current cursor position as the code part for the request
@@ -175,7 +225,7 @@ function useCodeCompletion() {
175225

176226
debug('Editor initialized')
177227

178-
async function init(editor: Editor) {
228+
async function init(editor: Editor, filename: string = '') {
179229
const { enabled } = await openai.get_code_completion_enabled_status()
180230
if (!enabled) {
181231
debug('Code completion is not enabled')
@@ -184,6 +234,11 @@ function useCodeCompletion() {
184234

185235
editorRef.value = editor
186236

237+
// Determine if the current file is a configuration file
238+
const content = editor.getValue()
239+
isConfigFile.value = checkIfConfigFile(filename, content)
240+
debug(`File type check: isConfigFile=${isConfigFile.value}, filename=${filename}`)
241+
187242
// Set up Tab key handler
188243
setupTabHandler(editor)
189244

@@ -195,15 +250,19 @@ function useCodeCompletion() {
195250

196251
if (e.action === 'insert' || e.action === 'remove') {
197252
// Clear current ghost text
198-
debouncedApplyGhostText()
253+
if (isConfigFile.value) {
254+
debouncedApplyGhostText()
255+
}
199256
}
200257
})
201258

202259
// Listen for cursor changes, using debounce
203260
editor.selection.on('changeCursor', () => {
204261
debug('Cursor changed')
205262
clearGhostText()
206-
debouncedApplyGhostText()
263+
if (isConfigFile.value) {
264+
debouncedApplyGhostText()
265+
}
207266
})
208267
}, 2000)
209268
}
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import EnvGroupTabs from './EnvGroupTabs.vue'
2+
3+
export default EnvGroupTabs
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import EnvIndicator from './EnvIndicator.vue'
2+
3+
export default EnvIndicator

app/src/components/ICP/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import ICP from './ICP.vue'
2+
3+
export default ICP

app/src/components/Logo/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import Logo from './Logo.vue'
2+
3+
export default Logo
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import NginxControl from './NginxControl.vue'
2+
3+
export default NginxControl
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import NodeSelector from './NodeSelector.vue'
2+
3+
export default NodeSelector
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import Notification from './Notification.vue'
2+
3+
export default Notification

app/src/components/OTPInput/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import OTPInput from './OTPInput.vue'
2+
3+
export default OTPInput
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import PageHeader from './PageHeader.vue'
2+
3+
export default PageHeader
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import ReactiveFromNow from './ReactiveFromNow.vue'
2+
3+
export default ReactiveFromNow
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import SensitiveString from './SensitiveString.vue'
2+
3+
export default SensitiveString
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import SetLanguage from './SetLanguage.vue'
2+
3+
export default SetLanguage

app/src/components/SwitchAppearance/SwitchAppearance.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script lang="ts" setup>
22
import type { Ref } from 'vue'
3-
import VPSwitch from '@/components/VPSwitch/VPSwitch.vue'
3+
import VPSwitch from '@/components/VPSwitch'
44
import { useSettingsStore } from '@/pinia'
55
import VPIconMoon from './icons/VPIconMoon.vue'
66
import VPIconSun from './icons/VPIconSun.vue'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import SwitchAppearance from './SwitchAppearance.vue'
2+
3+
export default SwitchAppearance
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import SystemRestoreContainer from './SystemRestoreContent.vue'
2+
3+
export default SystemRestoreContainer

app/src/components/TwoFA/index.ts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import Authorization from './Authorization.vue'
2+
import use2FAModal from './use2FAModal'
3+
4+
export default Authorization
5+
6+
export {
7+
use2FAModal,
8+
}

app/src/components/VPSwitch/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import VPSwitch from './VPSwitch.vue'
2+
3+
export default VPSwitch

app/src/constants/errors/docker.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export default {
2+
500001: () => $gettext('Docker client not initialized'),
3+
500002: () => $gettext('Failed to exec command: {0}'),
4+
500003: () => $gettext('Failed to attach to exec instance: {0}'),
5+
500004: () => $gettext('Failed to read output: {0}'),
6+
500005: () => $gettext('Command exited with unexpected exit code: {0}, error: {1}'),
7+
500006: () => $gettext('Container status unknown'),
8+
500007: () => $gettext('Failed to inspect container: {0}'),
9+
500008: () => $gettext('Nginx is not running in another container'),
10+
500009: () => $gettext('Failed to get hostname: {0}'),
11+
500010: () => $gettext('Failed to pull image: {0}'),
12+
500011: () => $gettext('Failed to inspect current container: {0}'),
13+
500012: () => $gettext('Failed to create temp container: {0}'),
14+
500013: () => $gettext('Failed to start temp container: {0}'),
15+
}

app/src/constants/errors/nginx.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export default {
22
50001: () => $gettext('Block is nil'),
3+
50002: () => $gettext('Reload nginx failed: {0}'),
34
}

0 commit comments

Comments
 (0)