mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-10 18:05:48 +02:00
refactor(preference): show more settings
This commit is contained in:
parent
59947a35db
commit
a0c005f314
17 changed files with 400 additions and 213 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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 }> {
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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>
|
||||||
|
|
90
app/src/views/preference/CertSettings.vue
Normal file
90
app/src/views/preference/CertSettings.vue
Normal 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>
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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>>
|
||||||
|
|
|
@ -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')"
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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:
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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{}
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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{}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue