mirror of
https://github.com/tnb-labs/panel.git
synced 2026-08-31 01:12:17 +08:00
feat: 网站默认设置
This commit is contained in:
+16
-14
@@ -9,20 +9,22 @@ import (
|
||||
type SettingKey string
|
||||
|
||||
const (
|
||||
SettingKeyName SettingKey = "name"
|
||||
SettingKeyVersion SettingKey = "version"
|
||||
SettingKeyChannel SettingKey = "channel"
|
||||
SettingKeyMonitor SettingKey = "monitor"
|
||||
SettingKeyMonitorDays SettingKey = "monitor_days"
|
||||
SettingKeyBackupPath SettingKey = "backup_path"
|
||||
SettingKeyWebsitePath SettingKey = "website_path"
|
||||
SettingKeyMySQLRootPassword SettingKey = "mysql_root_password"
|
||||
SettingKeyOfflineMode SettingKey = "offline_mode"
|
||||
SettingKeyAutoUpdate SettingKey = "auto_update"
|
||||
SettingKeyWebserver SettingKey = "webserver"
|
||||
SettingKeyPublicIPs SettingKey = "public_ips"
|
||||
SettingHiddenMenu SettingKey = "hidden_menu"
|
||||
SettingKeyCustomLogo SettingKey = "custom_logo"
|
||||
SettingKeyName SettingKey = "name"
|
||||
SettingKeyVersion SettingKey = "version"
|
||||
SettingKeyChannel SettingKey = "channel"
|
||||
SettingKeyMonitor SettingKey = "monitor"
|
||||
SettingKeyMonitorDays SettingKey = "monitor_days"
|
||||
SettingKeyBackupPath SettingKey = "backup_path"
|
||||
SettingKeyWebsitePath SettingKey = "website_path"
|
||||
SettingKeyWebsiteTLSVersions SettingKey = "website_tls_versions"
|
||||
SettingKeyWebsiteCipherSuites SettingKey = "website_tls_cipher_suites"
|
||||
SettingKeyMySQLRootPassword SettingKey = "mysql_root_password"
|
||||
SettingKeyOfflineMode SettingKey = "offline_mode"
|
||||
SettingKeyAutoUpdate SettingKey = "auto_update"
|
||||
SettingKeyWebserver SettingKey = "webserver"
|
||||
SettingKeyPublicIPs SettingKey = "public_ips"
|
||||
SettingHiddenMenu SettingKey = "hidden_menu"
|
||||
SettingKeyCustomLogo SettingKey = "custom_logo"
|
||||
)
|
||||
|
||||
type Setting struct {
|
||||
|
||||
+30
-12
@@ -84,6 +84,17 @@ func (r *websiteRepo) UpdateDefaultConfig(req *request.WebsiteDefaultConfig) err
|
||||
if err := io.Write(filepath.Join(app.Root, "server/nginx/html/stop.html"), req.Stop, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.NotFound != "" {
|
||||
if err := io.Write(filepath.Join(app.Root, "server/nginx/html/404.html"), req.NotFound, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := r.setting.SetSlice(biz.SettingKeyWebsiteTLSVersions, req.TLSVersions); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.setting.Set(biz.SettingKeyWebsiteCipherSuites, req.CipherSuites); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.reloadWebServer()
|
||||
}
|
||||
@@ -342,17 +353,22 @@ location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.env) {
|
||||
return nil, err
|
||||
}
|
||||
var notFound []byte
|
||||
switch app.Locale {
|
||||
case "zh_CN":
|
||||
notFound, err = embed.WebsiteFS.ReadFile(filepath.Join("website", "404_zh_CN.html"))
|
||||
case "zh_TW":
|
||||
notFound, err = embed.WebsiteFS.ReadFile(filepath.Join("website", "404_zh_TW.html"))
|
||||
default:
|
||||
notFound, err = embed.WebsiteFS.ReadFile(filepath.Join("website", "404.html"))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.New(r.t.Get("failed to get 404 template file: %v", err))
|
||||
|
||||
// 如果存在自定义 404 页面,则使用自定义的
|
||||
// TODO 需要兼容 Apache
|
||||
if io.Exists(filepath.Join(app.Root, "server/nginx/html/404.html")) {
|
||||
notFound, _ = os.ReadFile(filepath.Join(app.Root, "server/nginx/html/404.html"))
|
||||
} else {
|
||||
switch app.Locale {
|
||||
case "zh_CN":
|
||||
notFound, _ = embed.WebsiteFS.ReadFile(filepath.Join("website", "404_zh_CN.html"))
|
||||
case "zh_TW":
|
||||
notFound, _ = embed.WebsiteFS.ReadFile(filepath.Join("website", "404_zh_TW.html"))
|
||||
default:
|
||||
notFound, _ = embed.WebsiteFS.ReadFile(filepath.Join("website", "404.html"))
|
||||
}
|
||||
}
|
||||
|
||||
if err = io.Write(filepath.Join(req.Path, "404.html"), string(notFound), 0644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -487,11 +503,13 @@ func (r *websiteRepo) Update(req *request.WebsiteUpdate) error {
|
||||
break
|
||||
}
|
||||
}
|
||||
defaultTLSVersions, _ := r.setting.GetSlice(biz.SettingKeyWebsiteTLSVersions)
|
||||
defaultCipherSuites, _ := r.setting.Get(biz.SettingKeyWebsiteCipherSuites)
|
||||
if err = vhost.SetSSLConfig(&webservertypes.SSLConfig{
|
||||
Cert: certPath,
|
||||
Key: keyPath,
|
||||
Protocols: lo.If(len(req.SSLProtocols) > 0, req.SSLProtocols).Else([]string{"TLSv1.2", "TLSv1.3"}),
|
||||
Ciphers: lo.If(req.SSLCiphers != "", req.SSLCiphers).Else("ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305"),
|
||||
Protocols: lo.If(len(req.SSLProtocols) > 0, req.SSLProtocols).Else(defaultTLSVersions),
|
||||
Ciphers: lo.If(req.SSLCiphers != "", req.SSLCiphers).Else(defaultCipherSuites),
|
||||
HSTS: req.HSTS,
|
||||
OCSP: req.OCSP,
|
||||
HTTPRedirect: req.HTTPRedirect,
|
||||
|
||||
@@ -5,8 +5,11 @@ import (
|
||||
)
|
||||
|
||||
type WebsiteDefaultConfig struct {
|
||||
Index string `json:"index" form:"index" validate:"required"`
|
||||
Stop string `json:"stop" form:"stop" validate:"required"`
|
||||
Index string `json:"index" form:"index" validate:"required"`
|
||||
Stop string `json:"stop" form:"stop" validate:"required"`
|
||||
NotFound string `json:"not_found" form:"not_found"`
|
||||
TLSVersions []string `json:"tls_versions" form:"tls_versions" validate:"required|isSlice"`
|
||||
CipherSuites string `json:"cipher_suites" form:"cipher_suites" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteList struct {
|
||||
|
||||
@@ -921,6 +921,8 @@ func (s *CliService) Init(ctx context.Context, cmd *cli.Command) error {
|
||||
{Key: biz.SettingKeyMonitorDays, Value: "30"},
|
||||
{Key: biz.SettingKeyBackupPath, Value: filepath.Join(app.Root, "backup")},
|
||||
{Key: biz.SettingKeyWebsitePath, Value: filepath.Join(app.Root, "sites")},
|
||||
{Key: biz.SettingKeyWebsiteTLSVersions, Value: `["TLSv1.2","TLSv1.3"]`},
|
||||
{Key: biz.SettingKeyWebsiteCipherSuites, Value: `ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305`},
|
||||
{Key: biz.SettingKeyOfflineMode, Value: "false"},
|
||||
{Key: biz.SettingKeyAutoUpdate, Value: "true"},
|
||||
{Key: biz.SettingHiddenMenu, Value: "[]"},
|
||||
|
||||
+10
-12
@@ -35,20 +35,18 @@ func (s *WebsiteService) GetRewrites(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (s *WebsiteService) GetDefaultConfig(w http.ResponseWriter, r *http.Request) {
|
||||
index, err := io.Read(filepath.Join(app.Root, "server/nginx/html/index.html"))
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
stop, err := io.Read(filepath.Join(app.Root, "server/nginx/html/stop.html"))
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
index, _ := io.Read(filepath.Join(app.Root, "server/nginx/html/index.html"))
|
||||
stop, _ := io.Read(filepath.Join(app.Root, "server/nginx/html/stop.html"))
|
||||
notFound, _ := io.Read(filepath.Join(app.Root, "server/nginx/html/404.html"))
|
||||
tlsVersions, _ := s.settingRepo.GetSlice(biz.SettingKeyWebsiteTLSVersions)
|
||||
cipherSuites, _ := s.settingRepo.Get(biz.SettingKeyWebsiteCipherSuites)
|
||||
|
||||
Success(w, chix.M{
|
||||
"index": index,
|
||||
"stop": stop,
|
||||
"index": index,
|
||||
"stop": stop,
|
||||
"not_found": notFound,
|
||||
"tls_versions": tlsVersions,
|
||||
"cipher_suites": cipherSuites,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,7 @@ export default {
|
||||
// 获取默认配置
|
||||
defaultConfig: (): any => http.Get('/website/default_config'),
|
||||
// 保存默认配置
|
||||
saveDefaultConfig: (index: string, stop: string): any =>
|
||||
http.Post('/website/default_config', { index, stop }),
|
||||
saveDefaultConfig: (data: any): any => http.Post('/website/default_config', data),
|
||||
// 网站配置
|
||||
config: (id: number): any => http.Get('/website/' + id),
|
||||
// 保存网站配置
|
||||
|
||||
@@ -323,6 +323,26 @@ const hasArg = (args: string[], arg: string) => {
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-form v-if="setting.ssl">
|
||||
<n-form-item :label="$gettext('TLS Version')">
|
||||
<n-select
|
||||
v-model:value="setting.ssl_protocols"
|
||||
:options="[
|
||||
{ label: 'TLS 1.0', value: 'TLSv1.0' },
|
||||
{ label: 'TLS 1.1', value: 'TLSv1.1' },
|
||||
{ label: 'TLS 1.2', value: 'TLSv1.2' },
|
||||
{ label: 'TLS 1.3', value: 'TLSv1.3' }
|
||||
]"
|
||||
multiple
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item :label="$gettext('Cipher Suites')">
|
||||
<n-input
|
||||
type="textarea"
|
||||
v-model:value="setting.ssl_ciphers"
|
||||
:placeholder="$gettext('Enter the cipher suite, leave blank to reset to default')"
|
||||
rows="4"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item :label="$gettext('Certificate')">
|
||||
<n-input
|
||||
v-model:value="setting.ssl_cert"
|
||||
@@ -355,25 +375,7 @@ const hasArg = (args: string[], arg: string) => {
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<common-editor v-if="setting" v-model:content="setting.rewrite" height="60vh" />
|
||||
</n-flex>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="config" :tab="$gettext('Configuration')">
|
||||
<n-flex vertical>
|
||||
<n-alert type="info" w-full>
|
||||
{{
|
||||
$gettext(
|
||||
'If you modify the original text, other modifications will not take effect after clicking save!'
|
||||
)
|
||||
}}
|
||||
</n-alert>
|
||||
<n-alert type="warning" w-full>
|
||||
{{
|
||||
$gettext(
|
||||
'If you do not understand the configuration rules, please do not modify them arbitrarily, otherwise it may cause the website to be inaccessible or panel function abnormalities! If you have already encountered a problem, try resetting the configuration!'
|
||||
)
|
||||
}}
|
||||
</n-alert>
|
||||
<common-editor v-if="setting" v-model:value="setting.rewrite" height="60vh" />
|
||||
</n-flex>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="log" :tab="$gettext('Access Log')">
|
||||
|
||||
@@ -6,51 +6,45 @@ const { $gettext } = useGettext()
|
||||
|
||||
const currentTab = ref('default-page')
|
||||
|
||||
const defaultPageModel = ref({
|
||||
index: '',
|
||||
not_found: '',
|
||||
stop: ''
|
||||
const { data: model } = useRequest(website.defaultConfig, {
|
||||
initialData: {
|
||||
index: '',
|
||||
stop: '',
|
||||
not_found: '',
|
||||
tls_versions: ['TLSv1.2', 'TLSv1.3'],
|
||||
cipher_suites: ''
|
||||
}
|
||||
})
|
||||
|
||||
const defaultSettingModel = ref({
|
||||
tls_version: ['TLSv1.2', 'TLSv1.3'],
|
||||
cipher_suites: ''
|
||||
})
|
||||
watch(
|
||||
() => model.value.tls_versions,
|
||||
(newVal) => {
|
||||
if (!newVal.includes('TLSv1.1') && !newVal.includes('TLSv1.0')) {
|
||||
// 不包含 TLSv1.0 和 TLSv1.1
|
||||
model.value.cipher_suites =
|
||||
'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305'
|
||||
} else {
|
||||
// 包含 TLSv1.0 或 TLSv1.1
|
||||
model.value.cipher_suites =
|
||||
'@SECLEVEL=0:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const getDefaultPage = async () => {
|
||||
defaultPageModel.value = await website.defaultConfig()
|
||||
}
|
||||
|
||||
const handleSaveDefaultPage = () => {
|
||||
useRequest(
|
||||
website.saveDefaultConfig(defaultPageModel.value.index, defaultPageModel.value.stop)
|
||||
).onSuccess(() => {
|
||||
const handleSave = () => {
|
||||
useRequest(website.saveDefaultConfig(model.value)).onSuccess(() => {
|
||||
window.$message.success($gettext('Modified successfully'))
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDefaultPage()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-tabs v-model:value="currentTab" type="line" placement="left" animated>
|
||||
<n-tab-pane name="default-page" :tab="$gettext('Default Page')">
|
||||
<n-flex vertical>
|
||||
<common-editor v-model:value="defaultPageModel.index" height="60vh" />
|
||||
<common-editor v-model:value="model.index" height="60vh" />
|
||||
<n-flex>
|
||||
<n-button type="primary">
|
||||
{{ $gettext('Save Changes') }}
|
||||
</n-button>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="404-page" :tab="$gettext('404 Page')">
|
||||
<n-flex>
|
||||
<common-editor v-model:value="defaultPageModel.not_found" height="60vh" />
|
||||
<n-flex>
|
||||
<n-button type="primary">
|
||||
<n-button type="primary" @click="handleSave">
|
||||
{{ $gettext('Save Changes') }}
|
||||
</n-button>
|
||||
</n-flex>
|
||||
@@ -58,9 +52,19 @@ onMounted(() => {
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="stop-page" :tab="$gettext('Stop Page')">
|
||||
<n-flex>
|
||||
<common-editor v-model:value="defaultPageModel.stop" height="60vh" />
|
||||
<common-editor v-model:value="model.stop" height="60vh" />
|
||||
<n-flex>
|
||||
<n-button type="primary">
|
||||
<n-button type="primary" @click="handleSave">
|
||||
{{ $gettext('Save Changes') }}
|
||||
</n-button>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="404-page" :tab="$gettext('404 Page')">
|
||||
<n-flex>
|
||||
<common-editor v-model:value="model.not_found" height="60vh" />
|
||||
<n-flex>
|
||||
<n-button type="primary" @click="handleSave">
|
||||
{{ $gettext('Save Changes') }}
|
||||
</n-button>
|
||||
</n-flex>
|
||||
@@ -70,33 +74,49 @@ onMounted(() => {
|
||||
<n-alert type="info">待开发</n-alert>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="default-setting" :tab="$gettext('Default Settings')">
|
||||
<n-form>
|
||||
<n-form-item :label="$gettext('Default TLS Version')">
|
||||
<n-select
|
||||
v-model:value="defaultSettingModel.tls_version"
|
||||
:options="[
|
||||
{ label: 'TLS 1.0', value: 'TLSv1.0' },
|
||||
{ label: 'TLS 1.1', value: 'TLSv1.1' },
|
||||
{ label: 'TLS 1.2', value: 'TLSv1.2' },
|
||||
{ label: 'TLS 1.3', value: 'TLSv1.3' }
|
||||
]"
|
||||
multiple
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item :label="$gettext('Default Cipher Suites')">
|
||||
<n-input
|
||||
type="textarea"
|
||||
v-model:value="defaultSettingModel.cipher_suites"
|
||||
:placeholder="
|
||||
$gettext('Enter the default cipher suite, leave blank to reset to default')
|
||||
"
|
||||
rows="4"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-button type="primary">
|
||||
{{ $gettext('Save Changes') }}
|
||||
</n-button>
|
||||
</n-form>
|
||||
<n-flex vertical>
|
||||
<n-alert type="info">
|
||||
{{
|
||||
$gettext(
|
||||
'Modifying the default TLS version and cipher suites will affect all newly created websites. Existing websites will not be affected.'
|
||||
)
|
||||
}}
|
||||
</n-alert>
|
||||
<n-alert type="warning">
|
||||
{{
|
||||
$gettext(
|
||||
'Please adjust the settings carefully, improper configuration may lead to website inaccessible.'
|
||||
)
|
||||
}}
|
||||
</n-alert>
|
||||
<n-form>
|
||||
<n-form-item :label="$gettext('Default TLS Version')">
|
||||
<n-select
|
||||
v-model:value="model.tls_versions"
|
||||
:options="[
|
||||
{ label: 'TLS 1.0', value: 'TLSv1.0' },
|
||||
{ label: 'TLS 1.1', value: 'TLSv1.1' },
|
||||
{ label: 'TLS 1.2', value: 'TLSv1.2' },
|
||||
{ label: 'TLS 1.3', value: 'TLSv1.3' }
|
||||
]"
|
||||
multiple
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item :label="$gettext('Default Cipher Suites')">
|
||||
<n-input
|
||||
type="textarea"
|
||||
v-model:value="model.cipher_suites"
|
||||
:placeholder="
|
||||
$gettext('Enter the default cipher suite, leave blank to reset to default')
|
||||
"
|
||||
rows="4"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-button type="primary" @click="handleSave">
|
||||
{{ $gettext('Save Changes') }}
|
||||
</n-button>
|
||||
</n-form>
|
||||
</n-flex>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user