Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ services:
- ../..:/workspaces:cached
- ./go-path:/root/go
- ./data/nginx:/etc/nginx
- /var/run/docker.sock:/var/run/docker.sock
command: sleep infinity
environment:
- NGINX_UI_CERT_CA_DIR=https://pebble:14000/dir
Expand All @@ -25,6 +26,28 @@ services:
- nginx-ui
networks:
nginxui:
nginx-ui-3:
image: nginx-ui-dev
container_name: nginx-ui-3
volumes:
- ../..:/workspaces:cached
- ./data/nginx-ui-3/nginx:/etc/nginx
- ./data/nginx-ui-3/nginx-ui:/etc/nginx-ui
- /var/run/docker.sock:/var/run/docker.sock
working_dir: /workspaces/nginx-ui
command: ./.devcontainer/node-supervisor.sh
depends_on:
- nginx-ui
networks:
nginxui:
nginx:
image: nginx-ui-dev
container_name: nginx
volumes:
- ./data/nginx-ui-3/nginx:/etc/nginx
command: sleep infinity
networks:
nginxui:
pebble:
image: ghcr.io/letsencrypt/pebble:latest
volumes:
Expand Down
2 changes: 1 addition & 1 deletion .devcontainer/init-nginx.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ if [ "$(ls -A /etc/nginx)" = "" ]; then
fi

# start nginx
nginx -g "daemon off;"
nginx
2 changes: 1 addition & 1 deletion .devcontainer/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# install air
go install github.com/air-verse/air@latest

# install zsh-autosuggestions
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
Expand Down
7 changes: 6 additions & 1 deletion api/config/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,12 @@ func AddConfig(c *gin.Context) {
return
}

output := nginx.Reload()
output, err := nginx.Reload()
if err != nil {
cosy.ErrHandler(c, err)
return
}

if nginx.GetLogLevel(output) >= nginx.Warn {
cosy.ErrHandler(c, cosy.WrapErrorWithParams(config.ErrNginxReloadFailed, output))
return
Expand Down
25 changes: 21 additions & 4 deletions api/nginx/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,50 @@ import (

"github.com/0xJacky/Nginx-UI/internal/nginx"
"github.com/gin-gonic/gin"
"github.com/uozi-tech/cosy"
)

// Reload reloads the nginx
func Reload(c *gin.Context) {
output := nginx.Reload()
output, err := nginx.Reload()
if err != nil {
cosy.ErrHandler(c, err)
return
}
c.JSON(http.StatusOK, gin.H{
"message": output,
"level": nginx.GetLogLevel(output),
})
}

func Test(c *gin.Context) {
output := nginx.TestConf()
// TestConfig tests the nginx config
func TestConfig(c *gin.Context) {
output, err := nginx.TestConfig()
if err != nil {
cosy.ErrHandler(c, err)
return
}
c.JSON(http.StatusOK, gin.H{
"message": output,
"level": nginx.GetLogLevel(output),
})
}

// Restart restarts the nginx
func Restart(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "ok",
})
go nginx.Restart()
}

// Status returns the status of the nginx
func Status(c *gin.Context) {
lastOutput := nginx.GetLastOutput()
lastOutput, err := nginx.GetLastOutput()
if err != nil {
cosy.ErrHandler(c, err)
return
}

running := nginx.IsNginxRunning()

Expand Down
2 changes: 1 addition & 1 deletion api/nginx/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ func InitRouter(r *gin.RouterGroup) {
r.POST("ngx/format_code", FormatNginxConfig)
r.POST("nginx/reload", Reload)
r.POST("nginx/restart", Restart)
r.POST("nginx/test", Test)
r.POST("nginx/test", TestConfig)
r.GET("nginx/status", Status)
// Get detailed Nginx status information, including connection count, process information, etc. (Issue #850)
r.GET("nginx/detail_status", GetDetailStatus)
Expand Down
9 changes: 6 additions & 3 deletions api/nginx/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package nginx

import (
"errors"
"net/http"
"strings"
"time"
Expand Down Expand Up @@ -119,10 +118,14 @@ func ToggleStubStatus(c *gin.Context) {
}

// Reload Nginx configuration
reloadOutput := nginx.Reload()
reloadOutput, err := nginx.Reload()
if err != nil {
cosy.ErrHandler(c, err)
return
}
if len(reloadOutput) > 0 && (strings.Contains(strings.ToLower(reloadOutput), "error") ||
strings.Contains(strings.ToLower(reloadOutput), "failed")) {
cosy.ErrHandler(c, errors.New("Reload Nginx failed"))
cosy.ErrHandler(c, cosy.WrapErrorWithParams(nginx.ErrReloadFailed, reloadOutput))
return
}

Expand Down
1 change: 1 addition & 0 deletions api/settings/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func GetSettings(c *gin.Context) {
settings.NginxSettings.ErrorLogPath = nginx.GetErrorLogPath()
settings.NginxSettings.ConfigDir = nginx.GetConfPath()
settings.NginxSettings.PIDPath = nginx.GetPIDPath()
settings.NginxSettings.StubStatusPort = settings.NginxSettings.GetStubStatusPort()

if settings.NginxSettings.ReloadCmd == "" {
settings.NginxSettings.ReloadCmd = "nginx -s reload"
Expand Down
1 change: 1 addition & 0 deletions app/src/api/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export interface NginxSettings {
reload_cmd: string
restart_cmd: string
stub_status_port: number
container_name: string
}

export interface NodeSettings {
Expand Down
3 changes: 3 additions & 0 deletions app/src/components/Breadcrumb/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Breadcrumb from './Breadcrumb.vue'

export default Breadcrumb
65 changes: 62 additions & 3 deletions app/src/components/CodeEditor/CodeCompletion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,39 @@ function debug(...args: any[]) {
}
}

// Config file patterns and extensions
const CONFIG_FILE_EXTENSIONS = ['.conf', '.config']
const SENSITIVE_CONTENT_PATTERNS = [
/-----BEGIN [A-Z ]+ PRIVATE KEY-----/,
/-----BEGIN CERTIFICATE-----/,
/apiKey\s*[:=]\s*["'][a-zA-Z0-9]+["']/,
/password\s*[:=]\s*["'][^"']+["']/,
/secret\s*[:=]\s*["'][^"']+["']/,
]

function useCodeCompletion() {
const editorRef = ref<Editor>()
const currentGhostText = ref<string>('')
const isConfigFile = ref<boolean>(false)

const ws = openai.code_completion()

// Check if the current file is a configuration file
function checkIfConfigFile(filename: string, content: string): boolean {
// Check file extension
const hasConfigExtension = CONFIG_FILE_EXTENSIONS.some(ext => filename.toLowerCase().endsWith(ext))

// Check if it's an Nginx configuration file based on common patterns
const hasNginxPatterns = /server\s*\{|location\s*\/|http\s*\{|upstream\s*[\w-]+\s*\{/.test(content)

return hasConfigExtension || hasNginxPatterns
}

// Check if content contains sensitive information that shouldn't be sent
function containsSensitiveContent(content: string): boolean {
return SENSITIVE_CONTENT_PATTERNS.some(pattern => pattern.test(content))
}

function getAISuggestions(code: string, context: string, position: Point, callback: (suggestion: string) => void, language: string = 'nginx', suffix: string = '', requestId: string) {
if (!ws || ws.readyState !== WebSocket.OPEN) {
debug('WebSocket is not open')
Expand All @@ -29,6 +56,17 @@ function useCodeCompletion() {
return
}

// Skip if not a config file or contains sensitive content
if (!isConfigFile.value) {
debug('Skipping AI suggestions for non-config file')
return
}

if (containsSensitiveContent(context)) {
debug('Skipping AI suggestions due to sensitive content')
return
}

const message = {
context,
code,
Expand Down Expand Up @@ -57,8 +95,20 @@ function useCodeCompletion() {
return
}

if (!isConfigFile.value) {
debug('Skipping ghost text for non-config file')
return
}

try {
const currentText = editorRef.value.getValue()

// Skip if content contains sensitive information
if (containsSensitiveContent(currentText)) {
debug('Skipping ghost text due to sensitive content')
return
}

const cursorPosition = editorRef.value.getCursorPosition()

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

debug('Editor initialized')

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

editorRef.value = editor

// Determine if the current file is a configuration file
const content = editor.getValue()
isConfigFile.value = checkIfConfigFile(filename, content)
debug(`File type check: isConfigFile=${isConfigFile.value}, filename=${filename}`)

// Set up Tab key handler
setupTabHandler(editor)

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

if (e.action === 'insert' || e.action === 'remove') {
// Clear current ghost text
debouncedApplyGhostText()
if (isConfigFile.value) {
debouncedApplyGhostText()
}
}
})

// Listen for cursor changes, using debounce
editor.selection.on('changeCursor', () => {
debug('Cursor changed')
clearGhostText()
debouncedApplyGhostText()
if (isConfigFile.value) {
debouncedApplyGhostText()
}
})
}, 2000)
}
Expand Down
3 changes: 3 additions & 0 deletions app/src/components/EnvGroupTabs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import EnvGroupTabs from './EnvGroupTabs.vue'

export default EnvGroupTabs
3 changes: 3 additions & 0 deletions app/src/components/EnvIndicator/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import EnvIndicator from './EnvIndicator.vue'

export default EnvIndicator
3 changes: 3 additions & 0 deletions app/src/components/ICP/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ICP from './ICP.vue'

export default ICP
3 changes: 3 additions & 0 deletions app/src/components/Logo/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Logo from './Logo.vue'

export default Logo
3 changes: 3 additions & 0 deletions app/src/components/NginxControl/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import NginxControl from './NginxControl.vue'

export default NginxControl
3 changes: 3 additions & 0 deletions app/src/components/NodeSelector/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import NodeSelector from './NodeSelector.vue'

export default NodeSelector
3 changes: 3 additions & 0 deletions app/src/components/Notification/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Notification from './Notification.vue'

export default Notification
3 changes: 3 additions & 0 deletions app/src/components/OTPInput/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import OTPInput from './OTPInput.vue'

export default OTPInput
3 changes: 3 additions & 0 deletions app/src/components/PageHeader/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import PageHeader from './PageHeader.vue'

export default PageHeader
3 changes: 3 additions & 0 deletions app/src/components/ReactiveFromNow/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ReactiveFromNow from './ReactiveFromNow.vue'

export default ReactiveFromNow
3 changes: 3 additions & 0 deletions app/src/components/SensitiveString/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import SensitiveString from './SensitiveString.vue'

export default SensitiveString
3 changes: 3 additions & 0 deletions app/src/components/SetLanguage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import SetLanguage from './SetLanguage.vue'

export default SetLanguage
2 changes: 1 addition & 1 deletion app/src/components/SwitchAppearance/SwitchAppearance.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts" setup>
import type { Ref } from 'vue'
import VPSwitch from '@/components/VPSwitch/VPSwitch.vue'
import VPSwitch from '@/components/VPSwitch'
import { useSettingsStore } from '@/pinia'
import VPIconMoon from './icons/VPIconMoon.vue'
import VPIconSun from './icons/VPIconSun.vue'
Expand Down
3 changes: 3 additions & 0 deletions app/src/components/SwitchAppearance/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import SwitchAppearance from './SwitchAppearance.vue'

export default SwitchAppearance
3 changes: 3 additions & 0 deletions app/src/components/SystemRestore/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import SystemRestoreContainer from './SystemRestoreContent.vue'

export default SystemRestoreContainer
8 changes: 8 additions & 0 deletions app/src/components/TwoFA/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Authorization from './Authorization.vue'
import use2FAModal from './use2FAModal'

export default Authorization

export {
use2FAModal,
}
3 changes: 3 additions & 0 deletions app/src/components/VPSwitch/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import VPSwitch from './VPSwitch.vue'

export default VPSwitch
15 changes: 15 additions & 0 deletions app/src/constants/errors/docker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export default {
500001: () => $gettext('Docker client not initialized'),
500002: () => $gettext('Failed to exec command: {0}'),
500003: () => $gettext('Failed to attach to exec instance: {0}'),
500004: () => $gettext('Failed to read output: {0}'),
500005: () => $gettext('Command exited with unexpected exit code: {0}, error: {1}'),
500006: () => $gettext('Container status unknown'),
500007: () => $gettext('Failed to inspect container: {0}'),
500008: () => $gettext('Nginx is not running in another container'),
500009: () => $gettext('Failed to get hostname: {0}'),
500010: () => $gettext('Failed to pull image: {0}'),
500011: () => $gettext('Failed to inspect current container: {0}'),
500012: () => $gettext('Failed to create temp container: {0}'),
500013: () => $gettext('Failed to start temp container: {0}'),
}
1 change: 1 addition & 0 deletions app/src/constants/errors/nginx.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export default {
50001: () => $gettext('Block is nil'),
50002: () => $gettext('Reload nginx failed: {0}'),
}
Loading