feat(template): add selection box; add scheme and host field for reverse_proxy #608
|
@ -13,7 +13,7 @@ bin = "tmp/main"
|
|||
# Customize binary.
|
||||
full_bin = "APP_ENV=dev APP_USER=air ./tmp/main"
|
||||
# Watch these filename extensions.
|
||||
include_ext = ["go", "tpl", "tmpl", "html", "toml", "po"]
|
||||
include_ext = ["go", "tpl", "tmpl", "html", "toml", "po", "conf"]
|
||||
# Ignore these filename extensions or directories.
|
||||
exclude_dir = ["assets", "tmp", "vendor", "app/node_modules", "upload", "docs", "resources", ".idea"]
|
||||
# Watch these directories if you specified.
|
||||
|
|
|
@ -66,7 +66,6 @@ func GetTemplateConfList(c *gin.Context) {
|
|||
|
||||
func GetTemplateBlockList(c *gin.Context) {
|
||||
configList, err := template.GetTemplateList("block")
|
||||
|
||||
if err != nil {
|
||||
api.ErrHandler(c, err)
|
||||
return
|
||||
|
|
|
@ -4,19 +4,18 @@ import type { NgxDirective, NgxLocation, NgxServer } from '@/api/ngx'
|
|||
|
||||
export interface Variable {
|
||||
type?: string
|
||||
name?: { [key: string]: string }
|
||||
name?: Record<string, string>
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value?: any
|
||||
mask?: Record<string, Record<string, string>>
|
||||
}
|
||||
|
||||
export interface Template extends NgxServer {
|
||||
name: string
|
||||
description: { [key: string]: string }
|
||||
description: Record<string, string>
|
||||
author: string
|
||||
filename: string
|
||||
variables: {
|
||||
[key: string]: Variable
|
||||
}
|
||||
variables: Record<string, Variable>
|
||||
custom: string
|
||||
locations?: NgxLocation[]
|
||||
directives?: NgxDirective[]
|
||||
|
|
|
@ -13,6 +13,7 @@ const step = ref(0)
|
|||
const visible = ref(false)
|
||||
const data = ref({}) as Ref<AutoCertOptions>
|
||||
const issuing_cert = ref(false)
|
||||
const domain = ref('')
|
||||
|
||||
provide('issuing_cert', issuing_cert)
|
||||
function open() {
|
||||
|
@ -22,6 +23,7 @@ function open() {
|
|||
challenge_method: 'dns01',
|
||||
key_type: '2048',
|
||||
} as AutoCertOptions
|
||||
domain.value = ''
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
|
@ -32,7 +34,6 @@ const modalVisible = ref(false)
|
|||
const modalClosable = ref(true)
|
||||
|
||||
const refObtainCertLive = ref()
|
||||
const domain = ref('')
|
||||
|
||||
const computedDomain = computed(() => {
|
||||
return `*.${domain.value}`
|
||||
|
|
|
@ -173,4 +173,9 @@ function duplicate(index: number) {
|
|||
.ant-collapse-header {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:deep(.ant-collapse-header-text) {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -119,19 +119,21 @@ provide('ngx_directives', ngx_directives)
|
|||
>
|
||||
<p>{{ $gettext('Author') }}: {{ data.author }}</p>
|
||||
<p>{{ $gettext('Description') }}: {{ trans_description(data) }}</p>
|
||||
<TemplateForm v-model:data="data.variables" />
|
||||
<template v-if="data.custom">
|
||||
<h2>{{ $gettext('Custom') }}</h2>
|
||||
<TemplateForm v-model="data.variables" />
|
||||
<div
|
||||
v-if="data.custom"
|
||||
class="mb-4"
|
||||
>
|
||||
<h3>{{ $gettext('Custom') }}</h3>
|
||||
<CodeEditor
|
||||
v-model:content="data.custom"
|
||||
default-height="150px"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
<DirectiveEditor
|
||||
v-if="data.directives"
|
||||
readonly
|
||||
/>
|
||||
<br>
|
||||
<LocationEditor
|
||||
v-if="data.locations"
|
||||
:locations="data.locations"
|
||||
|
|
|
@ -2,25 +2,8 @@
|
|||
import TemplateFormItem from '@/views/domain/ngx_conf/config_template/TemplateFormItem.vue'
|
||||
import type { Variable } from '@/api/template'
|
||||
|
||||
const props = defineProps<{
|
||||
data: {
|
||||
[key: string]: Variable
|
||||
}
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:data': [data: {
|
||||
[key: string]: Variable
|
||||
}]
|
||||
}>()
|
||||
|
||||
const data = computed({
|
||||
get() {
|
||||
return props.data
|
||||
},
|
||||
set(v) {
|
||||
emit('update:data', v)
|
||||
},
|
||||
const data = defineModel<Record<string, Variable>>({
|
||||
default: () => {},
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -29,7 +12,7 @@ const data = computed({
|
|||
<TemplateFormItem
|
||||
v-for="(_, k) in data"
|
||||
:key="k"
|
||||
v-model:data="data[k]"
|
||||
v-model="data[k]"
|
||||
:name="k.toString()"
|
||||
/>
|
||||
</AForm>
|
||||
|
|
|
@ -4,35 +4,32 @@ import _ from 'lodash'
|
|||
import { useSettingsStore } from '@/pinia'
|
||||
import type { Variable } from '@/api/template'
|
||||
|
||||
const props = defineProps<{
|
||||
data: Variable
|
||||
name: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:data': [data: Variable]
|
||||
}>()
|
||||
|
||||
const data = computed({
|
||||
get() {
|
||||
return props.data
|
||||
},
|
||||
set(v) {
|
||||
emit('update:data', v)
|
||||
},
|
||||
const data = defineModel<Variable>({
|
||||
default: () => {},
|
||||
})
|
||||
|
||||
const { language } = storeToRefs(useSettingsStore())
|
||||
|
||||
const trans_name = computed(() => {
|
||||
return props.data?.name?.[language.value] ?? props.data?.name?.en ?? ''
|
||||
return data.value?.name?.[language.value] ?? data.value?.name?.en ?? ''
|
||||
})
|
||||
|
||||
const build_template = inject('build_template') as () => void
|
||||
|
||||
const value = computed(() => props.data.value)
|
||||
const value = computed(() => data.value.value)
|
||||
|
||||
watch(value, _.throttle(build_template, 500))
|
||||
|
||||
const selectOptions = computed(() => {
|
||||
return Object.keys(data.value?.mask || {}).map(k => {
|
||||
const label = data.value.mask?.[k]?.[language.value] ?? data.value.mask?.[k]?.en ?? ''
|
||||
|
||||
return {
|
||||
label,
|
||||
value: k,
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -41,6 +38,11 @@ watch(value, _.throttle(build_template, 500))
|
|||
v-if="data.type === 'string'"
|
||||
v-model:value="data.value"
|
||||
/>
|
||||
<ASelect
|
||||
v-else-if="data.type === 'select'"
|
||||
v-model:value="data.value"
|
||||
:options="selectOptions"
|
||||
/>
|
||||
<ASwitch
|
||||
v-else-if="data.type === 'boolean'"
|
||||
v-model:checked="data.value"
|
||||
|
|
|
@ -31,6 +31,7 @@ export const zhTWConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
|
|||
items: [
|
||||
{text: '構建', link: '/zh_TW/guide/build'},
|
||||
{text: '專案結構', link: '/zh_TW/guide/project-structure'},
|
||||
{text: '配置模板', link: '/zh_TW/guide/nginx-ui-template'},
|
||||
{text: '貢獻程式碼', link: '/zh_TW/guide/contributing'}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -13,31 +13,71 @@ Please note, you need to recompile the backend after modifying or adding new con
|
|||
|
||||
Nginx UI Template file consists of two parts: the file header and the actual Nginx configuration.
|
||||
|
||||
Below is a configuration template for hotlink protection, which we will use as a basis to introduce the file format and related syntax of Nginx UI Template.
|
||||
Below is a configuration template for reverse proxy, which we will use as a basis to introduce the file format and related syntax of Nginx UI Template.
|
||||
|
||||
```nginx configuration
|
||||
# Nginx UI Template Start
|
||||
name = "Hotlink Protection"
|
||||
name = "Reverse Proxy"
|
||||
author = "@0xJacky"
|
||||
description = { en = "Hotlink Protection Config Template", zh_CN = "防盗链配置模板"}
|
||||
description = { en = "Reverse Proxy Config", zh_CN = "反向代理配置"}
|
||||
|
||||
[variables.NoneReferer]
|
||||
[variables.enableWebSocket]
|
||||
type = "boolean"
|
||||
name = { en = "Allow Referer is None", zh_CN = "允许空 Referer"}
|
||||
value = false
|
||||
name = { en = "Enable WebSocket", zh_CN = "启用 WebSocket"}
|
||||
value = true
|
||||
|
||||
[variables.AllowReferers]
|
||||
[variables.clientMaxBodySize]
|
||||
type = "string"
|
||||
name = { en = "Allow Referers", zh_CN = "允许的 Referers"}
|
||||
value = ""
|
||||
name = { en = "Client Max Body Size", zh_CN = "客户端最大请求内容大小"}
|
||||
value = "1000m"
|
||||
|
||||
[variables.scheme]
|
||||
type = "select"
|
||||
name = { en = "Scheme", zh_CN = "协议"}
|
||||
value = "http"
|
||||
mask = { http = { en = "HTTP" }, https = { en = "HTTPS" } }
|
||||
|
||||
[variables.host]
|
||||
type = "string"
|
||||
name = { en = "Host", zh_CN = "主机"}
|
||||
value = "127.0.0.1"
|
||||
|
||||
[variables.port]
|
||||
type = "string"
|
||||
name = { en = "Port", zh_CN = "端口"}
|
||||
value = 9000
|
||||
# Nginx UI Template End
|
||||
|
||||
location ~ .*\.(jpg|png|js|css)$ {
|
||||
valid_referers {{- if .NoneReferer}} none {{- end}} blocked server_names {{if .AllowReferers}}{{.AllowReferers}}{{- end}};
|
||||
if ($invalid_referer) {
|
||||
return 403;
|
||||
}
|
||||
# Nginx UI Custom Start
|
||||
{{- if .enableWebSocket }}
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
{{- end }}
|
||||
# Nginx UI Custom End
|
||||
|
||||
if ($host != $server_name) {
|
||||
return 404;
|
||||
}
|
||||
|
||||
location / {
|
||||
{{ if .enableWebSocket }}
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
{{ end }}
|
||||
|
||||
client_max_body_size {{ .clientMaxBodySize }};
|
||||
|
||||
proxy_redirect off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_pass {{ .scheme }}://{{ .host }}:{{ .port }}/;
|
||||
}
|
||||
```
|
||||
|
||||
## File Header
|
||||
|
@ -51,27 +91,44 @@ The file header includes the following fields:
|
|||
| `name` | Name of the configuration | string | Yes |
|
||||
| `author` | Author | string | Yes |
|
||||
| `description` | Desciption, uses a toml dictionary for multi-language support | toml dictionary | Yes |
|
||||
| `variables.VariableName.type` | Variable type, currently supports `boolean` and `string` | string | No |
|
||||
| `variables.VariableName.name` | Variable display name, is a toml dictionary to support multi-language | toml dictionary | No |
|
||||
| `variables.VariableName.type` | Variable type, currently supports `boolean`, `string` and `select` | string | Yes |
|
||||
| `variables.VariableName.name` | Variable display name, is a toml dictionary to support multi-language | toml dictionary | Yes |
|
||||
| `variables.VariableName.value` | Default value of the variable | boolean/string (according to type definition) | No |
|
||||
| `variables.VariableName.mask` | The options of select | toml dictionary | No |
|
||||
|
||||
Example:
|
||||
|
||||
```toml
|
||||
# Nginx UI Template Start
|
||||
name = "Hotlink Protection"
|
||||
name = "Reverse Proxy"
|
||||
author = "@0xJacky"
|
||||
description = { en = "Hotlink Protection Config Template", zh_CN = "防盗链配置模板"}
|
||||
description = { en = "Reverse Proxy Config", zh_CN = "反向代理配置"}
|
||||
|
||||
[variables.NoneReferer]
|
||||
[variables.enableWebSocket]
|
||||
type = "boolean"
|
||||
name = { en = "Allow Referer is None", zh_CN = "允许空 Referer"}
|
||||
value = false
|
||||
name = { en = "Enable WebSocket", zh_CN = "启用 WebSocket"}
|
||||
value = true
|
||||
|
||||
[variables.AllowReferers]
|
||||
[variables.clientMaxBodySize]
|
||||
type = "string"
|
||||
name = { en = "Allow Referers", zh_CN = "允许的 Referers"}
|
||||
value = ""
|
||||
name = { en = "Client Max Body Size", zh_CN = "客户端最大请求内容大小"}
|
||||
value = "1000m"
|
||||
|
||||
[variables.scheme]
|
||||
type = "select"
|
||||
name = { en = "Scheme", zh_CN = "协议"}
|
||||
value = "http"
|
||||
mask = { http = { en = "HTTP" }, https = { en = "HTTPS" } }
|
||||
|
||||
[variables.host]
|
||||
type = "string"
|
||||
name = { en = "Host", zh_CN = "主机"}
|
||||
value = "127.0.0.1"
|
||||
|
||||
[variables.port]
|
||||
type = "string"
|
||||
name = { en = "Port", zh_CN = "端口"}
|
||||
value = 9000
|
||||
# Nginx UI Template End
|
||||
```
|
||||
|
||||
|
@ -83,29 +140,45 @@ When you click the "View" button, a dialog will appear, as shown below.
|
|||
|
||||
<img src="/assets/nginx-ui-template/en/config-ui.png" width="350px" title="Config Modal" />
|
||||
|
||||
The input boxes and switches in the interface correspond to the variable types `boolean` and `string`.
|
||||
The following table shows the relationship between the variable type and the UI element:
|
||||
|
||||
| Variable Type | UI Element |
|
||||
|:-------------:|:----------:|
|
||||
| `boolean` | switcher |
|
||||
| `string` | input |
|
||||
| `select` | select |
|
||||
|
||||
## Nginx Configuration
|
||||
The Nginx configuration should be provided after the file header. This part will be parsed using the Go `text/template` library. This library provides powerful template generation capabilities, including conditional judgment, looping, and complex text processing, etc.
|
||||
For more information, please check [Go Documentation](https://pkg.go.dev/text/template).
|
||||
|
||||
The variables defined in the header can be used in this part, such as `.NoneReferer` and `.AllowReferers`.
|
||||
The variables defined in the header can be used in this part, such as `.scheme`, `.host` and `.port`.
|
||||
Please note that you need to define the variables in the header in advance before using them in this part.
|
||||
|
||||
Here is an example:
|
||||
|
||||
```nginx configuration
|
||||
location ~ .*\.(jpg|png|js|css)$ {
|
||||
valid_referers {{- if .NoneReferer}} none {{- end}} blocked server_names {{if .AllowReferers}}{{.AllowReferers}}{{- end}};
|
||||
if ($invalid_referer) {
|
||||
return 403;
|
||||
location / {
|
||||
{{ if .enableWebSocket }}
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
{{ end }}
|
||||
|
||||
client_max_body_size {{ .clientMaxBodySize }};
|
||||
|
||||
proxy_redirect off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Forwarded $proxy_add_forwarded;
|
||||
|
||||
proxy_pass {{ .scheme }}://{{ .host }}:{{ .port }}/;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When users input variable values in the app input boxes, the system will automatically generate new configuration content, as shown below:
|
||||
|
||||
<img src="/assets/nginx-ui-template/en/config-ui-after-input.png" width="350px" title="Config Modal" />
|
||||
When users change the form, the system will automatically generate new configuration content based on the template and the input of user.
|
||||
|
||||
In addition to the variables defined in the template header, we also provide macro-defined variables, as shown in the table below:
|
||||
|
||||
|
|
Before Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 192 KiB |
Before Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 192 KiB |
Before Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 189 KiB |
|
@ -12,31 +12,71 @@ Nginx UI Template 提供了一种开箱即用的配置模板机制。在 NgxConf
|
|||
|
||||
Nginx UI Template 文件由两部分组成:文件头部以及具体的 Nginx 配置。
|
||||
|
||||
以下是一个关于防盗链的配置模板,我们将以这个模板为基础为您介绍 Nginx UI Template 的文件格式及相关语法。
|
||||
以下是一个关于反向代理的配置模板,我们将以这个模板为基础为您介绍 Nginx UI Template 的文件格式及相关语法。
|
||||
|
||||
```nginx configuration
|
||||
# Nginx UI Template Start
|
||||
name = "Hotlink Protection"
|
||||
name = "Reverse Proxy"
|
||||
author = "@0xJacky"
|
||||
description = { en = "Hotlink Protection Config Template", zh_CN = "防盗链配置模板"}
|
||||
description = { en = "Reverse Proxy Config", zh_CN = "反向代理配置"}
|
||||
|
||||
[variables.NoneReferer]
|
||||
[variables.enableWebSocket]
|
||||
type = "boolean"
|
||||
name = { en = "Allow Referer is None", zh_CN = "允许空 Referer"}
|
||||
value = false
|
||||
name = { en = "Enable WebSocket", zh_CN = "启用 WebSocket"}
|
||||
value = true
|
||||
|
||||
[variables.AllowReferers]
|
||||
[variables.clientMaxBodySize]
|
||||
type = "string"
|
||||
name = { en = "Allow Referers", zh_CN = "允许的 Referers"}
|
||||
value = ""
|
||||
name = { en = "Client Max Body Size", zh_CN = "客户端最大请求内容大小"}
|
||||
value = "1000m"
|
||||
|
||||
[variables.scheme]
|
||||
type = "select"
|
||||
name = { en = "Scheme", zh_CN = "协议"}
|
||||
value = "http"
|
||||
mask = { http = { en = "HTTP" }, https = { en = "HTTPS" } }
|
||||
|
||||
[variables.host]
|
||||
type = "string"
|
||||
name = { en = "Host", zh_CN = "主机"}
|
||||
value = "127.0.0.1"
|
||||
|
||||
[variables.port]
|
||||
type = "string"
|
||||
name = { en = "Port", zh_CN = "端口"}
|
||||
value = 9000
|
||||
# Nginx UI Template End
|
||||
|
||||
location ~ .*\.(jpg|png|js|css)$ {
|
||||
valid_referers {{- if .NoneReferer}} none {{- end}} blocked server_names {{if .AllowReferers}}{{.AllowReferers}}{{- end}};
|
||||
if ($invalid_referer) {
|
||||
return 403;
|
||||
}
|
||||
# Nginx UI Custom Start
|
||||
{{- if .enableWebSocket }}
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
{{- end }}
|
||||
# Nginx UI Custom End
|
||||
|
||||
if ($host != $server_name) {
|
||||
return 404;
|
||||
}
|
||||
|
||||
location / {
|
||||
{{ if .enableWebSocket }}
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
{{ end }}
|
||||
|
||||
client_max_body_size {{ .clientMaxBodySize }};
|
||||
|
||||
proxy_redirect off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_pass {{ .scheme }}://{{ .host }}:{{ .port }}/;
|
||||
}
|
||||
```
|
||||
|
||||
## 文件头部
|
||||
|
@ -46,31 +86,48 @@ location ~ .*\.(jpg|png|js|css)$ {
|
|||
文件头部包含以下字段:
|
||||
|
||||
| 字段 | 描述 | 类型 | 必要 |
|
||||
|:----------------------:|:------------------------------:|:---------------------------:|:--:|
|
||||
|:----------------------:|:----------------------------------------:|:---------------------------:|:--:|
|
||||
| `name` | 配置的名称 | string | 是 |
|
||||
| `author` | 作者 | string | 是 |
|
||||
| `description` | 描述,使用 toml 格式的字典来实现多语言描述 | toml 字典 | 是 |
|
||||
| `variables.变量名称.type` | 变量类型,目前支持 `boolean` 和 `string` | string (boolean/string) | 否 |
|
||||
| `variables.变量名称.name` | 变量显示的名称,是一个 toml 格式的字典,用于支持多语言 | toml 字典 | 否 |
|
||||
| `variables.变量名称.type` | 变量类型,目前支持 `boolean`, `string` 和 `select` | string | 是 |
|
||||
| `variables.变量名称.name` | 变量显示的名称,是一个 toml 格式的字典,用于支持多语言 | toml 字典 | 是 |
|
||||
| `variables.变量名称.value` | 变量的默认值 | boolean/string (根据 type 定义) | 否 |
|
||||
| `variables.变量名称.mask` | 选择框的选项 | toml 字典 | 否 |
|
||||
|
||||
示例如下:
|
||||
|
||||
```toml
|
||||
# Nginx UI Template Start
|
||||
name = "Hotlink Protection"
|
||||
name = "Reverse Proxy"
|
||||
author = "@0xJacky"
|
||||
description = { en = "Hotlink Protection Config Template", zh_CN = "防盗链配置模板"}
|
||||
description = { en = "Reverse Proxy Config", zh_CN = "反向代理配置"}
|
||||
|
||||
[variables.NoneReferer]
|
||||
[variables.enableWebSocket]
|
||||
type = "boolean"
|
||||
name = { en = "Allow Referer is None", zh_CN = "允许空 Referer"}
|
||||
value = false
|
||||
name = { en = "Enable WebSocket", zh_CN = "启用 WebSocket"}
|
||||
value = true
|
||||
|
||||
[variables.AllowReferers]
|
||||
[variables.clientMaxBodySize]
|
||||
type = "string"
|
||||
name = { en = "Allow Referers", zh_CN = "允许的 Referers"}
|
||||
value = ""
|
||||
name = { en = "Client Max Body Size", zh_CN = "客户端最大请求内容大小"}
|
||||
value = "1000m"
|
||||
|
||||
[variables.scheme]
|
||||
type = "select"
|
||||
name = { en = "Scheme", zh_CN = "协议"}
|
||||
value = "http"
|
||||
mask = { http = { en = "HTTP" }, https = { en = "HTTPS" } }
|
||||
|
||||
[variables.host]
|
||||
type = "string"
|
||||
name = { en = "Host", zh_CN = "主机"}
|
||||
value = "127.0.0.1"
|
||||
|
||||
[variables.port]
|
||||
type = "string"
|
||||
name = { en = "Port", zh_CN = "端口"}
|
||||
value = 9000
|
||||
# Nginx UI Template End
|
||||
```
|
||||
|
||||
|
@ -82,7 +139,14 @@ value = ""
|
|||
|
||||
<img src="/assets/nginx-ui-template/zh_CN/config-ui.png" width="350px" title="配置 Modal" />
|
||||
|
||||
界面中的输入框和开关对应着变量的类型 `boolean` 和 `string`。
|
||||
下表展示了变量类型与用户界面元素的关系:
|
||||
|
||||
| 类型 | 用户界面元素 |
|
||||
|:---------:|:------:|
|
||||
| `boolean` | 开关 |
|
||||
| `string` | 输入框 |
|
||||
| `select` | 选择框 |
|
||||
|
||||
|
||||
## Nginx 配置
|
||||
Nginx 配置应该在文件头部之后提供,这部分将使用 Go 的 `text/template` 库进行解析。这个库提供了强大的模板生成能力,包括条件判断、循环以及复杂的文本处理等。
|
||||
|
@ -93,16 +157,27 @@ Nginx 配置应该在文件头部之后提供,这部分将使用 Go 的 `text/
|
|||
示例如下:
|
||||
|
||||
```nginx configuration
|
||||
location ~ .*\.(jpg|png|js|css)$ {
|
||||
valid_referers {{- if .NoneReferer}} none {{- end}} blocked server_names {{if .AllowReferers}}{{.AllowReferers}}{{- end}};
|
||||
if ($invalid_referer) {
|
||||
return 403;
|
||||
location / {
|
||||
{{ if .enableWebSocket }}
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
{{ end }}
|
||||
|
||||
client_max_body_size {{ .clientMaxBodySize }};
|
||||
|
||||
proxy_redirect off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Forwarded $proxy_add_forwarded;
|
||||
|
||||
proxy_pass {{ .scheme }}://{{ .host }}:{{ .port }}/;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
当用户在前端的输入框中输入变量的值后,系统将会自动生成新的配置内容,效果如下:
|
||||
<img src="/assets/nginx-ui-template/zh_CN/config-ui-after-input.png" width="350px" title="配置 Modal" />
|
||||
当用户修改前端的表单后,系统将会根据用户的输入和配置模板自动生成新的配置内容。
|
||||
|
||||
除了模板头部定义的变量,我们还提供了宏定义的变量,如下表所示:
|
||||
|
||||
|
|
189
docs/zh_TW/guide/nginx-ui-template.md
Normal file
|
@ -0,0 +1,189 @@
|
|||
# 配置模板
|
||||
|
||||
Nginx UI Template 提供了一種開箱即用的配置模板機制。在 NgxConfigEditor 中,我們設計了一個可視化界面,使使用者能夠方便地將模板中的配置插入到當前的配置文件中。
|
||||
在本指南中,我們將介紹這種配置模板的文件格式和語法規則。
|
||||
配置模板文件存儲在 `template/block` 目錄中,我們歡迎並期待您通過提交 [PR](https://github.com/0xJacky/nginx-ui/pulls) 的形式分享您編寫的配置模板。
|
||||
|
||||
::: tip
|
||||
請注意,每次修改或新增配置文件後,需要重新編譯後端以生效。
|
||||
:::
|
||||
|
||||
## 文件格式
|
||||
|
||||
Nginx UI Template 文件由兩部分組成:文件頭部以及具體的 Nginx 配置。
|
||||
|
||||
以下是一個關於反向代理的配置模板,我們將以此模板為基礎向您介紹 Nginx UI Template 的文件格式及相關語法。
|
||||
|
||||
```nginx configuration
|
||||
# Nginx UI Template Start
|
||||
name = "Reverse Proxy"
|
||||
author = "@0xJacky"
|
||||
description = { en = "Reverse Proxy Config", zh_CN = "反向代理配置"}
|
||||
|
||||
[variables.enableWebSocket]
|
||||
type = "boolean"
|
||||
name = { en = "Enable WebSocket", zh_CN = "啟用 WebSocket"}
|
||||
value = true
|
||||
|
||||
[variables.clientMaxBodySize]
|
||||
type = "string"
|
||||
name = { en = "Client Max Body Size", zh_CN = "客戶端最大請求內容大小"}
|
||||
value = "1000m"
|
||||
|
||||
[variables.scheme]
|
||||
type = "select"
|
||||
name = { en = "Scheme", zh_CN = "協議"}
|
||||
value = "http"
|
||||
mask = { http = { en = "HTTP" }, https = { en = "HTTPS" } }
|
||||
|
||||
[variables.host]
|
||||
type = "string"
|
||||
name = { en = "Host", zh_CN = "主機"}
|
||||
value = "127.0.0.1"
|
||||
|
||||
[variables.port]
|
||||
type = "string"
|
||||
name = { en = "Port", zh_CN = "端口"}
|
||||
value = 9000
|
||||
# Nginx UI Template End
|
||||
|
||||
# Nginx UI Custom Start
|
||||
{{- if .enableWebSocket }}
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
{{- end }}
|
||||
# Nginx UI Custom End
|
||||
|
||||
if ($host != $server_name) {
|
||||
return 404;
|
||||
}
|
||||
|
||||
location / {
|
||||
{{ if .enableWebSocket }}
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
{{ end }}
|
||||
|
||||
client_max_body_size {{ .clientMaxBodySize }};
|
||||
|
||||
proxy_redirect off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_pass {{ .scheme }}://{{ .host }}:{{ .port }}/;
|
||||
}
|
||||
```
|
||||
|
||||
## 文件頭部
|
||||
|
||||
文件頭部應包含在 `# Nginx UI Template Start` 和 `# Nginx UI Template End` 之間,並遵循 toml 語法。
|
||||
|
||||
文件頭部包含以下欄位:
|
||||
|
||||
| 欄位 | 描述 | 類型 | 必要 |
|
||||
|:----------------------:|:----------------------------------------:|:---------------------------:|:--:|
|
||||
| `name` | 配置的名稱 | string | 是 |
|
||||
| `author` | 作者 | string | 是 |
|
||||
| `description` | 描述,使用 toml 格式的字典來實現多語言描述 | toml 字典 | 是 |
|
||||
| `variables.變量名稱.type` | 變量類型,目前支持 `boolean`, `string` 和 `select` | string | 是 |
|
||||
| `variables.變量名稱.name` | 變量顯示的名稱,是一個 toml 格式的字典,用於支持多語言 | toml 字典 | 是 |
|
||||
| `variables.變量名稱.value` | 變量的默認值 | boolean/string (根據 type 定義) | 否 |
|
||||
| `variables.變量名稱.mask` | 選擇框的選項 | toml 字典 | 否 |
|
||||
|
||||
示例如下:
|
||||
|
||||
```toml
|
||||
# Nginx UI Template Start
|
||||
name = "Reverse Proxy"
|
||||
author = "@0xJacky"
|
||||
description = { en = "Reverse Proxy Config", zh_CN = "反向代理配置"}
|
||||
|
||||
[variables.enableWebSocket]
|
||||
type = "boolean"
|
||||
name = { en = "Enable WebSocket", zh_CN = "啟用 WebSocket"}
|
||||
value = true
|
||||
|
||||
[variables.clientMaxBodySize]
|
||||
type = "string"
|
||||
name = { en = "Client Max Body Size", zh_CN = "客戶端最大請求內容大小"}
|
||||
value = "1000m"
|
||||
|
||||
[variables.scheme]
|
||||
type = "select"
|
||||
name = { en = "Scheme", zh_CN = "協議"}
|
||||
value = "http"
|
||||
mask = { http = { en = "HTTP" }, https = { en = "HTTPS" } }
|
||||
|
||||
[variables.host]
|
||||
type = "string"
|
||||
name = { en = "Host", zh_CN = "主機"}
|
||||
value = "127.0.0.1"
|
||||
|
||||
[variables.port]
|
||||
type = "string"
|
||||
name = { en = "Port", zh_CN = "端口"}
|
||||
value = 9000
|
||||
# Nginx UI Template End
|
||||
```
|
||||
|
||||
其中,名稱、作者及描述將會以摘要的形式在配置列表中顯示。
|
||||
|
||||

|
||||
|
||||
當您點擊「查看」按鈕,界面會顯示一個對話框,如下圖所示。
|
||||
|
||||
<img src="/assets/nginx-ui-template/zh_TW/config-ui.png" width="350px" title="配置 Modal" />
|
||||
|
||||
下表展示了變量類型與使用者界面元素的關係:
|
||||
|
||||
| 類型 | 使用者界面元素 |
|
||||
|:---------:|:------:|
|
||||
| `boolean` | 開關 |
|
||||
| `string` | 輸入框 |
|
||||
| `select` | 選擇框 |
|
||||
|
||||
|
||||
## Nginx 配置
|
||||
Nginx 配置應該在文件頭部之後提供,這部分將使用 Go 的 `text/template` 庫進行解析。這個庫提供了強大的模板生成能力,包括條件判斷、循環以及複雜的文本處理等。
|
||||
具體語法可以參考 [Go 文件](https://pkg.go.dev/text/template)。
|
||||
|
||||
在頭部中定義的變量可以在這部分中使用,如 `.NoneReferer` 和 `.AllowReferers`。請注意,需要預先在頭部定義變量,才能在這部分中使用。
|
||||
|
||||
示例如下:
|
||||
|
||||
```nginx configuration
|
||||
location / {
|
||||
{{ if .enableWebSocket }}
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
{{ end }}
|
||||
|
||||
client_max_body_size {{ .clientMaxBodySize }};
|
||||
|
||||
proxy_redirect off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Forwarded $proxy_add_forwarded;
|
||||
|
||||
proxy_pass {{ .scheme }}://{{ .host }}:{{ .port }}/;
|
||||
}
|
||||
```
|
||||
|
||||
當使用者修改前端的表單後,系統將會根據使用者的輸入和配置模板自動生成新的配置內容。
|
||||
|
||||
除了模板頭部定義的變量,我們還提供了宏定義的變量,如下表所示:
|
||||
|
||||
| 變量名 | 描述 |
|
||||
|:----------:|:-----------------------:|
|
||||
| HTTPPORT | Nginx UI 監聽的端口 |
|
||||
| HTTP01PORT | 用於 HTTP01 Challenge 的端口 |
|
||||
|
||||
上述變量可以直接在配置部分使用,無需在頭部定義。
|
|
@ -19,9 +19,10 @@ import (
|
|||
)
|
||||
|
||||
type Variable struct {
|
||||
Type string `json:"type"`
|
||||
Type string `json:"type"` // string, bool, select
|
||||
Name map[string]string `json:"name"`
|
||||
Value interface{} `json:"value"`
|
||||
Mask map[string]map[string]string `json:"mask,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigInfoItem struct {
|
||||
|
@ -48,22 +49,22 @@ func GetTemplateInfo(path, name string) (configListItem ConfigInfoItem) {
|
|||
}(file)
|
||||
|
||||
r := bufio.NewReader(file)
|
||||
bytes, _, err := r.ReadLine()
|
||||
lineBytes, _, err := r.ReadLine()
|
||||
if err == io.EOF {
|
||||
return
|
||||
}
|
||||
line := strings.TrimSpace(string(bytes))
|
||||
line := strings.TrimSpace(string(lineBytes))
|
||||
|
||||
if line != "# Nginx UI Template Start" {
|
||||
return
|
||||
}
|
||||
var content string
|
||||
for {
|
||||
bytes, _, err = r.ReadLine()
|
||||
lineBytes, _, err = r.ReadLine()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
line = strings.TrimSpace(string(bytes))
|
||||
line = strings.TrimSpace(string(lineBytes))
|
||||
if line == "# Nginx UI Template End" {
|
||||
break
|
||||
}
|
||||
|
@ -123,7 +124,6 @@ func ParseTemplate(path, name string, bindData map[string]Variable) (c ConfigDet
|
|||
}
|
||||
|
||||
t, err := template.New(name).Parse(custom)
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "error parse template.custom")
|
||||
return
|
||||
|
@ -147,7 +147,6 @@ func ParseTemplate(path, name string, bindData map[string]Variable) (c ConfigDet
|
|||
content = templatePart[1]
|
||||
|
||||
t, err = template.New(name).Parse(content)
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "error parse template")
|
||||
return
|
||||
|
|
|
@ -13,13 +13,23 @@ type = "string"
|
|||
name = { en = "Client Max Body Size", zh_CN = "客户端最大请求内容大小"}
|
||||
value = "1000m"
|
||||
|
||||
[variables.scheme]
|
||||
type = "select"
|
||||
name = { en = "Scheme", zh_CN = "协议"}
|
||||
value = "http"
|
||||
mask = { http = { en = "HTTP" }, https = { en = "HTTPS" } }
|
||||
|
||||
[variables.host]
|
||||
type = "string"
|
||||
name = { en = "Host", zh_CN = "主机"}
|
||||
value = "127.0.0.1"
|
||||
|
||||
[variables.port]
|
||||
type = "string"
|
||||
name = { en = "Port", zh_CN = "端口"}
|
||||
value = 9000
|
||||
# Nginx UI Template End
|
||||
|
||||
|
||||
# Nginx UI Custom Start
|
||||
{{- if .enableWebSocket }}
|
||||
map $http_upgrade $connection_upgrade {
|
||||
|
@ -67,5 +77,5 @@ location / {
|
|||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Forwarded $proxy_add_forwarded;
|
||||
|
||||
proxy_pass http://127.0.0.1:{{ .port }}/;
|
||||
proxy_pass {{ .scheme }}://{{ .host }}:{{ .port }}/;
|
||||
}
|
||||
|
|