feat: upstream health check #166

This commit is contained in:
0xJacky 2023-12-15 20:18:29 +08:00
parent b2854d4965
commit d9d8bee5d0
No known key found for this signature in database
GPG key ID: B6E4A6E4A561BAF0
14 changed files with 275 additions and 292 deletions

7
api/upstream/router.go Normal file
View file

@ -0,0 +1,7 @@
package upstream
import "github.com/gin-gonic/gin"
func InitRouter(r *gin.RouterGroup) {
r.GET("/availability_test", AvailabilityTest)
}

45
api/upstream/upstream.go Normal file
View file

@ -0,0 +1,45 @@
package upstream
import (
"github.com/0xJacky/Nginx-UI/internal/logger"
"github.com/0xJacky/Nginx-UI/internal/upstream"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"net/http"
"time"
)
func AvailabilityTest(c *gin.Context) {
var upGrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// upgrade http to websocket
ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
logger.Error(err)
return
}
defer ws.Close()
var body []string
err = ws.ReadJSON(&body)
if err != nil {
logger.Error(err)
return
}
for {
err = ws.WriteJSON(upstream.AvailabilityTest(body))
if err != nil {
logger.Error(err)
}
time.Sleep(10 * time.Second)
}
}

View file

@ -34,7 +34,7 @@
"vue-router": "^4.2.5",
"vue3-ace-editor": "2.2.4",
"vue3-apexcharts": "^1.4.4",
"vue3-gettext": "3.0.0-beta.2",
"vue3-gettext": "^3.0.0-beta.3",
"vuedraggable": "^4.1.0",
"xterm": "^5.3.0",
"xterm-addon-attach": "^0.9.0",
@ -67,8 +67,7 @@
"unplugin-auto-import": "^0.17.1",
"unplugin-vue-components": "^0.25.2",
"unplugin-vue-define-options": "^1.4.0",
"vite": "^5.0.8",
"vite-plugin-html": "^3.2.0",
"vite": "^5.0.9",
"vite-svg-loader": "^5.1.0",
"vue-tsc": "^1.8.22"
}

297
app/pnpm-lock.yaml generated
View file

@ -75,8 +75,8 @@ dependencies:
specifier: ^1.4.4
version: 1.4.4(apexcharts@3.44.0)(vue@3.3.11)
vue3-gettext:
specifier: 3.0.0-beta.2
version: 3.0.0-beta.2(@vue/compiler-sfc@3.3.10)(vue@3.3.11)
specifier: ^3.0.0-beta.3
version: 3.0.0-beta.3(@vue/compiler-sfc@3.3.10)(vue@3.3.11)
vuedraggable:
specifier: ^4.1.0
version: 4.1.0(vue@3.3.11)
@ -111,10 +111,10 @@ devDependencies:
version: 6.13.1(eslint@8.55.0)(typescript@5.3.2)
'@vitejs/plugin-vue':
specifier: ^4.5.0
version: 4.5.1(vite@5.0.8)(vue@3.3.11)
version: 4.5.1(vite@5.0.9)(vue@3.3.11)
'@vitejs/plugin-vue-jsx':
specifier: ^3.1.0
version: 3.1.0(vite@5.0.8)(vue@3.3.11)
version: 3.1.0(vite@5.0.9)(vue@3.3.11)
'@vue/compiler-sfc':
specifier: ^3.3.10
version: 3.3.10
@ -170,11 +170,8 @@ devDependencies:
specifier: ^1.4.0
version: 1.4.0(vue@3.3.11)
vite:
specifier: ^5.0.8
version: 5.0.8(@types/node@20.10.2)(less@4.2.0)
vite-plugin-html:
specifier: ^3.2.0
version: 3.2.0(vite@5.0.8)
specifier: ^5.0.9
version: 5.0.9(@types/node@20.10.2)(less@4.2.0)
vite-svg-loader:
specifier: ^5.1.0
version: 5.1.0(vue@3.3.11)
@ -875,13 +872,6 @@ packages:
engines: {node: '>=6.0.0'}
dev: true
/@jridgewell/source-map@0.3.5:
resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==}
dependencies:
'@jridgewell/gen-mapping': 0.3.3
'@jridgewell/trace-mapping': 0.3.20
dev: true
/@jridgewell/sourcemap-codec@1.4.15:
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
@ -920,14 +910,6 @@ packages:
dev: false
optional: true
/@rollup/pluginutils@4.2.1:
resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
engines: {node: '>= 8.0.0'}
dependencies:
estree-walker: 2.0.2
picomatch: 2.3.1
dev: true
/@rollup/pluginutils@5.1.0:
resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
engines: {node: '>=14.0.0'}
@ -1332,7 +1314,7 @@ packages:
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
dev: true
/@vitejs/plugin-vue-jsx@3.1.0(vite@5.0.8)(vue@3.3.11):
/@vitejs/plugin-vue-jsx@3.1.0(vite@5.0.9)(vue@3.3.11):
resolution: {integrity: sha512-w9M6F3LSEU5kszVb9An2/MmXNxocAnUb3WhRr8bHlimhDrXNt6n6D2nJQR3UXpGlZHh/EsgouOHCsM8V3Ln+WA==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
@ -1342,20 +1324,20 @@ packages:
'@babel/core': 7.23.5
'@babel/plugin-transform-typescript': 7.23.5(@babel/core@7.23.5)
'@vue/babel-plugin-jsx': 1.1.5(@babel/core@7.23.5)
vite: 5.0.8(@types/node@20.10.2)(less@4.2.0)
vite: 5.0.9(@types/node@20.10.2)(less@4.2.0)
vue: 3.3.11(typescript@5.3.2)
transitivePeerDependencies:
- supports-color
dev: true
/@vitejs/plugin-vue@4.5.1(vite@5.0.8)(vue@3.3.11):
/@vitejs/plugin-vue@4.5.1(vite@5.0.9)(vue@3.3.11):
resolution: {integrity: sha512-DaUzYFr+2UGDG7VSSdShKa9sIWYBa1LL8KC0MNOf2H5LjcTPjob0x8LbkqXWmAtbANJCkpiQTj66UVcQkN2s3g==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
vite: ^4.0.0 || ^5.0.0
vue: ^3.2.25
dependencies:
vite: 5.0.8(@types/node@20.10.2)(less@4.2.0)
vite: 5.0.9(@types/node@20.10.2)(less@4.2.0)
vue: 3.3.11(typescript@5.3.2)
dev: true
@ -1852,10 +1834,6 @@ packages:
resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==}
dev: false
/async@3.2.5:
resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==}
dev: true
/asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
dev: false
@ -1932,10 +1910,6 @@ packages:
update-browserslist-db: 1.0.13(browserslist@4.22.1)
dev: true
/buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
dev: true
/builtin-modules@3.3.0:
resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==}
engines: {node: '>=6'}
@ -1959,13 +1933,6 @@ packages:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
/camel-case@4.1.2:
resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==}
dependencies:
pascal-case: 3.1.2
tslib: 2.6.2
dev: true
/camelcase-css@2.0.1:
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
engines: {node: '>= 6'}
@ -2027,13 +1994,6 @@ packages:
engines: {node: '>=8'}
dev: true
/clean-css@5.3.3:
resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==}
engines: {node: '>= 10.0'}
dependencies:
source-map: 0.6.1
dev: true
/clean-regexp@1.0.0:
resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==}
engines: {node: '>=4'}
@ -2058,10 +2018,6 @@ packages:
/color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
/colorette@2.0.20:
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
dev: true
/combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
@ -2079,10 +2035,6 @@ packages:
typical: 4.0.0
dev: false
/commander@2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
dev: true
/commander@4.1.1:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
@ -2093,11 +2045,6 @@ packages:
engines: {node: '>= 10'}
dev: true
/commander@8.3.0:
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
engines: {node: '>= 12'}
dev: true
/comment-parser@1.4.1:
resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==}
engines: {node: '>= 12.0.0'}
@ -2114,15 +2061,6 @@ packages:
/concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
/connect-history-api-fallback@1.6.0:
resolution: {integrity: sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==}
engines: {node: '>=0.8'}
dev: true
/consola@2.15.3:
resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==}
dev: true
/convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
dev: true
@ -2156,16 +2094,6 @@ packages:
shebang-command: 2.0.0
which: 2.0.2
/css-select@4.3.0:
resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==}
dependencies:
boolbase: 1.0.0
css-what: 6.1.0
domhandler: 4.3.1
domutils: 2.8.0
nth-check: 2.1.1
dev: true
/css-select@5.1.0:
resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
dependencies:
@ -2311,14 +2239,6 @@ packages:
resolution: {integrity: sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==}
dev: false
/dom-serializer@1.4.1:
resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==}
dependencies:
domelementtype: 2.3.0
domhandler: 4.3.1
entities: 2.2.0
dev: true
/dom-serializer@2.0.0:
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
dependencies:
@ -2331,13 +2251,6 @@ packages:
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
dev: true
/domhandler@4.3.1:
resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==}
engines: {node: '>= 4'}
dependencies:
domelementtype: 2.3.0
dev: true
/domhandler@5.0.3:
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
engines: {node: '>= 4'}
@ -2345,14 +2258,6 @@ packages:
domelementtype: 2.3.0
dev: true
/domutils@2.8.0:
resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==}
dependencies:
dom-serializer: 1.4.1
domelementtype: 2.3.0
domhandler: 4.3.1
dev: true
/domutils@3.1.0:
resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==}
dependencies:
@ -2361,35 +2266,10 @@ packages:
domhandler: 5.0.3
dev: true
/dot-case@3.0.4:
resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==}
dependencies:
no-case: 3.0.4
tslib: 2.6.2
dev: true
/dotenv-expand@8.0.3:
resolution: {integrity: sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg==}
engines: {node: '>=12'}
dev: true
/dotenv@16.3.1:
resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==}
engines: {node: '>=12'}
dev: true
/eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
dev: false
/ejs@3.1.9:
resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==}
engines: {node: '>=0.10.0'}
hasBin: true
dependencies:
jake: 10.8.7
dev: true
/electron-to-chromium@1.4.601:
resolution: {integrity: sha512-SpwUMDWe9tQu8JX5QCO1+p/hChAi9AE9UpoC3rcHVc+gdCGlbT3SGb5I1klgb952HRIyvt9wZhSz9bNBYz9swA==}
dev: true
@ -2410,10 +2290,6 @@ packages:
tapable: 2.2.1
dev: true
/entities@2.2.0:
resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==}
dev: true
/entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
@ -3067,12 +2943,6 @@ packages:
flat-cache: 3.2.0
dev: true
/filelist@1.0.4:
resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
dependencies:
minimatch: 5.1.6
dev: true
/fill-range@7.0.1:
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
engines: {node: '>=8'}
@ -3153,15 +3023,6 @@ packages:
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
dev: true
/fs-extra@10.1.0:
resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
engines: {node: '>=12'}
dependencies:
graceful-fs: 4.2.11
jsonfile: 6.1.0
universalify: 2.0.1
dev: true
/fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
@ -3382,20 +3243,6 @@ packages:
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
dev: true
/html-minifier-terser@6.1.0:
resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==}
engines: {node: '>=12'}
hasBin: true
dependencies:
camel-case: 4.1.2
clean-css: 5.3.3
commander: 8.3.0
he: 1.2.0
param-case: 3.0.4
relateurl: 0.2.7
terser: 5.24.0
dev: true
/html-tags@3.3.1:
resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==}
engines: {node: '>=8'}
@ -3645,17 +3492,6 @@ packages:
'@pkgjs/parseargs': 0.11.0
dev: false
/jake@10.8.7:
resolution: {integrity: sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==}
engines: {node: '>=10'}
hasBin: true
dependencies:
async: 3.2.5
chalk: 4.1.2
filelist: 1.0.4
minimatch: 3.1.2
dev: true
/jiti@1.21.0:
resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==}
hasBin: true
@ -3735,14 +3571,6 @@ packages:
resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
dev: true
/jsonfile@6.1.0:
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
dependencies:
universalify: 2.0.1
optionalDependencies:
graceful-fs: 4.2.11
dev: true
/keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
dependencies:
@ -3838,12 +3666,6 @@ packages:
js-tokens: 4.0.0
dev: false
/lower-case@2.0.2:
resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
dependencies:
tslib: 2.6.2
dev: true
/lru-cache@10.1.0:
resolution: {integrity: sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==}
engines: {node: 14 || >=16.14}
@ -3965,13 +3787,6 @@ packages:
dependencies:
brace-expansion: 1.1.11
/minimatch@5.1.6:
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
engines: {node: '>=10'}
dependencies:
brace-expansion: 2.0.1
dev: true
/minimatch@9.0.3:
resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
engines: {node: '>=16 || 14 >=14.17'}
@ -4041,20 +3856,6 @@ packages:
- supports-color
optional: true
/no-case@3.0.4:
resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
dependencies:
lower-case: 2.0.2
tslib: 2.6.2
dev: true
/node-html-parser@5.4.2:
resolution: {integrity: sha512-RaBPP3+51hPne/OolXxcz89iYvQvKOydaqoePpOgXcrOKZhjVIzmpKZz+Hd/RBO2/zN2q6CNJhQzucVz+u3Jyw==}
dependencies:
css-select: 4.3.0
he: 1.2.0
dev: true
/node-releases@2.0.14:
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
dev: true
@ -4194,13 +3995,6 @@ packages:
engines: {node: '>=6'}
dev: true
/param-case@3.0.4:
resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==}
dependencies:
dot-case: 3.0.4
tslib: 2.6.2
dev: true
/parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
@ -4241,13 +4035,6 @@ packages:
resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
dev: false
/pascal-case@3.1.2:
resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==}
dependencies:
no-case: 3.0.4
tslib: 2.6.2
dev: true
/path-browserify@1.0.1:
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
dev: true
@ -4281,10 +4068,6 @@ packages:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
/pathe@0.2.0:
resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==}
dev: true
/pathe@1.1.1:
resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==}
dev: true
@ -4513,11 +4296,6 @@ packages:
jsesc: 0.5.0
dev: true
/relateurl@0.2.7:
resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==}
engines: {node: '>= 0.10'}
dev: true
/resize-observer-polyfill@1.5.1:
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
dev: false
@ -4702,16 +4480,11 @@ packages:
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
engines: {node: '>=0.10.0'}
/source-map-support@0.5.21:
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
dependencies:
buffer-from: 1.1.2
source-map: 0.6.1
dev: true
/source-map@0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
requiresBuild: true
optional: true
/spdx-correct@3.2.0:
resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==}
@ -4957,17 +4730,6 @@ packages:
engines: {node: '>=6'}
dev: true
/terser@5.24.0:
resolution: {integrity: sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==}
engines: {node: '>=10'}
hasBin: true
dependencies:
'@jridgewell/source-map': 0.3.5
acorn: 8.11.2
commander: 2.20.3
source-map-support: 0.5.21
dev: true
/text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
dev: true
@ -5150,11 +4912,6 @@ packages:
'@types/unist': 2.0.10
dev: true
/universalify@2.0.1:
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
engines: {node: '>= 10.0.0'}
dev: true
/unplugin-auto-import@0.17.1(@vueuse/core@10.6.1):
resolution: {integrity: sha512-QvdJKtFK0COSuRXzVnwjG3ir870zVhdMg6O8GKG3UO/O5W4fmJm5h71QvzI7Gp8Sx0qfCvC3f+2v0Vm489fnqQ==}
engines: {node: '>=14'}
@ -5264,7 +5021,7 @@ packages:
'@types/node': 20.10.2
rimraf: 5.0.5
typescript: 5.3.2
vite: 5.0.8(@types/node@20.10.2)(less@4.2.0)
vite: 5.0.9(@types/node@20.10.2)(less@4.2.0)
transitivePeerDependencies:
- less
- lightningcss
@ -5274,26 +5031,6 @@ packages:
- terser
dev: false
/vite-plugin-html@3.2.0(vite@5.0.8):
resolution: {integrity: sha512-2VLCeDiHmV/BqqNn5h2V+4280KRgQzCFN47cst3WiNK848klESPQnzuC3okH5XHtgwHH/6s1Ho/YV6yIO0pgoQ==}
peerDependencies:
vite: '>=2.0.0'
dependencies:
'@rollup/pluginutils': 4.2.1
colorette: 2.0.20
connect-history-api-fallback: 1.6.0
consola: 2.15.3
dotenv: 16.3.1
dotenv-expand: 8.0.3
ejs: 3.1.9
fast-glob: 3.3.2
fs-extra: 10.1.0
html-minifier-terser: 6.1.0
node-html-parser: 5.4.2
pathe: 0.2.0
vite: 5.0.8(@types/node@20.10.2)(less@4.2.0)
dev: true
/vite-svg-loader@5.1.0(vue@3.3.11):
resolution: {integrity: sha512-M/wqwtOEjgb956/+m5ZrYT/Iq6Hax0OakWbokj8+9PXOnB7b/4AxESHieEtnNEy7ZpjsjYW1/5nK8fATQMmRxw==}
peerDependencies:
@ -5303,8 +5040,8 @@ packages:
vue: 3.3.11(typescript@5.3.2)
dev: true
/vite@5.0.8(@types/node@20.10.2)(less@4.2.0):
resolution: {integrity: sha512-jYMALd8aeqR3yS9xlHd0OzQJndS9fH5ylVgWdB+pxTwxLKdO1pgC5Dlb398BUxpfaBxa4M9oT7j1g503Gaj5IQ==}
/vite@5.0.9(@types/node@20.10.2)(less@4.2.0):
resolution: {integrity: sha512-wVqMd5kp28QWGgfYPDfrj771VyHTJ4UDlCteLH7bJDGDEamaz5hV8IX6h1brSGgnnyf9lI2RnzXq/JmD0c2wwg==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
@ -5430,8 +5167,8 @@ packages:
vue: 3.3.11(typescript@5.3.2)
dev: false
/vue3-gettext@3.0.0-beta.2(@vue/compiler-sfc@3.3.10)(vue@3.3.11):
resolution: {integrity: sha512-pty6Nj1cXtF2WrUvNY6caDfPlJFu7A6QCOH3btzTlMj1hk86Xpfj0XwyGdxTz/8YnGwVR5eiaudxQYbYW/0ZIw==}
/vue3-gettext@3.0.0-beta.3(@vue/compiler-sfc@3.3.10)(vue@3.3.11):
resolution: {integrity: sha512-zE6qKEhzlL4R/El9Z6dSg+tmXjfLorG5/Y2o+Z+DMt7dMxeYF3FQbkHzvU7DKZMXkAkkscwspmwsYAXp5ctGdA==}
engines: {node: '>= 12.0.0'}
hasBin: true
peerDependencies:

14
app/src/api/upstream.ts Normal file
View file

@ -0,0 +1,14 @@
import ws from '@/lib/websocket'
export interface UpstreamStatus {
online: boolean
latency: number
}
const upstream = {
availability_test() {
return ws('/api/availability_test')
},
}
export default upstream

View file

@ -1 +1 @@
{"version":"2.0.0-beta.7","build_id":94,"total_build":298}
{"version":"2.0.0-beta.7","build_id":97,"total_build":301}

View file

@ -2,8 +2,11 @@
import { MoreOutlined, PlusOutlined } from '@ant-design/icons-vue'
import { useGettext } from 'vue3-gettext'
import Modal from 'ant-design-vue/lib/modal'
import type { NgxConfig } from '@/api/ngx'
import _ from 'lodash'
import type { NgxConfig, NgxDirective } from '@/api/ngx'
import DirectiveEditor from '@/views/domain/ngx_conf/directive/DirectiveEditor.vue'
import type { UpstreamStatus } from '@/api/upstream.ts'
import upstream from '@/api/upstream.ts'
const { $gettext } = useGettext()
@ -11,12 +14,17 @@ const [modal, ContextHolder] = Modal.useModal()
const ngx_config = inject('ngx_config') as NgxConfig
const current_upstream_index = ref(0)
function add_upstream() {
async function add_upstream() {
if (!ngx_config.upstreams)
ngx_config.upstreams = []
ngx_config.upstreams?.push({
name: '',
comments: '',
directives: [],
})
rename(ngx_config.upstreams.length - 1)
}
function remove_upstream(index: number) {
@ -54,12 +62,54 @@ function ok() {
ngx_config.upstreams[renameIdx.value].name = buffer.value
open.value = false
}
const availabilityResult = ref({}) as Ref<Record<string, UpstreamStatus>>
const websocket = ref()
function availability_test() {
const sockets: string[] = []
for (const u of ngx_config.upstreams ?? []) {
for (const d of u.directives ?? []) {
if (d.directive === 'server')
sockets.push(d.params.split(' ')[0])
}
}
websocket.value = upstream.availability_test()
websocket.value.onopen = () => {
websocket.value.send(JSON.stringify(sockets))
}
websocket.value.onmessage = (e: MessageEvent) => {
availabilityResult.value = JSON.parse(e.data)
}
}
onMounted(() => {
availability_test()
})
onBeforeUnmount(() => {
websocket.value?.close()
})
async function _restartTest() {
websocket.value?.close()
availability_test()
}
const restartTest = _.throttle(_restartTest, 5000)
watch(ngx_directives, () => {
restartTest()
}, { deep: true })
</script>
<template>
<div>
<ContextHolder />
<ATabs v-model:activeKey="current_upstream_index">
<ATabs
v-if="ngx_config.upstreams && ngx_config.upstreams.length > 0"
v-model:activeKey="current_upstream_index"
>
<ATabPane
v-for="(v, k) in ngx_config.upstreams"
:key="k"
@ -82,7 +132,14 @@ function ok() {
</template>
<div class="tab-content">
<DirectiveEditor />
<DirectiveEditor>
<template #directiveSuffix="{ directive }: {directive: NgxDirective}">
<template v-if="availabilityResult[directive.params]?.online">
<ABadge color="green" />
{{ availabilityResult[directive.params]?.latency.toFixed(2) }}ms
</template>
</template>
</DirectiveEditor>
</div>
</ATabPane>
@ -97,10 +154,21 @@ function ok() {
</AButton>
</template>
</ATabs>
<div v-else>
<AEmpty />
<div class="flex justify-center">
<AButton
type="primary"
@click="add_upstream"
>
{{ $gettext('Create') }}
</AButton>
</div>
</div>
<AModal
v-model:open="open"
:title="$gettext('Rename Upstream')"
:title="$gettext('Upstream Name')"
centered
@ok="ok"
>

View file

@ -8,6 +8,7 @@ import type { NgxDirective } from '@/api/ngx'
defineProps<{
readonly?: boolean
context?: string
}>()
const { $gettext } = useGettext()
@ -33,8 +34,19 @@ provide('current_idx', current_idx)
v-auto-animate
:index="index"
:readonly="readonly"
:context="context"
@click="current_idx = index"
/>
>
<template
v-if="$slots.directiveSuffix"
#suffix="{ directive }"
>
<slot
name="directiveSuffix"
:directive="directive"
/>
</template>
</DirectiveEditorItem>
</template>
</Draggable>

View file

@ -11,6 +11,7 @@ import type { NgxDirective } from '@/api/ngx'
const props = defineProps<{
index: number
readonly?: boolean
context?: string
}>()
const { $gettext, interpolate } = useGettext()
@ -72,6 +73,15 @@ const currentIdx = inject('current_idx')
<HolderOutlined />
{{ ngx_directives[props.index].directive }}
</template>
<template
v-if="$slots.suffix"
#suffix
>
<slot
name="suffix"
:directive="ngx_directives[props.index]"
/>
</template>
</AInput>
<APopconfirm

View file

@ -1 +1 @@
{"version":"2.0.0-beta.7","build_id":94,"total_build":298}
{"version":"2.0.0-beta.7","build_id":97,"total_build":301}

4
go.mod
View file

@ -17,7 +17,7 @@ require (
github.com/go-co-op/gocron v1.36.0
github.com/go-playground/validator/v10 v10.16.0
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/google/uuid v1.4.0
github.com/google/uuid v1.5.0
github.com/gorilla/websocket v1.5.1
github.com/hpcloud/tail v1.0.0
github.com/jpillora/overseer v1.1.6
@ -195,6 +195,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
github.com/pquerna/otp v1.4.0 // indirect
github.com/prometheus-community/pro-bing v0.3.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sacloud/api-client-go v0.2.9 // indirect
@ -239,6 +240,7 @@ require (
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/oauth2 v0.15.0 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect

4
go.sum
View file

@ -1065,6 +1065,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
@ -1470,6 +1472,8 @@ github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/pretty66/websocketproxy v0.0.0-20220507015215-930b3a686308 h1:JfSau4YABtkm5gRtFWuRWHT2Lsw4ZbyB4F/qORwf+BA=
github.com/pretty66/websocketproxy v0.0.0-20220507015215-930b3a686308/go.mod h1:hxhFuMswfNko9fAxYeqBapfUdJHAgDafBs/MzOZh0X8=
github.com/prometheus-community/pro-bing v0.3.0 h1:SFT6gHqXwbItEDJhTkzPWVqU6CLEtqEfNAPp47RUON4=
github.com/prometheus-community/pro-bing v0.3.0/go.mod h1:p9dLb9zdmv+eLxWfCT6jESWuDrS+YzpPkQBgysQF8a0=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=

View file

@ -0,0 +1,83 @@
package upstream
import (
"net"
"sync"
"time"
)
const MaxTimeout = 5 * time.Second
const MaxConcurrentWorker = 10
type Status struct {
Online bool `json:"online"`
Latency float32 `json:"latency"`
}
func AvailabilityTest(body []string) (result map[string]*Status) {
result = make(map[string]*Status)
wg := sync.WaitGroup{}
wg.Add(len(body))
c := make(chan struct{}, MaxConcurrentWorker)
for _, socket := range body {
c <- struct{}{}
s := &Status{}
go testLatency(c, &wg, socket, s)
result[socket] = s
}
wg.Wait()
return
}
func testLatency(c chan struct{}, wg *sync.WaitGroup, socket string, status *Status) {
defer func() {
wg.Done()
<-c
}()
scopedWg := sync.WaitGroup{}
scopedWg.Add(2)
go testTCPLatency(&scopedWg, socket, status)
go testUnixSocketLatency(&scopedWg, socket, status)
scopedWg.Wait()
}
func testTCPLatency(wg *sync.WaitGroup, socket string, status *Status) {
defer func() {
wg.Done()
}()
start := time.Now()
conn, err := net.DialTimeout("tcp", socket, MaxTimeout)
if err != nil {
return
}
defer conn.Close()
end := time.Now()
status.Online = true
status.Latency = float32(end.Sub(start)) / float32(time.Millisecond)
}
func testUnixSocketLatency(wg *sync.WaitGroup, socket string, status *Status) {
defer func() {
wg.Done()
}()
start := time.Now()
conn, err := net.DialTimeout("unix", socket, MaxTimeout)
if err != nil {
return
}
defer conn.Close()
end := time.Now()
status.Online = true
status.Latency = float32(end.Sub(start)) / float32(time.Millisecond)
}

View file

@ -12,6 +12,7 @@ import (
"github.com/0xJacky/Nginx-UI/api/system"
"github.com/0xJacky/Nginx-UI/api/template"
"github.com/0xJacky/Nginx-UI/api/terminal"
"github.com/0xJacky/Nginx-UI/api/upstream"
"github.com/0xJacky/Nginx-UI/api/user"
"github.com/gin-contrib/static"
"github.com/gin-gonic/gin"
@ -64,6 +65,7 @@ func InitRouter() *gin.Engine {
analytic.InitWebSocketRouter(w)
terminal.InitRouter(w)
nginx.InitNginxLogRouter(w)
upstream.InitRouter(w)
}
}