mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 02:15:48 +02:00
feat: add enabled/disabled field to environment model #169
This commit is contained in:
parent
cc5d2be1bd
commit
b429c15893
17 changed files with 250 additions and 240 deletions
|
@ -3,13 +3,12 @@ package cluster
|
||||||
import (
|
import (
|
||||||
"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/environment"
|
"github.com/0xJacky/Nginx-UI/internal/cosy"
|
||||||
"github.com/0xJacky/Nginx-UI/model"
|
"github.com/0xJacky/Nginx-UI/model"
|
||||||
"github.com/0xJacky/Nginx-UI/query"
|
"github.com/0xJacky/Nginx-UI/query"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetEnvironment(c *gin.Context) {
|
func GetEnvironment(c *gin.Context) {
|
||||||
|
@ -27,99 +26,34 @@ func GetEnvironment(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetEnvironmentList(c *gin.Context) {
|
func GetEnvironmentList(c *gin.Context) {
|
||||||
data, err := environment.RetrieveEnvironmentList()
|
cosy.Core[model.Environment](c).
|
||||||
if err != nil {
|
SetFussy("name").
|
||||||
api.ErrHandler(c, err)
|
SetEqual("enabled").
|
||||||
return
|
SetTransformer(func(m *model.Environment) any {
|
||||||
}
|
return analytic.GetNode(m)
|
||||||
c.JSON(http.StatusOK, gin.H{
|
}).PagingList()
|
||||||
"data": data,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type EnvironmentManageJson struct {
|
|
||||||
Name string `json:"name" binding:"required"`
|
|
||||||
URL string `json:"url" binding:"required"`
|
|
||||||
Token string `json:"token" binding:"required"`
|
|
||||||
OperationSync bool `json:"operation_sync"`
|
|
||||||
SyncApiRegex string `json:"sync_api_regex"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateRegex(data EnvironmentManageJson) error {
|
|
||||||
if data.OperationSync {
|
|
||||||
_, err := regexp.Compile(data.SyncApiRegex)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddEnvironment(c *gin.Context) {
|
func AddEnvironment(c *gin.Context) {
|
||||||
var json EnvironmentManageJson
|
cosy.Core[model.Environment](c).SetValidRules(gin.H{
|
||||||
if !api.BindAndValid(c, &json) {
|
"name": "required",
|
||||||
return
|
"url": "required,url",
|
||||||
}
|
"token": "required",
|
||||||
if err := validateRegex(json); err != nil {
|
"enabled": "omitempty,boolean",
|
||||||
api.ErrHandler(c, err)
|
}).ExecutedHook(func(c *cosy.Ctx[model.Environment]) {
|
||||||
return
|
go analytic.RestartRetrieveNodesStatus()
|
||||||
}
|
}).Create()
|
||||||
|
|
||||||
env := model.Environment{
|
|
||||||
Name: json.Name,
|
|
||||||
URL: json.URL,
|
|
||||||
Token: json.Token,
|
|
||||||
OperationSync: json.OperationSync,
|
|
||||||
SyncApiRegex: json.SyncApiRegex,
|
|
||||||
}
|
|
||||||
|
|
||||||
envQuery := query.Environment
|
|
||||||
|
|
||||||
err := envQuery.Create(&env)
|
|
||||||
if err != nil {
|
|
||||||
api.ErrHandler(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
go analytic.RestartRetrieveNodesStatus()
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, env)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func EditEnvironment(c *gin.Context) {
|
func EditEnvironment(c *gin.Context) {
|
||||||
id := cast.ToInt(c.Param("id"))
|
cosy.Core[model.Environment](c).SetValidRules(gin.H{
|
||||||
|
"name": "required",
|
||||||
var json EnvironmentManageJson
|
"url": "required,url",
|
||||||
if !api.BindAndValid(c, &json) {
|
"token": "required",
|
||||||
return
|
"enabled": "omitempty,boolean",
|
||||||
}
|
}).ExecutedHook(func(c *cosy.Ctx[model.Environment]) {
|
||||||
if err := validateRegex(json); err != nil {
|
go analytic.RestartRetrieveNodesStatus()
|
||||||
api.ErrHandler(c, err)
|
}).Modify()
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
envQuery := query.Environment
|
|
||||||
|
|
||||||
env, err := envQuery.FirstByID(id)
|
|
||||||
if err != nil {
|
|
||||||
api.ErrHandler(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = envQuery.Where(envQuery.ID.Eq(env.ID)).Updates(&model.Environment{
|
|
||||||
Name: json.Name,
|
|
||||||
URL: json.URL,
|
|
||||||
Token: json.Token,
|
|
||||||
OperationSync: json.OperationSync,
|
|
||||||
SyncApiRegex: json.SyncApiRegex,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
api.ErrHandler(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
go analytic.RestartRetrieveNodesStatus()
|
|
||||||
|
|
||||||
GetEnvironment(c)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteEnvironment(c *gin.Context) {
|
func DeleteEnvironment(c *gin.Context) {
|
||||||
|
|
|
@ -1,47 +1,50 @@
|
||||||
[server]
|
[server]
|
||||||
HttpPort = 9000
|
HttpPort = 9000
|
||||||
RunMode = debug
|
RunMode = debug
|
||||||
JwtSecret =
|
JwtSecret =
|
||||||
Email =
|
Email =
|
||||||
HTTPChallengePort = 9180
|
HTTPChallengePort = 9180
|
||||||
StartCmd = bash
|
StartCmd = bash
|
||||||
Database = database
|
Database = database
|
||||||
CADir =
|
CADir =
|
||||||
GithubProxy =
|
GithubProxy =
|
||||||
NodeSecret =
|
NodeSecret =
|
||||||
Demo = false
|
Demo = false
|
||||||
PageSize = 10
|
PageSize = 10
|
||||||
HttpHost = 0.0.0.0
|
HttpHost = 0.0.0.0
|
||||||
CertRenewalInterval = 7
|
CertRenewalInterval = 7
|
||||||
RecursiveNameservers =
|
RecursiveNameservers =
|
||||||
SkipInstallation = false
|
SkipInstallation = false
|
||||||
Name =
|
Name =
|
||||||
|
|
||||||
[nginx]
|
[nginx]
|
||||||
AccessLogPath = /var/log/nginx/access.log
|
AccessLogPath = /var/log/nginx/access.log
|
||||||
ErrorLogPath = /var/log/nginx/error.log
|
ErrorLogPath = /var/log/nginx/error.log
|
||||||
ConfigDir =
|
ConfigDir =
|
||||||
PIDPath =
|
PIDPath =
|
||||||
TestConfigCmd =
|
TestConfigCmd =
|
||||||
ReloadCmd =
|
ReloadCmd =
|
||||||
RestartCmd =
|
RestartCmd =
|
||||||
|
|
||||||
[openai]
|
[openai]
|
||||||
Model =
|
Model =
|
||||||
BaseUrl =
|
BaseUrl =
|
||||||
Proxy =
|
Proxy =
|
||||||
Token =
|
Token =
|
||||||
|
|
||||||
[casdoor]
|
[casdoor]
|
||||||
Endpoint =
|
Endpoint =
|
||||||
ClientId =
|
ClientId =
|
||||||
ClientSecret =
|
ClientSecret =
|
||||||
Certificate =
|
Certificate =
|
||||||
Organization =
|
Organization =
|
||||||
Application =
|
Application =
|
||||||
RedirectUri =
|
RedirectUri =
|
||||||
|
|
||||||
[logrotate]
|
[logrotate]
|
||||||
Enabled = false
|
Enabled = false
|
||||||
CMD = logrotate /etc/logrotate.d/nginx
|
CMD = logrotate /etc/logrotate.d/nginx
|
||||||
Interval = 1440
|
Interval = 1440
|
||||||
|
|
||||||
|
[cluster]
|
||||||
|
Node =
|
||||||
|
|
|
@ -66,16 +66,21 @@ async function request() {
|
||||||
let hasCodeBlockIndicator = false
|
let hasCodeBlockIndicator = false
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const { done, value } = await reader.read()
|
try {
|
||||||
if (done) {
|
const { done, value } = await reader.read()
|
||||||
setTimeout(() => {
|
if (done) {
|
||||||
scrollToBottom()
|
setTimeout(() => {
|
||||||
}, 500)
|
scrollToBottom()
|
||||||
loading.value = false
|
}, 500)
|
||||||
store_record()
|
loading.value = false
|
||||||
|
store_record()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
apply(value!)
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
apply(value!)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function apply(input: Uint8Array) {
|
function apply(input: Uint8Array) {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { storeToRefs } from 'pinia'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { computed, watch } from 'vue'
|
import { computed, watch } from 'vue'
|
||||||
import { useSettingsStore } from '@/pinia'
|
import { useSettingsStore } from '@/pinia'
|
||||||
import settings from '@/api/settings'
|
|
||||||
|
|
||||||
const settingsStore = useSettingsStore()
|
const settingsStore = useSettingsStore()
|
||||||
|
|
||||||
|
@ -28,10 +27,6 @@ watch(node_id, async () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const { server_name } = storeToRefs(useSettingsStore())
|
const { server_name } = storeToRefs(useSettingsStore())
|
||||||
|
|
||||||
settings.get_server_name().then(r => {
|
|
||||||
server_name.value = r.name
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -14,11 +14,21 @@ const emit = defineEmits(['update:target', 'update:map'])
|
||||||
const data = ref([]) as Ref<Environment[]>
|
const data = ref([]) as Ref<Environment[]>
|
||||||
const data_map = ref({}) as Ref<Record<number, Environment>>
|
const data_map = ref({}) as Ref<Record<number, Environment>>
|
||||||
|
|
||||||
environment.get_list().then(r => {
|
onMounted(async () => {
|
||||||
data.value = r.data
|
let hasMore = true
|
||||||
r.data?.forEach(node => {
|
let page = 1
|
||||||
data_map.value[node.id] = node
|
while (hasMore) {
|
||||||
})
|
await environment.get_list({ page, enabled: true }).then(r => {
|
||||||
|
data.value.push(...r.data)
|
||||||
|
r.data?.forEach(node => {
|
||||||
|
data_map.value[node.id] = node
|
||||||
|
})
|
||||||
|
hasMore = r.data.length === r.pagination.per_page
|
||||||
|
page++
|
||||||
|
}).catch(() => {
|
||||||
|
hasMore = false
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const value = computed({
|
const value = computed({
|
||||||
|
@ -35,14 +45,24 @@ const value = computed({
|
||||||
emit('update:target', v)
|
emit('update:target', v)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const noData = computed(() => {
|
||||||
|
return props.hiddenLocal && !data?.value?.length
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ACheckboxGroup
|
<ACheckboxGroup
|
||||||
v-model:value="value"
|
v-model:value="value"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
|
:class="{
|
||||||
|
'justify-center': noData,
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<ARow :gutter="[16, 16]">
|
<ARow
|
||||||
|
v-if="!noData"
|
||||||
|
:gutter="[16, 16]"
|
||||||
|
>
|
||||||
<ACol
|
<ACol
|
||||||
v-if="!hiddenLocal"
|
v-if="!hiddenLocal"
|
||||||
:span="8"
|
:span="8"
|
||||||
|
@ -76,7 +96,7 @@ const value = computed({
|
||||||
</ATag>
|
</ATag>
|
||||||
</ACol>
|
</ACol>
|
||||||
</ARow>
|
</ARow>
|
||||||
<AEmpty v-if="hiddenLocal && data?.length === 0" />
|
<AEmpty v-else />
|
||||||
</ACheckboxGroup>
|
</ACheckboxGroup>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
import { storeToRefs } from 'pinia'
|
||||||
import FooterLayout from './FooterLayout.vue'
|
import FooterLayout from './FooterLayout.vue'
|
||||||
import SideBar from './SideBar.vue'
|
import SideBar from './SideBar.vue'
|
||||||
import HeaderLayout from './HeaderLayout.vue'
|
import HeaderLayout from './HeaderLayout.vue'
|
||||||
import PageHeader from '@/components/PageHeader/PageHeader.vue'
|
import PageHeader from '@/components/PageHeader/PageHeader.vue'
|
||||||
|
import { useSettingsStore } from '@/pinia'
|
||||||
|
import settings from '@/api/settings'
|
||||||
|
|
||||||
const drawer_visible = ref(false)
|
const drawer_visible = ref(false)
|
||||||
const collapsed = ref(collapse())
|
const collapsed = ref(collapse())
|
||||||
|
@ -19,6 +22,12 @@ function getClientWidth() {
|
||||||
function collapse() {
|
function collapse() {
|
||||||
return getClientWidth() < 1280
|
return getClientWidth() < 1280
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { server_name } = storeToRefs(useSettingsStore())
|
||||||
|
|
||||||
|
settings.get_server_name().then(r => {
|
||||||
|
server_name.value = r.name
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -150,10 +159,6 @@ body {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-card-bordered {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-notice-wrapper .ant-tabs-content {
|
.header-notice-wrapper .ant-tabs-content {
|
||||||
max-height: 250px;
|
max-height: 250px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ interface meta {
|
||||||
|
|
||||||
interface sidebar {
|
interface sidebar {
|
||||||
path: string
|
path: string
|
||||||
name: () => string
|
name: string
|
||||||
meta: meta
|
meta: meta
|
||||||
children: sidebar[]
|
children: sidebar[]
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ const visible: ComputedRef<sidebar[]> = computed(() => {
|
||||||
|
|
||||||
const t: sidebar = {
|
const t: sidebar = {
|
||||||
path: s.path,
|
path: s.path,
|
||||||
name: s?.meta?.name ?? (() => ''),
|
name: s.name as string,
|
||||||
meta: s.meta as unknown as meta,
|
meta: s.meta as unknown as meta,
|
||||||
children: [],
|
children: [],
|
||||||
};
|
};
|
||||||
|
|
|
@ -42,12 +42,17 @@ onMounted(async () => {
|
||||||
users.value = []
|
users.value = []
|
||||||
let page = 1
|
let page = 1
|
||||||
while (true) {
|
while (true) {
|
||||||
const r = await acme_user.get_list({ page })
|
try {
|
||||||
|
const r = await acme_user.get_list({ page })
|
||||||
|
|
||||||
users.value.push(...r.data)
|
users.value.push(...r.data)
|
||||||
if (r?.data?.length < r?.pagination?.per_page)
|
if (r?.data?.length < r?.pagination?.per_page)
|
||||||
|
break
|
||||||
|
page++
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
break
|
break
|
||||||
page++
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init()
|
init()
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ServerAnalytic from '@/views/dashboard/ServerAnalytic.vue'
|
import ServerAnalytic from '@/views/dashboard/ServerAnalytic.vue'
|
||||||
import Environments from '@/views/dashboard/Environments.vue'
|
import Environments from '@/views/dashboard/Environments.vue'
|
||||||
|
|
||||||
|
const key = ref(0)
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
key.value++
|
||||||
|
}, 5 * 60 * 1000)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<ServerAnalytic />
|
<ServerAnalytic :key />
|
||||||
<Environments />
|
<Environments />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -25,10 +25,21 @@ const node_map = computed(() => {
|
||||||
|
|
||||||
let websocket: ReconnectingWebSocket | WebSocket
|
let websocket: ReconnectingWebSocket | WebSocket
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
let hasMore = true
|
||||||
|
let page = 1
|
||||||
|
while (hasMore) {
|
||||||
|
await environment.get_list({ page, enabled: true }).then(r => {
|
||||||
|
data.value.push(...r.data)
|
||||||
|
hasMore = r.data.length === r.pagination.per_page
|
||||||
|
page++
|
||||||
|
}).catch(() => {
|
||||||
|
hasMore = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
environment.get_list().then(r => {
|
|
||||||
data.value = r.data
|
|
||||||
})
|
|
||||||
websocket = analytic.nodes()
|
websocket = analytic.nodes()
|
||||||
websocket.onmessage = async m => {
|
websocket.onmessage = async m => {
|
||||||
const nodes = JSON.parse(m.data)
|
const nodes = JSON.parse(m.data)
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { h } from 'vue'
|
import { h } from 'vue'
|
||||||
import { Badge } from 'ant-design-vue'
|
import { Badge, Tag } from 'ant-design-vue'
|
||||||
import type { customRender } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
|
import type { customRender } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
|
||||||
import { datetime } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
|
import { datetime } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
|
||||||
import environment from '@/api/environment'
|
import environment from '@/api/environment'
|
||||||
import StdCurd from '@/components/StdDesign/StdDataDisplay/StdCurd.vue'
|
import StdCurd from '@/components/StdDesign/StdDataDisplay/StdCurd.vue'
|
||||||
import { input } from '@/components/StdDesign/StdDataEntry'
|
import { input, switcher } from '@/components/StdDesign/StdDataEntry'
|
||||||
import type { Column, JSXElements } from '@/components/StdDesign/types'
|
import type { Column, JSXElements } from '@/components/StdDesign/types'
|
||||||
|
|
||||||
const columns: Column[] = [{
|
const columns: Column[] = [{
|
||||||
|
@ -16,6 +16,7 @@ const columns: Column[] = [{
|
||||||
edit: {
|
edit: {
|
||||||
type: input,
|
type: input,
|
||||||
},
|
},
|
||||||
|
search: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => $gettext('URL'),
|
title: () => $gettext('URL'),
|
||||||
|
@ -77,13 +78,19 @@ const columns: Column[] = [{
|
||||||
customRender: (args: customRender) => {
|
customRender: (args: customRender) => {
|
||||||
const template: JSXElements = []
|
const template: JSXElements = []
|
||||||
const { text } = args
|
const { text } = args
|
||||||
if (text === true || text > 0) {
|
if (args.record.enabled) {
|
||||||
template.push(<Badge status="success"/>)
|
if (text === true || text > 0) {
|
||||||
template.push($gettext('Online'))
|
template.push(<Badge status="success"/>)
|
||||||
|
template.push($gettext('Online'))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
template.push(<Badge status="error"/>)
|
||||||
|
template.push($gettext('Offline'))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
template.push(<Badge status="error"/>)
|
template.push(<Badge status="default"/>)
|
||||||
template.push($gettext('Offline'))
|
template.push($gettext('Disabled'))
|
||||||
}
|
}
|
||||||
|
|
||||||
return h('div', template)
|
return h('div', template)
|
||||||
|
@ -91,6 +98,26 @@ const columns: Column[] = [{
|
||||||
sortable: true,
|
sortable: true,
|
||||||
pithy: true,
|
pithy: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: () => $gettext('Enabled'),
|
||||||
|
dataIndex: 'enabled',
|
||||||
|
customRender: (args: customRender) => {
|
||||||
|
const template: JSXElements = []
|
||||||
|
const { text } = args
|
||||||
|
if (text === true || text > 0)
|
||||||
|
template.push(<Tag color="green">{$gettext('Enabled')}</Tag>)
|
||||||
|
|
||||||
|
else
|
||||||
|
template.push(<Tag color="orange">{$gettext('Disabled')}</Tag>)
|
||||||
|
|
||||||
|
return h('div', template)
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
type: switcher,
|
||||||
|
},
|
||||||
|
sortable: true,
|
||||||
|
pithy: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: () => $gettext('Updated at'),
|
title: () => $gettext('Updated at'),
|
||||||
dataIndex: 'updated_at',
|
dataIndex: 'updated_at',
|
||||||
|
|
|
@ -113,7 +113,13 @@ const errors: Record<string, Record<string, string>> = inject('errors') as Recor
|
||||||
</template>
|
</template>
|
||||||
</Draggable>
|
</Draggable>
|
||||||
</AFormItem>
|
</AFormItem>
|
||||||
<AFormItem :label="$gettext('Server Name')">
|
<AFormItem
|
||||||
|
:label="$gettext('Server Name')"
|
||||||
|
:validate-status="errors?.server?.name ? 'error' : ''"
|
||||||
|
:help="errors?.server?.name.includes('alpha_num_dash_dot')
|
||||||
|
? $gettext('The server name should only contain letters, numbers, dashes, and dots.')
|
||||||
|
: $gettext('Customize the name of local server to be displayed in the environment indicator.')"
|
||||||
|
>
|
||||||
<AInput v-model:value="data.server.name" />
|
<AInput v-model:value="data.server.name" />
|
||||||
</AFormItem>
|
</AFormItem>
|
||||||
</AForm>
|
</AForm>
|
||||||
|
|
|
@ -52,14 +52,16 @@ settings.get().then(r => {
|
||||||
data.value = r
|
data.value = r
|
||||||
})
|
})
|
||||||
|
|
||||||
const { server_name } = storeToRefs(useSettingsStore())
|
const settingsStore = useSettingsStore()
|
||||||
|
const { server_name } = storeToRefs(settingsStore)
|
||||||
const errors = ref({}) as Ref<Record<string, Record<string, string>>>
|
const errors = ref({}) as Ref<Record<string, Record<string, string>>>
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
// fix type
|
// fix type
|
||||||
data.value.server.http_challenge_port = data.value.server.http_challenge_port.toString()
|
data.value.server.http_challenge_port = data.value.server.http_challenge_port.toString()
|
||||||
settings.save(data.value).then(r => {
|
settings.save(data.value).then(r => {
|
||||||
server_name.value = r?.server?.name ?? ''
|
if (!settingsStore.is_remote)
|
||||||
|
server_name.value = r?.server?.name ?? ''
|
||||||
data.value = r
|
data.value = r
|
||||||
message.success($gettext('Save successfully'))
|
message.success($gettext('Save successfully'))
|
||||||
errors.value = {}
|
errors.value = {}
|
||||||
|
|
|
@ -16,26 +16,26 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type NodeInfo struct {
|
type NodeInfo struct {
|
||||||
NodeRuntimeInfo upgrader.RuntimeInfo `json:"node_runtime_info"`
|
NodeRuntimeInfo upgrader.RuntimeInfo `json:"node_runtime_info"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
CPUNum int `json:"cpu_num"`
|
CPUNum int `json:"cpu_num"`
|
||||||
MemoryTotal string `json:"memory_total"`
|
MemoryTotal string `json:"memory_total"`
|
||||||
DiskTotal string `json:"disk_total"`
|
DiskTotal string `json:"disk_total"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NodeStat struct {
|
type NodeStat struct {
|
||||||
AvgLoad *load.AvgStat `json:"avg_load"`
|
AvgLoad *load.AvgStat `json:"avg_load"`
|
||||||
CPUPercent float64 `json:"cpu_percent"`
|
CPUPercent float64 `json:"cpu_percent"`
|
||||||
MemoryPercent float64 `json:"memory_percent"`
|
MemoryPercent float64 `json:"memory_percent"`
|
||||||
DiskPercent float64 `json:"disk_percent"`
|
DiskPercent float64 `json:"disk_percent"`
|
||||||
Network net.IOCountersStat `json:"network"`
|
Network net.IOCountersStat `json:"network"`
|
||||||
Status bool `json:"status"`
|
Status bool `json:"status"`
|
||||||
ResponseAt time.Time `json:"response_at"`
|
ResponseAt time.Time `json:"response_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
EnvironmentID int `json:"environment_id,omitempty"`
|
EnvironmentID int `json:"environment_id,omitempty"`
|
||||||
*model.Environment
|
*model.Environment
|
||||||
NodeStat
|
NodeStat
|
||||||
NodeInfo
|
NodeInfo
|
||||||
}
|
}
|
||||||
|
@ -47,66 +47,74 @@ type TNodeMap map[int]*Node
|
||||||
var NodeMap TNodeMap
|
var NodeMap TNodeMap
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
NodeMap = make(TNodeMap)
|
NodeMap = make(TNodeMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetNode(env *model.Environment) (n *Node) {
|
func GetNode(env *model.Environment) (n *Node) {
|
||||||
if env == nil {
|
if env == nil {
|
||||||
logger.Error("env is nil")
|
// this should never happen
|
||||||
return
|
logger.Error("env is nil")
|
||||||
}
|
return
|
||||||
n, ok := NodeMap[env.ID]
|
}
|
||||||
if !ok {
|
if !env.Enabled {
|
||||||
n = &Node{}
|
return &Node{
|
||||||
}
|
Environment: env,
|
||||||
n.Environment = env
|
}
|
||||||
return n
|
}
|
||||||
|
n, ok := NodeMap[env.ID]
|
||||||
|
if !ok {
|
||||||
|
n = &Node{}
|
||||||
|
}
|
||||||
|
n.Environment = env
|
||||||
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitNode(env *model.Environment) (n *Node) {
|
func InitNode(env *model.Environment) (n *Node) {
|
||||||
n = &Node{
|
n = &Node{
|
||||||
Environment: env,
|
Environment: env,
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := url.JoinPath(env.URL, "/api/node")
|
u, err := url.JoinPath(env.URL, "/api/node")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
client := http.Client{
|
||||||
logger.Error(err)
|
Transport: &http.Transport{
|
||||||
return
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
}
|
},
|
||||||
client := http.Client{
|
}
|
||||||
Transport: &http.Transport{
|
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
req, err := http.NewRequest("GET", u, nil)
|
|
||||||
req.Header.Set("X-Node-Secret", env.Token)
|
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
req, err := http.NewRequest("GET", u, nil)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
req.Header.Set("X-Node-Secret", env.Token)
|
||||||
logger.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
resp, err := client.Do(req)
|
||||||
bytes, _ := io.ReadAll(resp.Body)
|
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
if err != nil {
|
||||||
logger.Error(string(bytes))
|
logger.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.Unmarshal(bytes, &n.NodeInfo)
|
defer resp.Body.Close()
|
||||||
if err != nil {
|
bytes, _ := io.ReadAll(resp.Body)
|
||||||
logger.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
logger.Error(string(bytes))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(bytes, &n.NodeInfo)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
package environment
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/0xJacky/Nginx-UI/internal/analytic"
|
|
||||||
"github.com/0xJacky/Nginx-UI/query"
|
|
||||||
)
|
|
||||||
|
|
||||||
func RetrieveEnvironmentList() (envs []*analytic.Node, err error) {
|
|
||||||
envQuery := query.Environment
|
|
||||||
|
|
||||||
data, err := envQuery.Find()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range data {
|
|
||||||
t := analytic.GetNode(v)
|
|
||||||
|
|
||||||
envs = append(envs, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -10,6 +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"`
|
||||||
OperationSync bool `json:"operation_sync"`
|
OperationSync bool `json:"operation_sync"`
|
||||||
SyncApiRegex string `json:"sync_api_regex"`
|
SyncApiRegex string `json:"sync_api_regex"`
|
||||||
}
|
}
|
||||||
|
|
5
settings/cluster.go
Normal file
5
settings/cluster.go
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package settings
|
||||||
|
|
||||||
|
type Cluster struct {
|
||||||
|
Node []string `ini:",,allowshadow"`
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue