mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-12-26 17:28:48 +08:00
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:
parent
5d83f21a26
commit
72a0e0200e
16 changed files with 449 additions and 194 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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.',
|
||||
|
|
|
|||
|
|
@ -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.',
|
||||
|
|
|
|||
|
|
@ -2514,6 +2514,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: 'Cookieの送信を許可する',
|
||||
preflight: 'プリフライトリクエストの高速応答',
|
||||
preflightHleper:
|
||||
'有効にすると、ブラウザがクロスオリジンのプリフライトリクエスト(OPTIONSリクエスト)を送信した場合、システムは自動的に204ステータスコードを返し、必要なクロスオリジン応答ヘッダーを設定します',
|
||||
|
||||
changeDatabase: 'データベースを切り替え',
|
||||
changeDatabaseHelper1: 'データベースの関連付けは、ウェブサイトのバックアップと復元に使用されます。',
|
||||
changeDatabaseHelper2: '別のデータベースに切り替えると、以前のバックアップが復元できなくなる可能性があります。',
|
||||
|
|
|
|||
|
|
@ -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: '다른 데이터베이스로 전환하면 이전 백업을 복원할 수 없게 될 수 있습니다.',
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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.',
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -2630,6 +2630,16 @@ const message = {
|
|||
'Emin değilseniz, 0.0.0.0/0 (ipv4) ::/0 (ipv6) girebilirsiniz [Not: Herhangi bir kaynak IP’ye izin vermek güvenli değildir]',
|
||||
http3Helper:
|
||||
'HTTP/3, HTTP/2’nin 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.',
|
||||
|
|
|
|||
|
|
@ -2421,6 +2421,15 @@ const message = {
|
|||
ipFromExample3: '如果不確定,可以填 0.0.0.0/0(ipv4) ::/0(ipv6) [注意:允許任意來源 IP 不安全]',
|
||||
http3Helper:
|
||||
'HTTP/3 是 HTTP/2 的升級版本,提供更快的連線速度和更好的效能,但並非所有瀏覽器都支援 HTTP/3,啟用後可能會導致部分瀏覽器無法訪問',
|
||||
cors: '跨域存取(CORS)',
|
||||
enableCors: '開啟跨域',
|
||||
allowOrigins: '允許存取的網域名稱',
|
||||
allowMethods: '允許的請求方法',
|
||||
allowHeaders: '允許的請求標頭',
|
||||
allowCredentials: '允許攜帶cookies',
|
||||
preflight: '預檢請求快速回應',
|
||||
preflightHleper:
|
||||
'開啟後,當瀏覽器發送跨域預檢請求(OPTIONS 請求)時,系統會自動返回 204 狀態碼並設定必要的跨域回應標頭',
|
||||
|
||||
changeDatabase: '切換資料庫',
|
||||
changeDatabaseHelper1: '資料庫關聯用於備份復原網站。',
|
||||
|
|
|
|||
|
|
@ -2413,6 +2413,15 @@ const message = {
|
|||
ipFromExample3: '如果不确定,可以填 0.0.0.0/0(ipv4) ::/0(ipv6) [注意:允许任意来源 IP 不安全]',
|
||||
http3Helper:
|
||||
'HTTP/3 是 HTTP/2 的升级版本,提供更快的连接速度和更好的性能,但是不是所有浏览器都支持 HTTP/3,开启后可能会导致部分浏览器无法访问',
|
||||
cors: '跨域访问(CORS)',
|
||||
enableCors: '开启跨域',
|
||||
allowOrigins: '允许访问的域名',
|
||||
allowMethods: '允许的请求方法',
|
||||
allowHeaders: '允许的请求头',
|
||||
allowCredentials: '允许携带cookies',
|
||||
preflight: '预检请求快速响应',
|
||||
preflightHleper:
|
||||
'开启后,当浏览器发送跨域预检请求(OPTIONS 请求)时,系统会自动返回 204 状态码并设置必要的跨域响应头',
|
||||
|
||||
changeDatabase: '切换数据库',
|
||||
changeDatabaseHelper1: '数据库关联用于备份恢复网站',
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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: '' });
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue