refactor(preference): show more settings

This commit is contained in:
Jacky 2024-10-23 16:51:54 +08:00
parent 59947a35db
commit a0c005f314
No known key found for this signature in database
GPG key ID: 215C21B10DF38B4D
17 changed files with 400 additions and 213 deletions

View file

@ -1,6 +1,7 @@
package settings package settings
import ( import (
"fmt"
"github.com/0xJacky/Nginx-UI/api" "github.com/0xJacky/Nginx-UI/api"
"github.com/0xJacky/Nginx-UI/internal/cron" "github.com/0xJacky/Nginx-UI/internal/cron"
"github.com/0xJacky/Nginx-UI/internal/nginx" "github.com/0xJacky/Nginx-UI/internal/nginx"
@ -19,22 +20,52 @@ func GetServerName(c *gin.Context) {
func GetSettings(c *gin.Context) { func GetSettings(c *gin.Context) {
settings.NginxSettings.AccessLogPath = nginx.GetAccessLogPath() settings.NginxSettings.AccessLogPath = nginx.GetAccessLogPath()
settings.NginxSettings.ErrorLogPath = nginx.GetErrorLogPath() settings.NginxSettings.ErrorLogPath = nginx.GetErrorLogPath()
settings.NginxSettings.ConfigDir = nginx.GetConfPath()
settings.NginxSettings.PIDPath = nginx.GetPIDPath()
if settings.NginxSettings.ReloadCmd == "" {
settings.NginxSettings.ReloadCmd = "nginx -s reload"
}
if settings.NginxSettings.RestartCmd == "" {
pidPath := nginx.GetPIDPath()
daemon := nginx.GetSbinPath()
if daemon == "" {
settings.NginxSettings.RestartCmd =
fmt.Sprintf("start-stop-daemon --stop --quiet --oknodo --retry=TERM/30/KILL/5"+
" --pidfile %s && nginx", pidPath)
return
}
settings.NginxSettings.RestartCmd =
fmt.Sprintf("start-stop-daemon --start --quiet --pidfile %s --exec %s", pidPath, daemon)
}
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"app": cSettings.AppSettings,
"server": cSettings.ServerSettings, "server": cSettings.ServerSettings,
"nginx": settings.NginxSettings, "database": settings.DatabaseSettings,
"openai": settings.OpenAISettings,
"logrotate": settings.LogrotateSettings,
"auth": settings.AuthSettings, "auth": settings.AuthSettings,
"casdoor": settings.CasdoorSettings,
"cert": settings.CertSettings,
"http": settings.HTTPSettings,
"logrotate": settings.LogrotateSettings,
"nginx": settings.NginxSettings,
"node": settings.NodeSettings,
"openai": settings.OpenAISettings,
"terminal": settings.TerminalSettings,
"webauthn": settings.WebAuthnSettings,
}) })
} }
func SaveSettings(c *gin.Context) { func SaveSettings(c *gin.Context) {
var json struct { var json struct {
Server cSettings.Server `json:"server"` Auth settings.Auth `json:"auth"`
Nginx settings.Nginx `json:"nginx"` Cert settings.Cert `json:"cert"`
Http settings.HTTP `json:"http"`
Node settings.Node `json:"node"`
Openai settings.OpenAI `json:"openai"` Openai settings.OpenAI `json:"openai"`
Logrotate settings.Logrotate `json:"logrotate"` Logrotate settings.Logrotate `json:"logrotate"`
Auth settings.Auth `json:"auth"`
} }
if !api.BindAndValid(c, &json) { if !api.BindAndValid(c, &json) {
@ -46,11 +77,12 @@ func SaveSettings(c *gin.Context) {
go cron.RestartLogrotate() go cron.RestartLogrotate()
} }
cSettings.ProtectedFill(cSettings.ServerSettings, &json.Server) cSettings.ProtectedFill(settings.AuthSettings, &json.Auth)
cSettings.ProtectedFill(settings.NginxSettings, &json.Nginx) cSettings.ProtectedFill(settings.CertSettings, &json.Cert)
cSettings.ProtectedFill(settings.HTTPSettings, &json.Http)
cSettings.ProtectedFill(settings.NodeSettings, &json.Node)
cSettings.ProtectedFill(settings.OpenAISettings, &json.Openai) cSettings.ProtectedFill(settings.OpenAISettings, &json.Openai)
cSettings.ProtectedFill(settings.LogrotateSettings, &json.Logrotate) cSettings.ProtectedFill(settings.LogrotateSettings, &json.Logrotate)
cSettings.ProtectedFill(settings.AuthSettings, &json.Auth)
err := settings.Save() err := settings.Save()
if err != nil { if err != nil {

View file

@ -1,16 +1,114 @@
import http from '@/lib/http' import http from '@/lib/http'
export interface AppSettings {
page_size: number
jwt_secret: string
}
export interface ServerSettings {
host: string
port: number
run_mode: 'debug' | 'release'
}
export interface DatabaseSettings {
name: string
}
export interface AuthSettings {
ip_white_list: string[]
ban_threshold_minutes: number
max_attempts: number
}
export interface CasdoorSettings {
endpoint: string
client_id: string
client_secret: string
certificate_path: string
organization: string
application: string
redirect_uri: string
}
export interface CertSettings {
email: string
ca_dir: string
renewal_interval: number
recursive_nameservers: string[]
http_challenge_port: string
}
export interface HTTPSettings {
github_proxy: string
insecure_skip_verify: boolean
}
export interface LogrotateSettings {
enabled: boolean
cmd: string
interval: number
}
export interface NginxSettings {
access_log_path: string
error_log_path: string
config_dir: string
log_dir_white_list: string[]
pid_path: string
reload_cmd: string
restart_cmd: string
}
export interface NodeSettings {
name: string
secret: string
}
export interface OpenaiSettings {
model: string
base_url: string
proxy: string
token: string
}
export interface TerminalSettings {
start_cmd: string
}
export interface WebauthnSettings {
rp_display_name: string
rpid: string
rp_origins: string[]
}
export interface BannedIP { export interface BannedIP {
ip: string ip: string
attempts: number attempts: number
expired_at: string expired_at: string
} }
export interface Settings {
app: AppSettings
server: ServerSettings
database: DatabaseSettings
auth: AuthSettings
casdoor: CasdoorSettings
cert: CertSettings
http: HTTPSettings
logrotate: LogrotateSettings
nginx: NginxSettings
node: NodeSettings
openai: OpenaiSettings
terminal: TerminalSettings
webauthn: WebauthnSettings
}
const settings = { const settings = {
get<T>(): Promise<T> { get(): Promise<Settings> {
return http.get('/settings') return http.get('/settings')
}, },
save<T>(data: T) { save(data: Settings) {
return http.post('/settings', data) return http.post('/settings', data)
}, },
get_server_name(): Promise<{ name: string }> { get_server_name(): Promise<{ name: string }> {

View file

@ -3,10 +3,9 @@ import { message } from 'ant-design-vue'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import PasskeyRegistration from './components/Passkey.vue' import PasskeyRegistration from './components/Passkey.vue'
import type { BannedIP } from '@/api/settings' import type { BannedIP, Settings } from '@/api/settings'
import setting from '@/api/settings' import setting from '@/api/settings'
import type { customRender } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer' import type { customRender } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
import type { Settings } from '@/views/preference/typedef'
import TOTP from '@/views/preference/components/TOTP.vue' import TOTP from '@/views/preference/components/TOTP.vue'
const data: Settings = inject('data') as Settings const data: Settings = inject('data') as Settings
@ -62,13 +61,41 @@ function removeBannedIP(ip: string) {
<h2> <h2>
{{ $gettext('Authentication Settings') }} {{ $gettext('Authentication Settings') }}
</h2> </h2>
<AAlert <div
v-if="data.webauthn.rpid
&& data.webauthn.rp_display_name
&& data.webauthn.rp_origins?.length > 0"
class="mb-4" class="mb-4"
:message="$gettext('Tips')" >
:description="$gettext('If the number of login failed attempts from a ip reach the max attempts in ban threshold minutes,' <h3>
+ ' the ip will be banned for a period of time.')" {{ $gettext('Webauthn') }}
type="info" </h3>
/> <div class="mb-4">
<h4>
{{ $gettext('RPID') }}
</h4>
<p>{{ data.webauthn.rpid }}</p>
</div>
<div class="mb-4">
<h4>
{{ $gettext('RP Display Name') }}
</h4>
<p>{{ data.webauthn.rp_display_name }}</p>
</div>
<div>
<h4>
{{ $gettext('RP Origins') }}
</h4>
<div
v-for="origin in data.webauthn.rp_origins"
:key="origin"
class="mb-4"
>
{{ origin }}
</div>
</div>
</div>
<h3>{{ $gettext('Throttle') }}</h3>
<AForm <AForm
layout="horizontal" layout="horizontal"
style="width:90%;max-width: 500px" style="width:90%;max-width: 500px"
@ -86,7 +113,14 @@ function removeBannedIP(ip: string) {
/> />
</AFormItem> </AFormItem>
</AForm> </AForm>
<h3> <AAlert
class="mb-6"
:message="$gettext('Tips')"
:description="$gettext('If the number of login failed attempts from a ip reach the max attempts in ban threshold minutes,'
+ ' the ip will be banned for a period of time.')"
type="info"
/>
<h3 class="mb-4">
{{ $gettext('Banned IPs') }} {{ $gettext('Banned IPs') }}
</h3> </h3>
<div class="mb-6"> <div class="mb-6">

View file

@ -1,7 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import Draggable from 'vuedraggable' import type { Settings } from '@/api/settings'
import { DeleteOutlined, HolderOutlined } from '@ant-design/icons-vue'
import type { Settings } from '@/views/preference/typedef'
import SensitiveString from '@/components/SensitiveString/SensitiveString.vue' import SensitiveString from '@/components/SensitiveString/SensitiveString.vue'
const data: Settings = inject('data') as Settings const data: Settings = inject('data') as Settings
@ -11,116 +9,43 @@ const errors: Record<string, Record<string, string>> = inject('errors') as Recor
<template> <template>
<AForm layout="vertical"> <AForm layout="vertical">
<AFormItem :label="$gettext('HTTP Host')"> <AFormItem :label="$gettext('HTTP Host')">
<p>{{ data.server.http_host }}</p> <p>{{ data.server.host }}</p>
</AFormItem> </AFormItem>
<AFormItem :label="$gettext('HTTP Port')"> <AFormItem :label="$gettext('HTTP Port')">
<p>{{ data.server.http_port }}</p> <p>{{ data.server.port }}</p>
</AFormItem> </AFormItem>
<AFormItem :label="$gettext('Run Mode')"> <AFormItem :label="$gettext('Run Mode')">
<p>{{ data.server.run_mode }}</p> <p>{{ data.server.run_mode }}</p>
</AFormItem> </AFormItem>
<AFormItem :label="$gettext('Jwt Secret')"> <AFormItem :label="$gettext('Jwt Secret')">
<SensitiveString :value="data.server.jwt_secret" /> <SensitiveString :value="data.app.jwt_secret" />
</AFormItem> </AFormItem>
<AFormItem :label="$gettext('Node Secret')"> <AFormItem :label="$gettext('Node Secret')">
<SensitiveString :value="data.server.node_secret" /> <SensitiveString :value="data.node.secret" />
</AFormItem> </AFormItem>
<AFormItem :label="$gettext('Terminal Start Command')"> <AFormItem :label="$gettext('Terminal Start Command')">
<p>{{ data.server.start_cmd }}</p> <p>{{ data.terminal.start_cmd }}</p>
</AFormItem>
<AFormItem :label="$gettext('HTTP Challenge Port')">
<AInputNumber v-model:value="data.server.http_challenge_port" />
</AFormItem> </AFormItem>
<AFormItem <AFormItem
:label="$gettext('Github Proxy')" :label="$gettext('Github Proxy')"
:validate-status="errors?.server?.github_proxy ? 'error' : ''" :validate-status="errors?.http?.github_proxy ? 'error' : ''"
:help="errors?.server?.github_proxy === 'url' :help="errors?.http?.github_proxy === 'url'
? $gettext('The url is invalid') ? $gettext('The url is invalid')
: ''" : ''"
> >
<AInput <AInput
v-model:value="data.server.github_proxy" v-model:value="data.http.github_proxy"
:placeholder="$gettext('For Chinese user: https://mirror.ghproxy.com/')" :placeholder="$gettext('For Chinese user: https://mirror.ghproxy.com/')"
/> />
</AFormItem> </AFormItem>
<AFormItem <AFormItem
:label="$gettext('CADir')" :label="$gettext('Node name')"
:validate-status="errors?.server?.ca_dir ? 'error' : ''" :validate-status="errors?.node?.name ? 'error' : ''"
:help="errors?.server?.ca_dir === 'url' :help="errors?.node?.name.includes('safety_text')
? $gettext('The url is invalid') ? $gettext('The node name should only contain letters, unicode, numbers, hyphens, dashes, and dots.')
: ''" : $gettext('Customize the name of local node to be displayed in the environment indicator.')"
> >
<AInput v-model:value="data.server.ca_dir" /> <AInput v-model:value="data.node.name" />
</AFormItem>
<AFormItem :label="$gettext('Certificate Renewal Interval')">
<AInputNumber
v-model:value="data.server.cert_renewal_interval"
:min="7"
:max="21"
:addon-after="$gettext('Days')"
/>
</AFormItem>
<AFormItem
:help="$gettext('Set the recursive nameservers to override the systems nameservers '
+ 'for the step of DNS challenge.')"
>
<template #label>
{{ $gettext('Recursive Nameservers') }}
<AButton
type="link"
@click="data.server.recursive_nameservers.push('')"
>
{{ $gettext('Add') }}
</AButton>
</template>
<Draggable
:list="data.server.recursive_nameservers"
item-key="name"
class="list-group"
ghost-class="ghost"
handle=".anticon-holder"
>
<template #item="{ index }">
<ARow>
<ACol :span="2">
<HolderOutlined class="p-2" />
</ACol>
<ACol :span="20">
<AInput
v-model:value="data.server.recursive_nameservers[index]"
:status="errors?.server?.recursive_nameservers?.[index] ? 'error' : undefined"
placeholder="8.8.8.8:53"
class="mb-4"
/>
</ACol>
<ACol :span="2">
<APopconfirm
:title="$gettext('Are you sure you want to remove this item?')"
:ok-text="$gettext('Yes')"
:cancel-text="$gettext('No')"
@confirm="data.server.recursive_nameservers.splice(index, 1)"
>
<AButton
type="link"
danger
>
<DeleteOutlined />
</AButton>
</APopconfirm>
</ACol>
</ARow>
</template>
</Draggable>
</AFormItem>
<AFormItem
:label="$gettext('Server Name')"
:validate-status="errors?.server?.name ? 'error' : ''"
:help="errors?.server?.name.includes('safety_text')
? $gettext('The server name should only contain letters, unicode, numbers, hyphens, dashes, and dots.')
: $gettext('Customize the name of local server to be displayed in the environment indicator.')"
>
<AInput v-model:value="data.server.name" />
</AFormItem> </AFormItem>
</AForm> </AForm>
</template> </template>

View file

@ -0,0 +1,90 @@
<script setup lang="ts">
import Draggable from 'vuedraggable'
import { DeleteOutlined, HolderOutlined } from '@ant-design/icons-vue'
import type { Settings } from '@/api/settings'
const data: Settings = inject('data') as Settings
const errors: Record<string, Record<string, string>> = inject('errors') as Record<string, Record<string, string>>
</script>
<template>
<AForm layout="vertical">
<AFormItem :label="$gettext('HTTP Challenge Port')">
<AInputNumber v-model:value="data.cert.http_challenge_port" />
</AFormItem>
<AFormItem
:label="$gettext('CADir')"
:validate-status="errors?.cert?.ca_dir ? 'error' : ''"
:help="errors?.cert?.ca_dir === 'url'
? $gettext('The url is invalid')
: ''"
>
<AInput v-model:value="data.cert.ca_dir" />
</AFormItem>
<AFormItem :label="$gettext('Certificate Renewal Interval')">
<AInputNumber
v-model:value="data.cert.renewal_interval"
:min="7"
:max="21"
:addon-after="$gettext('Days')"
/>
</AFormItem>
<AFormItem
:help="$gettext('Set the recursive nameservers to override the systems nameservers '
+ 'for the step of DNS challenge.')"
>
<template #label>
{{ $gettext('Recursive Nameservers') }}
<AButton
type="link"
@click="data.cert.recursive_nameservers.push('')"
>
{{ $gettext('Add') }}
</AButton>
</template>
<Draggable
:list="data.cert.recursive_nameservers"
item-key="name"
class="list-group"
ghost-class="ghost"
handle=".anticon-holder"
>
<template #item="{ index }">
<ARow>
<ACol :span="2">
<HolderOutlined class="p-2" />
</ACol>
<ACol :span="20">
<AInput
v-model:value="data.cert.recursive_nameservers[index]"
:status="errors?.cert?.recursive_nameservers?.[index] ? 'error' : undefined"
placeholder="8.8.8.8:53"
class="mb-4"
/>
</ACol>
<ACol :span="2">
<APopconfirm
:title="$gettext('Are you sure you want to remove this item?')"
:ok-text="$gettext('Yes')"
:cancel-text="$gettext('No')"
@confirm="data.cert.recursive_nameservers.splice(index, 1)"
>
<AButton
type="link"
danger
>
<DeleteOutlined />
</AButton>
</APopconfirm>
</ACol>
</ARow>
</template>
</Draggable>
</AFormItem>
</AForm>
</template>
<style lang="less" scoped>
</style>

View file

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Settings } from '@/views/preference/typedef' import type { Settings } from '@/api/settings'
const data: Settings = inject('data')! const data: Settings = inject('data')!
</script> </script>

View file

@ -1,30 +1,38 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Settings } from '@/views/preference/typedef' import type { Settings } from '@/api/settings'
const data: Settings = inject('data')! const data: Settings = inject('data')!
const errors: Record<string, Record<string, string>> = inject('errors') as Record<string, Record<string, string>>
</script> </script>
<template> <template>
<AForm layout="vertical"> <AForm layout="vertical">
<AFormItem <AFormItem :label="$gettext('Nginx Access Log Path')">
:label="$gettext('Nginx Access Log Path')"
:validate-status="errors?.nginx?.access_log_path ? 'error' : ''"
:help="errors?.nginx?.access_log_path === 'file'
? $gettext('File not found')
: ''"
>
{{ data.nginx.access_log_path }} {{ data.nginx.access_log_path }}
</AFormItem> </AFormItem>
<AFormItem <AFormItem :label="$gettext('Nginx Error Log Path')">
:label="$gettext('Nginx Error Log Path')"
:validate-status="errors?.nginx?.error_log_path ? 'error' : ''"
:help="errors?.nginx?.error_log_path === 'file'
? $gettext('File not found')
: ''"
>
{{ data.nginx.error_log_path }} {{ data.nginx.error_log_path }}
</AFormItem> </AFormItem>
<AFormItem :label="$gettext('Nginx Configurations Directory')">
{{ data.nginx.config_dir }}
</AFormItem>
<AFormItem :label="$gettext('Nginx Log Directory Whitelist')">
<div
v-for="dir in data.nginx.log_dir_white_list"
:key="dir"
class="mb-2"
>
{{ dir }}
</div>
</AFormItem>
<AFormItem :label="$gettext('Nginx PID Path')">
{{ data.nginx.pid_path }}
</AFormItem>
<AFormItem :label="$gettext('Nginx Reload Command')">
{{ data.nginx.reload_cmd }}
</AFormItem>
<AFormItem :label="$gettext('Nginx Restart Command')">
{{ data.nginx.restart_cmd }}
</AFormItem>
</AForm> </AForm>
</template> </template>

View file

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Settings } from '@/views/preference/typedef' import type { Settings } from '@/api/settings'
const data: Settings = inject('data')! const data: Settings = inject('data')!
const errors: Record<string, Record<string, string>> = inject('errors') as Record<string, Record<string, string>> const errors: Record<string, Record<string, string>> = inject('errors') as Record<string, Record<string, string>>

View file

@ -7,55 +7,86 @@ import settings from '@/api/settings'
import BasicSettings from '@/views/preference/BasicSettings.vue' import BasicSettings from '@/views/preference/BasicSettings.vue'
import OpenAISettings from '@/views/preference/OpenAISettings.vue' import OpenAISettings from '@/views/preference/OpenAISettings.vue'
import NginxSettings from '@/views/preference/NginxSettings.vue' import NginxSettings from '@/views/preference/NginxSettings.vue'
import type { Settings } from '@/views/preference/typedef' import type { Settings } from '@/api/settings'
import LogrotateSettings from '@/views/preference/LogrotateSettings.vue' import LogrotateSettings from '@/views/preference/LogrotateSettings.vue'
import { useSettingsStore } from '@/pinia' import { useSettingsStore } from '@/pinia'
import AuthSettings from '@/views/preference/AuthSettings.vue' import AuthSettings from '@/views/preference/AuthSettings.vue'
import use2FAModal from '@/components/TwoFA/use2FAModal' import use2FAModal from '@/components/TwoFA/use2FAModal'
import CertSettings from '@/views/preference/CertSettings.vue'
const data = ref<Settings>({ const data = ref<Settings>({
server: { app: {
http_host: '0.0.0.0', page_size: 10,
http_port: '9000',
run_mode: 'debug',
jwt_secret: '', jwt_secret: '',
start_cmd: '', },
email: '', server: {
http_challenge_port: '9180', host: '0.0.0.0',
github_proxy: '', port: 9000,
ca_dir: '', run_mode: 'debug',
node_secret: '', },
cert_renewal_interval: 7, database: {
recursive_nameservers: [],
name: '', name: '',
}, },
auth: {
ip_white_list: [],
ban_threshold_minutes: 10,
max_attempts: 10,
},
casdoor: {
endpoint: '',
client_id: '',
client_secret: '',
certificate_path: '',
organization: '',
application: '',
redirect_uri: '',
},
cert: {
email: '',
ca_dir: '',
renewal_interval: 7,
recursive_nameservers: [],
http_challenge_port: '9180',
},
http: {
github_proxy: '',
insecure_skip_verify: false,
},
logrotate: {
enabled: false,
cmd: '',
interval: 1440,
},
nginx: { nginx: {
access_log_path: '', access_log_path: '',
error_log_path: '', error_log_path: '',
config_dir: '', config_dir: '',
log_dir_white_list: [],
pid_path: '', pid_path: '',
reload_cmd: '', reload_cmd: '',
restart_cmd: '', restart_cmd: '',
}, },
node: {
name: '',
secret: '',
},
openai: { openai: {
model: '', model: '',
base_url: '', base_url: '',
proxy: '', proxy: '',
token: '', token: '',
}, },
logrotate: { terminal: {
enabled: false, start_cmd: '',
cmd: '',
interval: 1440,
}, },
auth: { webauthn: {
ip_white_list: [], rp_display_name: '',
ban_threshold_minutes: 10, rpid: '',
max_attempts: 10, rp_origins: [],
}, },
}) })
settings.get<Settings>().then(r => { settings.get().then(r => {
data.value = r data.value = r
}) })
@ -66,7 +97,7 @@ const refAuthSettings = ref()
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.cert.http_challenge_port = data.value.cert.http_challenge_port.toString()
const otpModal = use2FAModal() const otpModal = use2FAModal()
@ -123,6 +154,12 @@ onMounted(() => {
> >
<AuthSettings ref="refAuthSettings" /> <AuthSettings ref="refAuthSettings" />
</ATabPane> </ATabPane>
<ATabPane
key="cert"
:tab="$gettext('Cert')"
>
<CertSettings />
</ATabPane>
<ATabPane <ATabPane
key="nginx" key="nginx"
:tab="$gettext('Nginx')" :tab="$gettext('Nginx')"

View file

@ -1,41 +0,0 @@
export interface Settings {
server: {
http_host: string
http_port: string
run_mode: string
jwt_secret: string
node_secret: string
start_cmd: string
http_challenge_port: string
github_proxy: string
email: string
ca_dir: string
cert_renewal_interval: number
recursive_nameservers: string[]
name: string
}
nginx: {
access_log_path: string
error_log_path: string
config_dir: string
pid_path: string
reload_cmd: string
restart_cmd: string
}
openai: {
model: string
base_url: string
proxy: string
token: string
}
logrotate: {
enabled: boolean
cmd: string
interval: number
}
auth: {
ip_white_list: string[]
ban_threshold_minutes: number
max_attempts: number
}
}

View file

@ -1,17 +1,21 @@
version: '3' version: '3'
services: services:
nginx-demo-1: nginx-demo-1:
image: uozi/nginx-ui-demo:latest image: uozi/nginx-ui-demo:latest
restart: always restart: always
networks: networks:
- nginx-ui-network - nginx-ui-network
ports: entrypoint:
- "9003:80" - NGINX_UI_NODE_DEMO=true
ports:
- "9003:80"
nginx-demo-2: nginx-demo-2:
image: uozi/nginx-ui-demo:latest image: uozi/nginx-ui-demo:latest
restart: always restart: always
networks: entrypoint:
- nginx-ui-network - NGINX_UI_NODE_DEMO=true
networks:
- nginx-ui-network
networks: networks:
nginx-ui-network: nginx-ui-network:

View file

@ -2,8 +2,8 @@ package nginx
import ( import (
"github.com/0xJacky/Nginx-UI/internal/helper" "github.com/0xJacky/Nginx-UI/internal/helper"
"github.com/uozi-tech/cosy/logger"
"github.com/0xJacky/Nginx-UI/settings" "github.com/0xJacky/Nginx-UI/settings"
"github.com/uozi-tech/cosy/logger"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"regexp" "regexp"

View file

@ -5,7 +5,7 @@ import "github.com/go-acme/lego/v4/lego"
type Cert struct { type Cert struct {
Email string `json:"email" protected:"true"` Email string `json:"email" protected:"true"`
CADir string `json:"ca_dir" binding:"omitempty,url"` CADir string `json:"ca_dir" binding:"omitempty,url"`
RenewalInterval int `json:"cert_renewal_interval" binding:"min=7,max=21"` RenewalInterval int `json:"renewal_interval" binding:"min=7,max=21"`
RecursiveNameservers []string `json:"recursive_nameservers" binding:"omitempty,dive,hostname_port"` RecursiveNameservers []string `json:"recursive_nameservers" binding:"omitempty,dive,hostname_port"`
HTTPChallengePort string `json:"http_challenge_port"` HTTPChallengePort string `json:"http_challenge_port"`
} }

View file

@ -3,7 +3,7 @@ package settings
import "github.com/uozi-tech/cosy/settings" import "github.com/uozi-tech/cosy/settings"
type Cluster struct { type Cluster struct {
Node []string `ini:",,allowshadow"` Node []string `json:"node" ini:",,allowshadow"`
} }
var ClusterSettings = &Cluster{ var ClusterSettings = &Cluster{

View file

@ -3,7 +3,7 @@ package settings
import "crypto/md5" import "crypto/md5"
type Crypto struct { type Crypto struct {
Secret string Secret string `json:"secret"`
} }
var CryptoSettings = &Crypto{} var CryptoSettings = &Crypto{}

View file

@ -1,7 +1,7 @@
package settings package settings
type Database struct { type Database struct {
Name string Name string `json:"name"`
} }
var DatabaseSettings = &Database{ var DatabaseSettings = &Database{

View file

@ -1,9 +1,9 @@
package settings package settings
type WebAuthn struct { type WebAuthn struct {
RPDisplayName string RPDisplayName string `json:"rp_display_name"`
RPID string RPID string `json:"rpid"`
RPOrigins []string RPOrigins []string `json:"rp_origins"`
} }
var WebAuthnSettings = &WebAuthn{} var WebAuthnSettings = &WebAuthn{}