mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-22 16:00:51 +00:00
feat: add cors config for website (#10607)
This commit is contained in:
@@ -1161,3 +1161,45 @@ func (b *BaseApi) BatchOpWebsites(c *gin.Context) {
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Get CORS Config
|
||||
// @Accept json
|
||||
// @Param id path int true "id"
|
||||
// @Success 200 {object} request.CorsConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/cors/{id} [get]
|
||||
func (b *BaseApi) GetCORSConfig(c *gin.Context) {
|
||||
id, err := helper.GetParamID(c)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
res, err := websiteService.GetCors(id)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update CORS Config
|
||||
// @Accept json
|
||||
// @Param request body request.CorsConfigReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/cors/update [post]
|
||||
func (b *BaseApi) UpdateCORSConfig(c *gin.Context) {
|
||||
var req request.CorsConfigReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateCors(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
@@ -211,30 +211,39 @@ 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"`
|
||||
Cors bool `json:"cors"`
|
||||
AllowOrigins string `json:"allowOrigins"`
|
||||
AllowMethods string `json:"allowMethods"`
|
||||
AllowHeaders string `json:"allowHeaders"`
|
||||
AllowCredentials bool `json:"allowCredentials"`
|
||||
Preflight bool `json:"preflight"`
|
||||
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"`
|
||||
CorsConfig
|
||||
}
|
||||
|
||||
type CorsConfig struct {
|
||||
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 CorsConfigReq struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
CorsConfig
|
||||
}
|
||||
|
||||
type WebsiteProxyReq struct {
|
||||
|
||||
@@ -96,6 +96,9 @@ type IWebsiteService interface {
|
||||
ClearProxyCache(req request.NginxCommonReq) error
|
||||
DeleteProxy(req request.WebsiteProxyDel) (err error)
|
||||
|
||||
UpdateCors(req request.CorsConfigReq) error
|
||||
GetCors(websiteID uint) (*request.CorsConfig, error)
|
||||
|
||||
GetAntiLeech(id uint) (*response.NginxAntiLeechRes, error)
|
||||
UpdateAntiLeech(req request.NginxAntiLeechUpdate) (err error)
|
||||
|
||||
@@ -2018,6 +2021,65 @@ func (w WebsiteService) DeleteProxy(req request.WebsiteProxyDel) (err error) {
|
||||
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
||||
}
|
||||
|
||||
func (w WebsiteService) UpdateCors(req request.CorsConfigReq) error {
|
||||
website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params := []dto.NginxParam{
|
||||
{Name: "add_header", Params: []string{"Access-Control-Allow-Origin"}},
|
||||
{Name: "add_header", Params: []string{"Access-Control-Allow-Methods"}},
|
||||
{Name: "add_header", Params: []string{"Access-Control-Allow-Headers"}},
|
||||
{Name: "add_header", Params: []string{"Access-Control-Allow-Credentials"}},
|
||||
{Name: "if", Params: []string{"(", "$request_method", "=", "'OPTIONS'", ")"}},
|
||||
}
|
||||
if err := deleteNginxConfig(constant.NginxScopeServer, params, &website); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.Cors {
|
||||
return updateWebsiteConfig(website, func(server *components.Server) error {
|
||||
server.UpdateDirective("add_header", []string{"Access-Control-Allow-Origin", req.AllowOrigins, "always"})
|
||||
if req.AllowMethods != "" {
|
||||
server.UpdateDirective("add_header", []string{"Access-Control-Allow-Methods", req.AllowMethods, "always"})
|
||||
}
|
||||
if req.AllowHeaders != "" {
|
||||
server.UpdateDirective("add_header", []string{"Access-Control-Allow-Headers", req.AllowHeaders, "always"})
|
||||
}
|
||||
if req.AllowCredentials {
|
||||
server.UpdateDirective("add_header", []string{"Access-Control-Allow-Credentials", "true", "always"})
|
||||
}
|
||||
if req.Preflight {
|
||||
server.AddCorsOption()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w WebsiteService) GetCors(websiteID uint) (*request.CorsConfig, error) {
|
||||
website, err := websiteRepo.GetFirst(repo.WithByID(websiteID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server, err := getServer(website)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if server == nil {
|
||||
return nil, nil
|
||||
}
|
||||
cors := &request.CorsConfig{
|
||||
Cors: server.Cors,
|
||||
AllowOrigins: server.AllowOrigins,
|
||||
AllowMethods: server.AllowMethods,
|
||||
AllowHeaders: server.AllowHeaders,
|
||||
AllowCredentials: server.AllowCredentials,
|
||||
Preflight: server.Preflight,
|
||||
}
|
||||
return cors, nil
|
||||
}
|
||||
|
||||
func (w WebsiteService) GetAuthBasics(req request.NginxAuthReq) (res response.NginxAuthRes, err error) {
|
||||
var (
|
||||
website model.Website
|
||||
|
||||
@@ -1303,6 +1303,7 @@ const (
|
||||
SiteRootAuthBasicPath = "SiteRootAuthBasicPath"
|
||||
SitePathAuthBasicDir = "SitePathAuthBasicDir"
|
||||
SiteUpstreamDir = "SiteUpstreamDir"
|
||||
SiteCorsPath = "SiteCorsPath"
|
||||
)
|
||||
|
||||
func GetSitePath(website model.Website, confType string) string {
|
||||
@@ -1333,6 +1334,8 @@ func GetSitePath(website model.Website, confType string) string {
|
||||
return path.Join(GteSiteDir(website.Alias), "path_auth")
|
||||
case SiteUpstreamDir:
|
||||
return path.Join(GteSiteDir(website.Alias), "upstream")
|
||||
case SiteCorsPath:
|
||||
return path.Join(GteSiteDir(website.Alias), "cors", "cors.conf")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -1481,3 +1484,53 @@ func ParseDomain(domain string) (*model.WebsiteDomain, error) {
|
||||
Port: port,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func updateWebsiteConfig(website model.Website, updateFunc func(server *components.Server) error) error {
|
||||
configPath := GetSitePath(website, SiteConf)
|
||||
nginxContent, err := files.NewFileOp().GetContent(configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config, err := parser.NewStringParser(string(nginxContent)).Parse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.FilePath = configPath
|
||||
servers := config.FindServers()
|
||||
if len(servers) == 0 {
|
||||
return errors.New("nginx config is not valid")
|
||||
}
|
||||
server := servers[0]
|
||||
if err := updateFunc(server); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
return err
|
||||
}
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nginxCheckAndReload(string(nginxContent), configPath, nginxInstall.ContainerName)
|
||||
}
|
||||
|
||||
func getServer(website model.Website) (*components.Server, error) {
|
||||
configPath := GetSitePath(website, SiteConf)
|
||||
nginxContent, err := files.NewFileOp().GetContent(configPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config, err := parser.NewStringParser(string(nginxContent)).Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
servers := config.FindServers()
|
||||
if len(servers) == 0 {
|
||||
return nil, errors.New("nginx config is not valid")
|
||||
}
|
||||
server := servers[0]
|
||||
return server, nil
|
||||
}
|
||||
|
||||
@@ -61,6 +61,9 @@ func (a *WebsiteRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
websiteRouter.POST("/auths/path", baseApi.GetPathAuthConfig)
|
||||
websiteRouter.POST("/auths/path/update", baseApi.UpdatePathAuthConfig)
|
||||
|
||||
websiteRouter.GET("/cors/:id", baseApi.GetCORSConfig)
|
||||
websiteRouter.POST("/cors/update", baseApi.UpdateCORSConfig)
|
||||
|
||||
websiteRouter.POST("/leech", baseApi.GetAntiLeech)
|
||||
websiteRouter.POST("/leech/update", baseApi.UpdateAntiLeech)
|
||||
|
||||
|
||||
@@ -5,10 +5,16 @@ import (
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
Comment string
|
||||
Listens []*ServerListen
|
||||
Directives []IDirective
|
||||
Line int
|
||||
Comment string
|
||||
Listens []*ServerListen
|
||||
Directives []IDirective
|
||||
Line int
|
||||
Cors bool
|
||||
AllowMethods string
|
||||
AllowHeaders string
|
||||
AllowOrigins string
|
||||
AllowCredentials bool
|
||||
Preflight bool
|
||||
}
|
||||
|
||||
func (s *Server) GetCodeBlock() string {
|
||||
@@ -25,6 +31,28 @@ func NewServer(directive IDirective) (*Server, error) {
|
||||
switch dir.GetName() {
|
||||
case "listen":
|
||||
server.Listens = append(server.Listens, NewServerListen(dir.GetParameters(), dir.GetLine()))
|
||||
case "add_header":
|
||||
params := dir.GetParameters()
|
||||
if params[0] == "Access-Control-Allow-Origin" {
|
||||
server.Cors = true
|
||||
server.AllowOrigins = params[1]
|
||||
}
|
||||
if params[0] == "Access-Control-Allow-Methods" {
|
||||
server.AllowMethods = params[1]
|
||||
}
|
||||
if params[0] == "Access-Control-Allow-Headers" {
|
||||
server.AllowHeaders = params[1]
|
||||
}
|
||||
if params[0] == "Access-Control-Allow-Credentials" && params[1] == "true" {
|
||||
server.AllowCredentials = true
|
||||
}
|
||||
server.Directives = append(server.Directives, dir)
|
||||
case "if":
|
||||
params := dir.GetParameters()
|
||||
if params[0] == "(" && params[1] == "$request_method" && params[2] == `=` && params[3] == `'OPTIONS'` && params[4] == ")" {
|
||||
server.Preflight = true
|
||||
}
|
||||
server.Directives = append(server.Directives, dir)
|
||||
default:
|
||||
server.Directives = append(server.Directives, dir)
|
||||
}
|
||||
@@ -479,3 +507,26 @@ func (s *Server) UpdateAllowIPs(ips []string) {
|
||||
s.Directives = append(s.Directives, ipDirectives...)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) AddCorsOption() {
|
||||
newDir := &Directive{
|
||||
Name: "if",
|
||||
Parameters: []string{"(", "$request_method", "=", "'OPTIONS'", ")"},
|
||||
Block: &Block{},
|
||||
}
|
||||
block := &Block{}
|
||||
block.AppendDirectives(&Directive{
|
||||
Name: "return",
|
||||
Parameters: []string{"204"},
|
||||
})
|
||||
newDir.Block = block
|
||||
directives := s.GetDirectives()
|
||||
newDirectives := make([]IDirective, 0)
|
||||
for _, dir := range directives {
|
||||
if dir.GetName() != "listen" {
|
||||
newDirectives = append(newDirectives, dir)
|
||||
}
|
||||
}
|
||||
newDirectives = append(newDirectives, newDir)
|
||||
s.Directives = newDirectives
|
||||
}
|
||||
|
||||
@@ -681,4 +681,17 @@ export namespace Website {
|
||||
operate: string;
|
||||
taskID: string;
|
||||
}
|
||||
|
||||
export interface CorsConfig {
|
||||
cors: boolean;
|
||||
allowOrigins: string;
|
||||
allowMethods: string;
|
||||
allowHeaders: string;
|
||||
allowCredentials: boolean;
|
||||
preflight: boolean;
|
||||
}
|
||||
|
||||
export interface CorsConfigReq extends CorsConfig {
|
||||
websiteID: number;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,3 +359,11 @@ export const execComposer = (req: Website.ExecComposer) => {
|
||||
export const batchOpreate = (req: Website.BatchOperate) => {
|
||||
return http.post(`/websites/batch/operate`, req);
|
||||
};
|
||||
|
||||
export const getCorsConfig = (id: number) => {
|
||||
return http.get<Website.CorsConfig>(`/websites/cors/${id}`);
|
||||
};
|
||||
|
||||
export const updateCorsConfig = (req: Website.CorsConfigReq) => {
|
||||
return http.post(`/websites/cors/update`, req);
|
||||
};
|
||||
|
||||
@@ -2414,7 +2414,7 @@ const message = {
|
||||
ipFromExample3: '如果不确定,可以填 0.0.0.0/0(ipv4) ::/0(ipv6) [注意:允许任意来源 IP 不安全]',
|
||||
http3Helper:
|
||||
'HTTP/3 是 HTTP/2 的升级版本,提供更快的连接速度和更好的性能,但是不是所有浏览器都支持 HTTP/3,开启后可能会导致部分浏览器无法访问',
|
||||
cors: '跨域访问(CORS)',
|
||||
cors: '跨域访问',
|
||||
enableCors: '开启跨域',
|
||||
allowOrigins: '允许访问的域名',
|
||||
allowMethods: '允许的请求方法',
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<el-row>
|
||||
<el-col :xs="24" :sm="18" :md="18" :lg="14" :xl="14">
|
||||
<el-form
|
||||
ref="corsForm"
|
||||
label-position="right"
|
||||
label-width="150px"
|
||||
:model="corsSetting"
|
||||
:rules="rules"
|
||||
v-loading="loading"
|
||||
>
|
||||
<CorsSetting
|
||||
v-model="corsSetting.cors"
|
||||
:config="{
|
||||
allowOrigins: corsSetting.allowOrigins,
|
||||
allowMethods: corsSetting.allowMethods,
|
||||
allowHeaders: corsSetting.allowHeaders,
|
||||
allowCredentials: corsSetting.allowCredentials,
|
||||
preflight: corsSetting.preflight,
|
||||
}"
|
||||
enable-size="small"
|
||||
@update:config="updateonfig"
|
||||
></CorsSetting>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submit()">
|
||||
{{ $t('commons.button.save') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getCorsConfig, updateCorsConfig } from '@/api/modules/website';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import CorsSetting from '@/views/website/website/cors/index.vue';
|
||||
import { FormInstance } from 'element-plus';
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const rules = ref({
|
||||
allowOrigins: [Rules.requiredInput],
|
||||
});
|
||||
|
||||
const corsForm = ref<FormInstance>();
|
||||
const loading = ref(false);
|
||||
|
||||
const corsSetting = ref({
|
||||
cors: false,
|
||||
allowOrigins: '*',
|
||||
allowMethods: 'GET,POST,OPTIONS,PUT,DELETE',
|
||||
allowHeaders: '',
|
||||
allowCredentials: false,
|
||||
preflight: true,
|
||||
websiteID: props.id,
|
||||
});
|
||||
|
||||
const updateonfig = (config: any) => {
|
||||
corsSetting.value.allowOrigins = config.allowOrigins;
|
||||
corsSetting.value.allowMethods = config.allowMethods;
|
||||
corsSetting.value.allowHeaders = config.allowHeaders;
|
||||
corsSetting.value.allowCredentials = config.allowCredentials;
|
||||
corsSetting.value.preflight = config.preflight;
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
const isValid = await corsForm.value?.validate();
|
||||
if (!isValid) return;
|
||||
|
||||
corsSetting.value.websiteID = props.id;
|
||||
try {
|
||||
loading.value = true;
|
||||
await updateCorsConfig(corsSetting.value);
|
||||
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
|
||||
} catch (error) {
|
||||
return;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const getCors = () => {
|
||||
getCorsConfig(props.id).then((res: any) => {
|
||||
if (res.data) {
|
||||
corsSetting.value = res.data;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getCors();
|
||||
});
|
||||
</script>
|
||||
@@ -1,40 +1,43 @@
|
||||
<template>
|
||||
<div :style="{ '--main-height': mainHeight + 'px' }">
|
||||
<el-tabs tab-position="left" v-model="tabIndex" v-if="id > 0" class="custom-tabs" ref="tabsRef">
|
||||
<el-tab-pane :label="$t('website.domainConfig')">
|
||||
<el-tab-pane :label="$t('website.domainConfig')" name="0">
|
||||
<Domain :key="id" :id="id" v-if="tabIndex == '0'"></Domain>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('website.sitePath')">
|
||||
<el-tab-pane :label="$t('website.sitePath')" name="1">
|
||||
<SitePath :id="id" v-if="tabIndex == '1'"></SitePath>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('website.defaultDoc')">
|
||||
<el-tab-pane :label="$t('website.defaultDoc')" name="2">
|
||||
<Default :id="id" v-if="tabIndex == '2'"></Default>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('website.rate')">
|
||||
<el-tab-pane :label="$t('website.rate')" name="3">
|
||||
<LimitConn :id="id" v-if="tabIndex == '3'"></LimitConn>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('website.proxy')">
|
||||
<el-tab-pane :label="$t('website.proxy')" name="4">
|
||||
<Proxy :id="id" v-if="tabIndex == '4'"></Proxy>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('website.loadBalance')">
|
||||
<el-tab-pane :label="$t('website.loadBalance')" name="5">
|
||||
<LoadBalance :id="id" v-if="tabIndex == '5'"></LoadBalance>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('website.basicAuth')">
|
||||
<el-tab-pane :label="$t('website.basicAuth')" name="6">
|
||||
<AuthBasic :id="id" v-if="tabIndex == '6'"></AuthBasic>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="'HTTPS'">
|
||||
<el-tab-pane :label="$t('website.cors')" name="16">
|
||||
<Cors :id="id" v-if="tabIndex == '16'"></Cors>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="'HTTPS'" name="7">
|
||||
<HTTPS :id="id" v-if="tabIndex == '7'"></HTTPS>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('website.realIP')">
|
||||
<el-tab-pane :label="$t('website.realIP')" name="8">
|
||||
<RealIP :id="id" v-if="tabIndex == '8'"></RealIP>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('website.rewrite')">
|
||||
<el-tab-pane :label="$t('website.rewrite')" name="9">
|
||||
<Rewrite :id="id" v-if="tabIndex == '9'"></Rewrite>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('website.antiLeech')">
|
||||
<el-tab-pane :label="$t('website.antiLeech')" name="10">
|
||||
<AntiLeech :id="id" v-if="tabIndex == '10'"></AntiLeech>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('website.redirect')">
|
||||
<el-tab-pane :label="$t('website.redirect')" name="11">
|
||||
<Redirect :id="id" v-if="tabIndex == '11'"></Redirect>
|
||||
</el-tab-pane>
|
||||
|
||||
@@ -77,6 +80,7 @@ import LoadBalance from './load-balance/index.vue';
|
||||
import PHP from './php/index.vue';
|
||||
import RealIP from './real-ip/index.vue';
|
||||
import Resource from './resource/index.vue';
|
||||
import Cors from './cors/index.vue';
|
||||
|
||||
const props = defineProps({
|
||||
website: {
|
||||
|
||||
@@ -123,39 +123,20 @@
|
||||
</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>
|
||||
<CorsSetting
|
||||
v-model="proxy.cors"
|
||||
:config="{
|
||||
allowOrigins: proxy.allowOrigins,
|
||||
allowMethods: proxy.allowMethods,
|
||||
allowHeaders: proxy.allowHeaders,
|
||||
allowCredentials: proxy.allowCredentials,
|
||||
preflight: proxy.preflight,
|
||||
}"
|
||||
enable-size="large"
|
||||
@update:config="updateCorsConfig"
|
||||
></CorsSetting>
|
||||
|
||||
<el-divider content-position="left">{{ $t('website.replace') }}</el-divider>
|
||||
|
||||
<div>
|
||||
<div v-for="(replace, index) in replaces" :key="index" class="mb-3">
|
||||
<el-row :gutter="16">
|
||||
@@ -217,6 +198,7 @@ import { Website } from '@/api/interface/website';
|
||||
import { Units } from '@/global/mimetype';
|
||||
import { isDomain } from '@/utils/util';
|
||||
import { Delete, Plus } from '@element-plus/icons-vue';
|
||||
import CorsSetting from '@/views/website/website/cors/index.vue';
|
||||
|
||||
const proxyForm = ref<FormInstance>();
|
||||
const rules = ref({
|
||||
@@ -303,22 +285,6 @@ 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: '' });
|
||||
};
|
||||
@@ -335,6 +301,14 @@ const getProxyHost = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const updateCorsConfig = (config: any) => {
|
||||
proxy.value.allowOrigins = config.allowOrigins;
|
||||
proxy.value.allowMethods = config.allowMethods;
|
||||
proxy.value.allowHeaders = config.allowHeaders;
|
||||
proxy.value.allowCredentials = config.allowCredentials;
|
||||
proxy.value.preflight = config.preflight;
|
||||
};
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid) => {
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex justify-between items-center py-3" v-if="enableSize == 'large'">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="font-medium">{{ $t('website.enableCors') }}</span>
|
||||
</div>
|
||||
<el-switch v-model="corsEnabled" size="large" @change="handleCorsChange" />
|
||||
</div>
|
||||
|
||||
<el-form-item :label="$t('website.enableCors')" v-if="enableSize == 'small'">
|
||||
<el-switch v-model="corsEnabled" size="large" @change="handleCorsChange" />
|
||||
</el-form-item>
|
||||
|
||||
<el-collapse-transition>
|
||||
<div v-if="corsEnabled" class="mt-4">
|
||||
<el-form-item :label="$t('website.allowOrigins')" prop="allowOrigins">
|
||||
<el-input
|
||||
v-model="corsConfig.allowOrigins"
|
||||
type="textarea"
|
||||
placeholder="*"
|
||||
@input="emitUpdate"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('website.allowMethods')" prop="allowMethods">
|
||||
<el-input
|
||||
v-model="corsConfig.allowMethods"
|
||||
type="textarea"
|
||||
placeholder="GET,POST,OPTIONS,PUT,DELETE"
|
||||
@input="emitUpdate"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('website.allowHeaders')" prop="allowHeaders">
|
||||
<el-input v-model="corsConfig.allowHeaders" type="textarea" @input="emitUpdate"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('website.allowCredentials')" prop="allowCredentials">
|
||||
<el-switch v-model="corsConfig.allowCredentials" @change="emitUpdate" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('website.preflight')" prop="preflight">
|
||||
<el-switch v-model="corsConfig.preflight" @change="emitUpdate" />
|
||||
<span class="input-help">{{ $t('website.preflightHleper') }}</span>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-collapse-transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
interface CorsConfig {
|
||||
allowOrigins: string;
|
||||
allowMethods: string;
|
||||
allowHeaders: string;
|
||||
allowCredentials: boolean;
|
||||
preflight: boolean;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean;
|
||||
config: CorsConfig;
|
||||
enableSize: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
modelValue: false,
|
||||
config: () => ({
|
||||
allowOrigins: '*',
|
||||
allowMethods: 'GET,POST,OPTIONS,PUT,DELETE',
|
||||
allowHeaders: '',
|
||||
allowCredentials: false,
|
||||
preflight: true,
|
||||
}),
|
||||
enableSize: 'large',
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'update:config']);
|
||||
|
||||
const corsEnabled = ref(props.modelValue);
|
||||
const corsConfig = ref<CorsConfig>({ ...props.config });
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
corsEnabled.value = val;
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.config,
|
||||
(val) => {
|
||||
corsConfig.value = { ...val };
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
const handleCorsChange = (enabled: boolean) => {
|
||||
emit('update:modelValue', enabled);
|
||||
|
||||
if (enabled) {
|
||||
corsConfig.value = {
|
||||
allowOrigins: '*',
|
||||
allowMethods: 'GET,POST,OPTIONS,PUT,DELETE',
|
||||
allowHeaders: '',
|
||||
allowCredentials: false,
|
||||
preflight: true,
|
||||
};
|
||||
} else {
|
||||
corsConfig.value = {
|
||||
allowOrigins: '',
|
||||
allowMethods: '',
|
||||
allowHeaders: '',
|
||||
allowCredentials: false,
|
||||
preflight: true,
|
||||
};
|
||||
}
|
||||
emitUpdate();
|
||||
};
|
||||
|
||||
const emitUpdate = () => {
|
||||
emit('update:config', corsConfig.value);
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user