feat: support config cors in website proxy (#10551)

Refs https://github.com/1Panel-dev/1Panel/issues/7211
Refs https://github.com/1Panel-dev/1Panel/issues/7386
This commit is contained in:
CityFun 2025-09-30 17:34:22 +08:00 committed by GitHub
parent 5d83f21a26
commit 72a0e0200e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 449 additions and 194 deletions

View file

@ -211,24 +211,30 @@ type WebsiteUpdateDirPermission struct {
}
type WebsiteProxyConfig struct {
ID uint `json:"id" validate:"required"`
Operate string `json:"operate" validate:"required"`
Enable bool `json:"enable" `
Cache bool `json:"cache" `
CacheTime int `json:"cacheTime"`
CacheUnit string `json:"cacheUnit"`
ServerCacheTime int `json:"serverCacheTime"`
ServerCacheUnit string `json:"serverCacheUnit"`
Name string `json:"name" validate:"required"`
Modifier string `json:"modifier"`
Match string `json:"match" validate:"required"`
ProxyPass string `json:"proxyPass" validate:"required"`
ProxyHost string `json:"proxyHost" validate:"required"`
Content string `json:"content"`
FilePath string `json:"filePath"`
Replaces map[string]string `json:"replaces"`
SNI bool `json:"sni"`
ProxySSLName string `json:"proxySSLName"`
ID uint `json:"id" validate:"required"`
Operate string `json:"operate" validate:"required"`
Enable bool `json:"enable" `
Cache bool `json:"cache" `
CacheTime int `json:"cacheTime"`
CacheUnit string `json:"cacheUnit"`
ServerCacheTime int `json:"serverCacheTime"`
ServerCacheUnit string `json:"serverCacheUnit"`
Name string `json:"name" validate:"required"`
Modifier string `json:"modifier"`
Match string `json:"match" validate:"required"`
ProxyPass string `json:"proxyPass" validate:"required"`
ProxyHost string `json:"proxyHost" validate:"required"`
Content string `json:"content"`
FilePath string `json:"filePath"`
Replaces map[string]string `json:"replaces"`
SNI bool `json:"sni"`
ProxySSLName string `json:"proxySSLName"`
Cors bool `json:"cors"`
AllowOrigins string `json:"allowOrigins"`
AllowMethods string `json:"allowMethods"`
AllowHeaders string `json:"allowHeaders"`
AllowCredentials bool `json:"allowCredentials"`
Preflight bool `json:"preflight"`
}
type WebsiteProxyReq struct {

View file

@ -1681,7 +1681,7 @@ func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error)
switch req.Operate {
case "create":
config, err = parser.NewStringParser(string(nginx_conf.Proxy)).Parse()
config, err = parser.NewStringParser(string(nginx_conf.GetWebsiteFile("proxy.conf"))).Parse()
if err != nil {
return
}
@ -1748,6 +1748,35 @@ func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error)
} else {
location.UpdateDirective("proxy_ssl_server_name", []string{"off"})
}
if req.Cors {
location.UpdateDirective("add_header", []string{"Access-Control-Allow-Origin", req.AllowOrigins, "always"})
if req.AllowMethods != "" {
location.UpdateDirective("add_header", []string{"Access-Control-Allow-Methods", req.AllowMethods, "always"})
} else {
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Methods"})
}
if req.AllowHeaders != "" {
location.UpdateDirective("add_header", []string{"Access-Control-Allow-Headers", req.AllowHeaders, "always"})
} else {
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Headers"})
}
if req.AllowCredentials {
location.UpdateDirective("add_header", []string{"Access-Control-Allow-Credentials", "true", "always"})
} else {
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Credentials"})
}
if req.Preflight {
location.AddCorsOption()
} else {
location.RemoveDirective("if", []string{"(", "$request_method", "=", "'OPTIONS'", ")"})
}
} else {
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Origin"})
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Methods"})
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Headers"})
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Credentials"})
location.RemoveDirective("if", []string{"(", "$request_method", "=", "'OPTIONS'", ")"})
}
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return buserr.WithErr("ErrUpdateBuWebsite", err)
}
@ -1907,6 +1936,12 @@ func (w WebsiteService) GetProxies(id uint) (res []request.WebsiteProxyConfig, e
proxyConfig.ProxySSLName = directive.GetParameters()[0]
}
}
proxyConfig.Cors = location.Cors
proxyConfig.AllowCredentials = location.AllowCredentials
proxyConfig.AllowHeaders = location.AllowHeaders
proxyConfig.AllowOrigins = location.AllowOrigins
proxyConfig.AllowMethods = location.AllowMethods
proxyConfig.Preflight = location.Preflight
res = append(res, proxyConfig)
}
return

View file

@ -8,20 +8,26 @@ import (
)
type Location struct {
Modifier string
Match string
Cache bool
ProxyPass string
Host string
CacheTime int
CacheUint string
Comment string
Directives []IDirective
Line int
Parameters []string
Replaces map[string]string
ServerCacheTime int
ServerCacheUint string
Modifier string
Match string
Cache bool
ProxyPass string
Host string
CacheTime int
CacheUint string
Comment string
Directives []IDirective
Line int
Parameters []string
Replaces map[string]string
ServerCacheTime int
ServerCacheUint string
Cors bool
AllowMethods string
AllowHeaders string
AllowOrigins string
AllowCredentials bool
Preflight bool
}
func (l *Location) GetCodeBlock() string {
@ -69,6 +75,9 @@ func NewLocation(directive IDirective) *Location {
}
}
}
if params[0] == "(" && params[1] == "$request_method" && params[2] == `=` && params[3] == `'OPTIONS'` && params[4] == ")" {
location.Preflight = true
}
case "proxy_cache_valid":
timeParam := params[len(params)-1]
re := regexp.MustCompile(`^(\d+)(\w+)$`)
@ -90,6 +99,20 @@ func NewLocation(directive IDirective) *Location {
location.Replaces = make(map[string]string, 0)
}
location.Replaces[strings.Trim(params[0], "\"")] = strings.Trim(params[1], "\"")
case "add_header":
if params[0] == "Access-Control-Allow-Origin" {
location.Cors = true
location.AllowOrigins = params[1]
}
if params[0] == "Access-Control-Allow-Methods" {
location.AllowMethods = params[1]
}
if params[0] == "Access-Control-Allow-Headers" {
location.AllowHeaders = params[1]
}
if params[0] == "Access-Control-Allow-Credentials" && params[1] == "true" {
location.AllowCredentials = true
}
}
}
@ -264,3 +287,32 @@ func (l *Location) RemoveSubFilter() {
l.RemoveDirective("sub_filter_types", []string{"*"})
l.Replaces = nil
}
func (l *Location) AddCorsOption() {
newDir := &Directive{
Name: "if",
Parameters: []string{"(", "$request_method", "=", "'OPTIONS'", ")"},
Block: &Block{},
}
block := &Block{}
block.AppendDirectives(&Directive{
Name: "add_header",
Parameters: []string{"Access-Control-Max-Age", "1728000"},
})
block.AppendDirectives(&Directive{
Name: "add_header",
Parameters: []string{"Content-Type", "'text/plain;charset=UTF-8'"},
})
block.AppendDirectives(&Directive{
Name: "add_header",
Parameters: []string{"Content-Length", "0"},
})
block.AppendDirectives(&Directive{
Name: "return",
Parameters: []string{"204"},
})
newDir.Block = block
directives := l.GetDirectives()
directives = append(directives, newDir)
l.Directives = directives
}

View file

@ -407,6 +407,12 @@ export namespace Website {
proxyProtocol?: string;
sni?: boolean;
proxySSLName: string;
cors: boolean;
allowOrigins: string;
allowMethods: string;
allowHeaders: string;
allowCredentials: boolean;
preflight: boolean;
}
export interface ProxReplace {

View file

@ -2598,6 +2598,15 @@ const message = {
'If unsure, you can enter 0.0.0.0/0 (ipv4) ::/0 (ipv6) [Note: Allowing any source IP is not secure]',
http3Helper:
'HTTP/3 is an upgrade to HTTP/2, offering faster connection speeds and better performance, but not all browsers support HTTP/3. Enabling it may cause some browsers to be unable to access the site.',
cors: 'Cross-Origin Resource Sharing (CORS)',
enableCors: 'Enable CORS',
allowOrigins: 'Allowed domains',
allowMethods: 'Allowed request methods',
allowHeaders: 'Allowed request headers',
allowCredentials: 'Allow cookies to be sent',
preflight: 'Preflight request fast response',
preflightHleper:
'When enabled, when the browser sends a cross-origin preflight request (OPTIONS request), the system will automatically return a 204 status code and set the necessary cross-origin response headers',
changeDatabase: 'Change Database',
changeDatabaseHelper1: 'Database association is used for backing up and restoring the website.',

View file

@ -2575,6 +2575,16 @@ const message = {
ipFromExample2: 'Si el frontend es un CDN: rango IP del CDN',
ipFromExample3: 'En caso de duda: 0.0.0.0/0 (IPv4) ::/0 (IPv6) [Nota: poco seguro]',
http3Helper: 'HTTP/3 ofrece mayor velocidad y rendimiento, pero no todos los navegadores lo soportan.',
cors: 'Intercambio de Recursos de Origen Cruzado (CORS)',
enableCors: 'Habilitar CORS',
allowOrigins: 'Dominios permitidos',
allowMethods: 'Métodos de solicitud permitidos',
allowHeaders: 'Encabezados de solicitud permitidos',
allowCredentials: 'Permitir enviar cookies',
preflight: 'Respuesta rápida a solicitud de preflight',
preflightHleper:
'Cuando está habilitado, cuando el navegador envía una solicitud de preflight de origen cruzado (solicitud OPTIONS), el sistema devolverá automáticamente un código de estado 204 y establecerá los encabezados de respuesta de origen cruzado necesarios',
changeDatabase: 'Cambiar base de datos',
changeDatabaseHelper1: 'La asociación se usa en backups/restauraciones.',
changeDatabaseHelper2: 'Cambiar de DB invalida backups previos.',

View file

@ -2514,6 +2514,16 @@ const message = {
'不明な場合は0.0.0.0/0IPv4または::/0IPv6を入力できます[注意任意のソースIPを許可することは安全ではありません]',
http3Helper:
'HTTP/3はHTTP/2のアップグレード版でより高速な接続速度とパフォーマンスを提供しますただしすべてのブラウザがHTTP/3をサポートしているわけではなく有効にすると一部のブラウザがサイトにアクセスできなくなる可能性があります',
cors: 'クロスオリジンリソース共有(CORS)',
enableCors: 'CORSを有効にする',
allowOrigins: '許可されたドメイン',
allowMethods: '許可されたリクエストメソッド',
allowHeaders: '許可されたリクエストヘッダー',
allowCredentials: 'Cookieの送信を許可する',
preflight: 'プリフライトリクエストの高速応答',
preflightHleper:
'有効にするとブラウザがクロスオリジンのプリフライトリクエストOPTIONSリクエストを送信した場合システムは自動的に204ステータスコードを返し必要なクロスオリジン応答ヘッダーを設定します',
changeDatabase: 'データベースを切り替え',
changeDatabaseHelper1: 'データベースの関連付けはウェブサイトのバックアップと復元に使用されます',
changeDatabaseHelper2: '別のデータベースに切り替えると以前のバックアップが復元できなくなる可能性があります',

View file

@ -2471,6 +2471,16 @@ const message = {
'확실하지 않은 경우 0.0.0.0/0 (IPv4) 또는 ::/0 (IPv6) 입력할 있습니다. [주의: 모든 소스 IP를 허용하는 것은 안전하지 않습니다.]',
http3Helper:
'HTTP/3 HTTP/2 업그레이드 버전으로, 빠른 연결 속도와 나은 성능을 제공합니다. 그러나 모든 브라우저가 HTTP/3 지원하는 것은 아니며, 활성화하면 일부 브라우저가 사이트에 접근하지 못할 있습니다.',
cors: '교차 출처 리소스 공유(CORS)',
enableCors: 'CORS 활성화',
allowOrigins: '허용된 도메인',
allowMethods: '허용된 요청 메서드',
allowHeaders: '허용된 요청 헤더',
allowCredentials: '쿠키 전송 허용',
preflight: '프리플라이트 요청 빠른 응답',
preflightHleper:
'활성화하면 브라우저가 교차 출처 프리플라이트 요청(OPTIONS 요청) 보낼 시스템이 자동으로 204 상태 코드를 반환하고 필요한 교차 출처 응답 헤더를 설정합니다',
changeDatabase: '데이터베이스 전환',
changeDatabaseHelper1: '데이터베이스 연관은 웹사이트 백업 복원에 사용됩니다.',
changeDatabaseHelper2: '다른 데이터베이스로 전환하면 이전 백업을 복원할 없게 있습니다.',

View file

@ -2570,6 +2570,16 @@ const message = {
'Jika tidak pasti, anda boleh mengisi 0.0.0.0/0 (IPv4) atau ::/0 (IPv6). [Nota: Membenarkan sebarang sumber IP tidak selamat.]',
http3Helper:
'HTTP/3 adalah versi naik taraf HTTP/2, menyediakan kelajuan sambungan yang lebih pantas dan prestasi yang lebih baik. Walau bagaimanapun, tidak semua penyemak imbas menyokong HTTP/3, dan mengaktifkannya mungkin menyebabkan beberapa penyemak imbas tidak dapat mengakses laman web.',
cors: 'Perkongsian Sumber Asal Silang (CORS)',
enableCors: 'Dayakan CORS',
allowOrigins: 'Domain yang dibenarkan',
allowMethods: 'Kaedah permintaan yang dibenarkan',
allowHeaders: 'Pengepala permintaan yang dibenarkan',
allowCredentials: 'Benarkan kuki dihantar',
preflight: 'Tindak balas pantas permintaan preflight',
preflightHleper:
'Apabila didayakan, apabila pelayar menghantar permintaan preflight asal silang (permintaan OPTIONS), sistem akan secara automatik mengembalikan kod status 204 dan menetapkan pengepala respons asal silang yang diperlukan',
changeDatabase: 'Tukar Pangkalan Data',
changeDatabaseHelper1: 'Perkaitan pangkalan data digunakan untuk sandaran dan pemulihan laman web.',
changeDatabaseHelper2:

View file

@ -2575,6 +2575,16 @@ const message = {
'Se não tiver certeza, você pode preencher 0.0.0.0/0 (IPv4) ou ::/0 (IPv6). [Nota: Permitir qualquer fonte de IP não é seguro.]',
http3Helper:
'O HTTP/3 é uma versão atualizada do HTTP/2, fornecendo velocidades de conexão mais rápidas e melhor desempenho. No entanto, nem todos os navegadores suportam HTTP/3, e ativá-lo pode fazer com que alguns navegadores não consigam acessar o site.',
cors: 'Compartilhamento de Recursos de Origem Cruzada (CORS)',
enableCors: 'Habilitar CORS',
allowOrigins: 'Domínios permitidos',
allowMethods: 'Métodos de solicitação permitidos',
allowHeaders: 'Cabeçalhos de solicitação permitidos',
allowCredentials: 'Permitir envio de cookies',
preflight: 'Resposta rápida a solicitação de preflight',
preflightHleper:
'Quando habilitado, quando o navegador envia uma solicitação de preflight de origem cruzada (solicitação OPTIONS), o sistema retornará automaticamente um código de status 204 e definirá os cabeçalhos de resposta de origem cruzada necessários',
changeDatabase: 'Alterar Banco de Dados',
changeDatabaseHelper1: 'A associação do banco de dados é usada para backup e restauração do site.',
changeDatabaseHelper2: 'Alternar para outro banco de dados pode tornar backups anteriores irrecuperáveis.',

View file

@ -2572,6 +2572,16 @@ const message = {
'Если вы не уверены, вы можете указать 0.0.0.0/0 (IPv4) или ::/0 (IPv6). [Примечание: Разрешение любого источника IP небезопасно.]',
http3Helper:
'HTTP/3 это обновленная версия HTTP/2, обеспечивающая более высокую скорость соединения и лучшую производительность. Однако не все браузеры поддерживают HTTP/3, и его включение может привести к тому, что некоторые браузеры не смогут получить доступ к сайту.',
cors: 'Межсайтовый обмен ресурсами (CORS)',
enableCors: 'Включить CORS',
allowOrigins: 'Разрешенные домены',
allowMethods: 'Разрешенные методы запроса',
allowHeaders: 'Разрешенные заголовки запроса',
allowCredentials: 'Разрешить отправку cookies',
preflight: 'Быстрый ответ на предварительный запрос',
preflightHleper:
'При включении, когда браузер отправляет межсайтовый предварительный запрос (запрос OPTIONS), система автоматически вернет статус 204 и установит необходимые межсайтовые заголовки ответа',
changeDatabase: 'Сменить Базу Данных',
changeDatabaseHelper1: 'Связь базы данных используется для резервного копирования и восстановления сайта.',
changeDatabaseHelper2:

View file

@ -2630,6 +2630,16 @@ const message = {
'Emin değilseniz, 0.0.0.0/0 (ipv4) ::/0 (ipv6) girebilirsiniz [Not: Herhangi bir kaynak IPye izin vermek güvenli değildir]',
http3Helper:
'HTTP/3, HTTP/2nin bir yükseltmesidir, daha hızlı bağlantı hızları ve daha iyi performans sunar, ancak tüm tarayıcılar HTTP/3ü desteklemez. Etkinleştirilmesi bazı tarayıcıların siteye erişememesine neden olabilir.',
cors: 'Çapraz Kaynak Paylaşımı (CORS)',
enableCors: 'CORS etkinleştir',
allowOrigins: 'İzin verilen alan adları',
allowMethods: 'İzin verilen istek yöntemleri',
allowHeaders: 'İzin verilen istek başlıkları',
allowCredentials: 'Çerezlerin gönderilmesine izin ver',
preflight: 'Ön istek hızlı yanıtı',
preflightHleper:
'Etkinleştirildiğinde, tarayıcı çapraz kaynak ön isteği (OPTIONS isteği) gönderdiğinde, sistem otomatik olarak 204 durum kodunu döndürecek ve gerekli çapraz kaynak yanıt başlıklarını ayarlayacaktır',
changeDatabase: 'Veritabanını Değiştir',
changeDatabaseHelper1:
'Veritabanı ilişkilendirmesi, web sitesinin yedeklenmesi ve geri yüklenmesi için kullanılır.',

View file

@ -2421,6 +2421,15 @@ const message = {
ipFromExample3: '如果不確定可以填 0.0.0.0/0ipv4 ::/0ipv6 [注意允許任意來源 IP 不安全]',
http3Helper:
'HTTP/3 HTTP/2 的升級版本提供更快的連線速度和更好的效能但並非所有瀏覽器都支援 HTTP/3啟用後可能會導致部分瀏覽器無法訪問',
cors: '跨域存取(CORS)',
enableCors: '開啟跨域',
allowOrigins: '允許存取的網域名稱',
allowMethods: '允許的請求方法',
allowHeaders: '允許的請求標頭',
allowCredentials: '允許攜帶cookies',
preflight: '預檢請求快速回應',
preflightHleper:
'開啟後當瀏覽器發送跨域預檢請求OPTIONS 請求系統會自動返回 204 狀態碼並設定必要的跨域回應標頭',
changeDatabase: '切換資料庫',
changeDatabaseHelper1: '資料庫關聯用於備份復原網站',

View file

@ -2413,6 +2413,15 @@ const message = {
ipFromExample3: '如果不确定可以填 0.0.0.0/0ipv4 ::/0ipv6 [注意允许任意来源 IP 不安全]',
http3Helper:
'HTTP/3 HTTP/2 的升级版本提供更快的连接速度和更好的性能但是不是所有浏览器都支持 HTTP/3开启后可能会导致部分浏览器无法访问',
cors: '跨域访问(CORS)',
enableCors: '开启跨域',
allowOrigins: '允许访问的域名',
allowMethods: '允许的请求方法',
allowHeaders: '允许的请求头',
allowCredentials: '允许携带cookies',
preflight: '预检请求快速响应',
preflightHleper:
'开启后当浏览器发送跨域预检请求OPTIONS 请求系统会自动返回 204 状态码并设置必要的跨域响应头',
changeDatabase: '切换数据库',
changeDatabaseHelper1: '数据库关联用于备份恢复网站',

View file

@ -1,184 +1,188 @@
<template>
<el-row :gutter="20" v-loading="loading">
<el-col :xs="24" :sm="18" :md="18" :lg="14" :xl="14">
<el-alert :closable="false">
<template #default>
<span class="whitespace-pre-line">{{ $t('website.SSLHelper') }}</span>
</template>
</el-alert>
<el-form
class="moblie-form"
ref="httpsForm"
label-position="right"
label-width="auto"
label-width="150px"
:model="form"
:rules="rules"
>
<el-form-item prop="enable" :label="$t('website.enableHTTPS')">
<el-switch v-model="form.enable" @change="changeEnable"></el-switch>
</el-form-item>
<div v-if="form.enable">
<el-form-item :label="'HTTPS ' + $t('commons.table.port')" prop="HttpsPort">
<el-text>{{ form.httpsPort }}</el-text>
</el-form-item>
<el-text type="warning" class="!ml-2">{{ $t('website.ipWebsiteWarn') }}</el-text>
<el-divider content-position="left">{{ $t('website.SSLConfig') }}</el-divider>
<el-form-item :label="$t('website.HTTPConfig')" prop="httpConfig">
<el-select v-model="form.httpConfig" class="p-w-400">
<el-option :label="$t('website.HTTPToHTTPS')" :value="'HTTPToHTTPS'"></el-option>
<el-option :label="$t('website.HTTPAlso')" :value="'HTTPAlso'"></el-option>
<el-option :label="$t('website.HTTPSOnly')" :value="'HTTPSOnly'"></el-option>
</el-select>
</el-form-item>
<el-form-item :label="'HSTS'" prop="hsts">
<el-checkbox v-model="form.hsts">{{ $t('commons.button.enable') }}</el-checkbox>
<span class="input-help">{{ $t('website.hstsHelper') }}</span>
</el-form-item>
<el-form-item
:label="'HSTS ' + $t('website.includeSubDomains')"
prop="hstsIncludeSubDomains"
v-if="form.hsts"
>
<el-checkbox v-model="form.hstsIncludeSubDomains">
{{ $t('commons.button.enable') }}
</el-checkbox>
<span class="input-help">
{{ $t('website.hstsIncludeSubDomainsHelper') }}
</span>
</el-form-item>
<el-form-item :label="'HTTP3'" prop="http3">
<el-checkbox v-model="form.http3">{{ $t('commons.button.enable') }}</el-checkbox>
<span class="input-help">{{ $t('website.http3Helper') }}</span>
</el-form-item>
<el-form-item :label="$t('website.sslConfig')" prop="type">
<el-select v-model="form.type" @change="changeType(form.type)" class="p-w-400">
<el-option :label="$t('website.oldSSL')" :value="'existed'"></el-option>
<el-option :label="$t('website.manualSSL')" :value="'manual'"></el-option>
</el-select>
</el-form-item>
<div v-if="form.type === 'existed'">
<el-form-item :label="$t('website.acmeAccountManage')" prop="acmeAccountID">
<el-select
v-model="form.acmeAccountID"
:placeholder="$t('website.selectAcme')"
@change="listSSLs"
class="p-w-400"
>
<el-option :key="0" :label="$t('website.imported')" :value="0"></el-option>
<el-option
v-for="(acme, index) in acmeAccounts"
:key="index"
:label="acme.email"
:value="acme.id"
<el-collapse-transition>
<div v-if="form.enable">
<el-form-item :label="'HTTPS ' + $t('commons.table.port')" prop="HttpsPort">
<el-text>{{ form.httpsPort }}</el-text>
</el-form-item>
<el-text type="warning" class="!ml-2">{{ $t('website.ipWebsiteWarn') }}</el-text>
<el-divider content-position="left">{{ $t('website.SSLConfig') }}</el-divider>
<el-form-item :label="$t('website.HTTPConfig')" prop="httpConfig">
<el-select v-model="form.httpConfig" class="p-w-400">
<el-option :label="$t('website.HTTPToHTTPS')" :value="'HTTPToHTTPS'"></el-option>
<el-option :label="$t('website.HTTPAlso')" :value="'HTTPAlso'"></el-option>
<el-option :label="$t('website.HTTPSOnly')" :value="'HTTPSOnly'"></el-option>
</el-select>
</el-form-item>
<el-form-item :label="'HSTS'" prop="hsts">
<el-checkbox v-model="form.hsts">{{ $t('commons.button.enable') }}</el-checkbox>
<span class="input-help">{{ $t('website.hstsHelper') }}</span>
</el-form-item>
<el-form-item
:label="'HSTS ' + $t('website.includeSubDomains')"
prop="hstsIncludeSubDomains"
v-if="form.hsts"
>
<el-checkbox v-model="form.hstsIncludeSubDomains">
{{ $t('commons.button.enable') }}
</el-checkbox>
<span class="input-help">
{{ $t('website.hstsIncludeSubDomainsHelper') }}
</span>
</el-form-item>
<el-form-item :label="'HTTP3'" prop="http3">
<el-checkbox v-model="form.http3">{{ $t('commons.button.enable') }}</el-checkbox>
<span class="input-help">{{ $t('website.http3Helper') }}</span>
</el-form-item>
<el-form-item :label="$t('website.sslConfig')" prop="type">
<el-select v-model="form.type" @change="changeType(form.type)" class="p-w-400">
<el-option :label="$t('website.oldSSL')" :value="'existed'"></el-option>
<el-option :label="$t('website.manualSSL')" :value="'manual'"></el-option>
</el-select>
</el-form-item>
<div v-if="form.type === 'existed'">
<el-form-item :label="$t('website.acmeAccountManage')" prop="acmeAccountID">
<el-select
v-model="form.acmeAccountID"
:placeholder="$t('website.selectAcme')"
@change="listSSLs"
class="p-w-400"
>
<span>
{{ acme.email }}
<el-tag class="ml-5">{{ getAccountName(acme.type) }}</el-tag>
</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item :label="$t('website.ssl')" prop="websiteSSLId" :hide-required-asterisk="true">
<el-select
v-model="form.websiteSSLId"
:placeholder="$t('website.selectSSL')"
@change="changeSSl(form.websiteSSLId)"
class="p-w-400"
>
<el-option
v-for="(ssl, index) in ssls"
:key="index"
:label="ssl.primaryDomain"
:value="ssl.id"
:disabled="ssl.pem === ''"
></el-option>
</el-select>
</el-form-item>
</div>
<div v-if="form.type === 'manual'">
<el-form-item :label="$t('website.importType')" prop="type">
<el-select v-model="form.importType">
<el-option :label="$t('website.pasteSSL')" :value="'paste'"></el-option>
<el-option :label="$t('website.localSSL')" :value="'local'"></el-option>
</el-select>
</el-form-item>
<div v-if="form.importType === 'paste'">
<el-form-item :label="$t('website.privateKey')" prop="privateKey">
<el-input v-model="form.privateKey" :rows="6" type="textarea" />
<el-option :key="0" :label="$t('website.imported')" :value="0"></el-option>
<el-option
v-for="(acme, index) in acmeAccounts"
:key="index"
:label="acme.email"
:value="acme.id"
>
<span>
{{ acme.email }}
<el-tag class="ml-5">{{ getAccountName(acme.type) }}</el-tag>
</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item :label="$t('website.certificate')" prop="certificate">
<el-input v-model="form.certificate" :rows="6" type="textarea" />
<el-form-item :label="$t('website.ssl')" prop="websiteSSLId" :hide-required-asterisk="true">
<el-select
v-model="form.websiteSSLId"
:placeholder="$t('website.selectSSL')"
@change="changeSSl(form.websiteSSLId)"
class="p-w-400"
>
<el-option
v-for="(ssl, index) in ssls"
:key="index"
:label="ssl.primaryDomain"
:value="ssl.id"
:disabled="ssl.pem === ''"
></el-option>
</el-select>
</el-form-item>
</div>
<div v-if="form.importType === 'local'">
<el-form-item :label="$t('website.privateKeyPath')" prop="privateKeyPath">
<el-input v-model="form.privateKeyPath">
<template #prepend>
<el-button icon="Folder" @click="keyFileRef.acceptParams({ dir: false })" />
</template>
</el-input>
</el-form-item>
<el-form-item :label="$t('website.certificatePath')" prop="certificatePath">
<el-input v-model="form.certificatePath">
<template #prepend>
<el-button icon="Folder" @click="certFileRef.acceptParams({ dir: false })" />
</template>
</el-input>
<div v-if="form.type === 'manual'">
<el-form-item :label="$t('website.importType')" prop="type">
<el-select v-model="form.importType">
<el-option :label="$t('website.pasteSSL')" :value="'paste'"></el-option>
<el-option :label="$t('website.localSSL')" :value="'local'"></el-option>
</el-select>
</el-form-item>
<div v-if="form.importType === 'paste'">
<el-form-item :label="$t('website.privateKey')" prop="privateKey">
<el-input v-model="form.privateKey" :rows="6" type="textarea" />
</el-form-item>
<el-form-item :label="$t('website.certificate')" prop="certificate">
<el-input v-model="form.certificate" :rows="6" type="textarea" />
</el-form-item>
</div>
<div v-if="form.importType === 'local'">
<el-form-item :label="$t('website.privateKeyPath')" prop="privateKeyPath">
<el-input v-model="form.privateKeyPath">
<template #prepend>
<el-button icon="Folder" @click="keyFileRef.acceptParams({ dir: false })" />
</template>
</el-input>
</el-form-item>
<el-form-item :label="$t('website.certificatePath')" prop="certificatePath">
<el-input v-model="form.certificatePath">
<template #prepend>
<el-button
icon="Folder"
@click="certFileRef.acceptParams({ dir: false })"
/>
</template>
</el-input>
</el-form-item>
</div>
</div>
<el-form-item :label="' '" v-if="websiteSSL && websiteSSL.id > 0">
<el-descriptions :column="7" border direction="vertical">
<el-descriptions-item :label="$t('website.primaryDomain')">
{{ websiteSSL.primaryDomain }}
</el-descriptions-item>
<el-descriptions-item :label="$t('website.otherDomains')">
{{ websiteSSL.domains }}
</el-descriptions-item>
<el-descriptions-item :label="$t('website.brand')">
{{ websiteSSL.organization }}
</el-descriptions-item>
<el-descriptions-item :label="$t('ssl.provider')">
{{ getProvider(websiteSSL.provider) }}
</el-descriptions-item>
<el-descriptions-item
:label="$t('ssl.acmeAccount')"
v-if="websiteSSL.acmeAccount && websiteSSL.provider !== 'manual'"
>
{{ websiteSSL.acmeAccount.email }}
</el-descriptions-item>
<el-descriptions-item :label="$t('website.expireDate')">
{{ dateFormatSimple(websiteSSL.expireDate) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('website.remark')">
{{ websiteSSL.description }}
</el-descriptions-item>
</el-descriptions>
</el-form-item>
<el-divider content-position="left">{{ $t('website.SSLProConfig') }}</el-divider>
<el-form-item :label="$t('website.supportProtocol')" prop="SSLProtocol">
<el-checkbox-group v-model="form.SSLProtocol">
<el-checkbox :value="'TLSv1.3'">{{ 'TLS 1.3' }}</el-checkbox>
<el-checkbox :value="'TLSv1.2'">{{ 'TLS 1.2' }}</el-checkbox>
<el-checkbox :value="'TLSv1.1'">
{{ 'TLS 1.1' + $t('website.notSecurity') }}
</el-checkbox>
<el-checkbox :value="'TLSv1'">
{{ 'TLS 1.0' + $t('website.notSecurity') }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item prop="algorithm" :label="$t('website.encryptionAlgorithm')">
<el-input type="textarea" :rows="3" v-model.trim="form.algorithm"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submit(httpsForm)">
{{ $t('commons.button.save') }}
</el-button>
</el-form-item>
</div>
<el-form-item :label="' '" v-if="websiteSSL && websiteSSL.id > 0">
<el-descriptions :column="7" border direction="vertical">
<el-descriptions-item :label="$t('website.primaryDomain')">
{{ websiteSSL.primaryDomain }}
</el-descriptions-item>
<el-descriptions-item :label="$t('website.otherDomains')">
{{ websiteSSL.domains }}
</el-descriptions-item>
<el-descriptions-item :label="$t('website.brand')">
{{ websiteSSL.organization }}
</el-descriptions-item>
<el-descriptions-item :label="$t('ssl.provider')">
{{ getProvider(websiteSSL.provider) }}
</el-descriptions-item>
<el-descriptions-item
:label="$t('ssl.acmeAccount')"
v-if="websiteSSL.acmeAccount && websiteSSL.provider !== 'manual'"
>
{{ websiteSSL.acmeAccount.email }}
</el-descriptions-item>
<el-descriptions-item :label="$t('website.expireDate')">
{{ dateFormatSimple(websiteSSL.expireDate) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('website.remark')">
{{ websiteSSL.description }}
</el-descriptions-item>
</el-descriptions>
</el-form-item>
<el-divider content-position="left">{{ $t('website.SSLProConfig') }}</el-divider>
<el-form-item :label="$t('website.supportProtocol')" prop="SSLProtocol">
<el-checkbox-group v-model="form.SSLProtocol">
<el-checkbox :value="'TLSv1.3'">{{ 'TLS 1.3' }}</el-checkbox>
<el-checkbox :value="'TLSv1.2'">{{ 'TLS 1.2' }}</el-checkbox>
<el-checkbox :value="'TLSv1.1'">
{{ 'TLS 1.1' + $t('website.notSecurity') }}
</el-checkbox>
<el-checkbox :value="'TLSv1'">
{{ 'TLS 1.0' + $t('website.notSecurity') }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item prop="algorithm" :label="$t('website.encryptionAlgorithm')">
<el-input type="textarea" :rows="3" v-model.trim="form.algorithm"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submit(httpsForm)">
{{ $t('commons.button.save') }}
</el-button>
</el-form-item>
</div>
<div v-else>
<el-alert :closable="false">
<template #default>
<span style="white-space: pre-line">{{ $t('website.SSLHelper') }}</span>
</template>
</el-alert>
</div>
</el-collapse-transition>
</el-form>
</el-col>
</el-row>

View file

@ -122,6 +122,38 @@
</div>
</el-collapse-transition>
<el-divider content-position="left">{{ $t('website.cors') }}</el-divider>
<div class="flex justify-between items-center py-3">
<div class="flex flex-col gap-1">
<span class="font-medium">{{ $t('website.enableCors') }}</span>
</div>
<el-switch v-model="proxy.cors" size="large" @change="changeCors(proxy.cors)" />
</div>
<el-collapse-transition>
<div v-if="proxy.cors" class="mt-4">
<el-form-item :label="$t('website.allowOrigins')" prop="allowOrigins">
<el-input v-model="proxy.allowOrigins" type="textarea" placeholder="*"></el-input>
</el-form-item>
<el-form-item :label="$t('website.allowMethods')" prop="allowMethods">
<el-input
v-model="proxy.allowMethods"
type="textarea"
placeholder="GET,POST,OPTIONS,PUT,DELETE"
></el-input>
</el-form-item>
<el-form-item :label="$t('website.allowHeaders')" prop="allowHeaders">
<el-input v-model="proxy.allowHeaders" type="textarea"></el-input>
</el-form-item>
<el-form-item :label="$t('website.allowCredentials')" prop="allowCredentials">
<el-switch v-model="proxy.allowCredentials" />
</el-form-item>
<el-form-item :label="$t('website.preflight')" prop="preflight">
<el-switch v-model="proxy.preflight" />
<span class="input-help">{{ $t('website.preflightHleper') }}</span>
</el-form-item>
</div>
</el-collapse-transition>
<el-divider content-position="left">{{ $t('website.replace') }}</el-divider>
<div>
@ -194,6 +226,7 @@ const rules = ref({
proxyPass: [Rules.requiredInput],
proxyHost: [Rules.requiredInput],
proxyAddress: [Rules.requiredInput],
allowOrigins: [Rules.requiredInput],
});
const open = ref(false);
const loading = ref(false);
@ -219,6 +252,12 @@ const initData = (): Website.ProxyConfig => ({
proxySSLName: '',
serverCacheTime: 10,
serverCacheUnit: 'm',
cors: false,
allowOrigins: '*',
allowMethods: 'GET,POST,OPTIONS,PUT,DELETE',
allowHeaders: '',
allowCredentials: false,
preflight: true,
});
let proxy = ref(initData());
const replaces = ref<any>([]);
@ -264,6 +303,22 @@ const changeCache = (cache: boolean) => {
}
};
const changeCors = (cors: boolean) => {
if (cors) {
proxy.value.allowOrigins = '*';
proxy.value.allowMethods = 'GET,POST,OPTIONS,PUT,DELETE';
proxy.value.allowHeaders = '';
proxy.value.allowCredentials = false;
proxy.value.preflight = true;
} else {
proxy.value.allowOrigins = '';
proxy.value.allowMethods = '';
proxy.value.allowHeaders = '';
proxy.value.allowCredentials = false;
proxy.value.preflight = true;
}
};
const addReplaces = () => {
replaces.value.push({ key: '', value: '' });
};