feat: 伪静态支持保存为模版 (#6663)
Some checks are pending
sync2gitee / repo-sync (push) Waiting to run

Refs https://github.com/1Panel-dev/1Panel/issues/705
This commit is contained in:
zhengkunwang 2024-10-09 21:56:58 +08:00 committed by GitHub
parent 958993f81f
commit 2565373db6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 278 additions and 30 deletions

View file

@ -1047,3 +1047,39 @@ func (b *BaseApi) ChangeWebsiteDatabase(c *gin.Context) {
}
helper.SuccessWithOutData(c)
}
// @Tags Website
// @Summary Operate custom rewrite
// @Description 编辑自定义重写模版
// @Accept json
// @Param request body request.CustomRewriteOperate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/rewrite/custom [post]
func (b *BaseApi) OperateCustomRewrite(c *gin.Context) {
var req request.CustomRewriteOperate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteService.OperateCustomRewrite(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags Website
// @Summary List custom rewrite
// @Description 获取自定义重写模版列表
// @Accept json
// @Success 200 {object} []string
// @Security ApiKeyAuth
// @Router /websites/rewrite/custom [get]
func (b *BaseApi) ListCustomRewrite(c *gin.Context) {
res, err := websiteService.ListCustomRewrite()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}

View file

@ -24,6 +24,12 @@ type NginxRewriteReq struct {
Name string `json:"name" validate:"required"`
}
type CustomRewriteOperate struct {
Operate string `json:"operate" validate:"required,oneof=create delete"`
Content string `json:"content"`
Name string `json:"name"`
}
type NginxRewriteUpdate struct {
WebsiteID uint `json:"websiteId" validate:"required"`
Name string `json:"name" validate:"required"`

View file

@ -134,7 +134,7 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
if err != nil {
return err
}
if err = fileOp.CopyFile(fmt.Sprintf("%s/%s.conf", tmpPath, website.Alias), GetSitePath(*website, SiteConfDir)); err != nil {
if err = fileOp.CopyFile(fmt.Sprintf("%s/%s.conf", tmpPath, website.Alias), GetOpenrestyDir(SiteConfDir)); err != nil {
return err
}

View file

@ -78,6 +78,9 @@ type IWebsiteService interface {
GetRewriteConfig(req request.NginxRewriteReq) (*response.NginxRewriteRes, error)
UpdateRewriteConfig(req request.NginxRewriteUpdate) error
OperateCustomRewrite(req request.CustomRewriteOperate) error
ListCustomRewrite() ([]string, error)
LoadWebsiteDirConfig(req request.WebsiteCommonReq) (*response.WebsiteDirConfig, error)
UpdateSiteDir(req request.WebsiteUpdateDir) error
UpdateSitePermission(req request.WebsiteUpdateDirPermission) error
@ -1431,9 +1434,11 @@ func (w WebsiteService) GetRewriteConfig(req request.NginxRewriteReq) (*response
}
} else {
rewriteFile := fmt.Sprintf("rewrite/%s.conf", strings.ToLower(req.Name))
contentByte, err = nginx_conf.Rewrites.ReadFile(rewriteFile)
if err != nil {
return nil, err
contentByte, _ = nginx_conf.Rewrites.ReadFile(rewriteFile)
if contentByte == nil {
customRewriteDir := GetOpenrestyDir(DefaultRewriteDir)
customRewriteFile := path.Join(customRewriteDir, fmt.Sprintf("%s.conf", strings.ToLower(req.Name)))
contentByte, err = files.NewFileOp().GetContent(customRewriteFile)
}
}
return &response.NginxRewriteRes{
@ -1441,6 +1446,47 @@ func (w WebsiteService) GetRewriteConfig(req request.NginxRewriteReq) (*response
}, err
}
func (w WebsiteService) OperateCustomRewrite(req request.CustomRewriteOperate) error {
rewriteDir := GetOpenrestyDir(DefaultRewriteDir)
fileOp := files.NewFileOp()
if !fileOp.Stat(rewriteDir) {
if err := fileOp.CreateDir(rewriteDir, 0755); err != nil {
return err
}
}
rewriteFile := path.Join(rewriteDir, fmt.Sprintf("%s.conf", req.Name))
switch req.Operate {
case "create":
if fileOp.Stat(rewriteFile) {
return buserr.New(constant.ErrNameIsExist)
}
return fileOp.WriteFile(rewriteFile, strings.NewReader(req.Content), 0755)
case "delete":
return fileOp.DeleteFile(rewriteFile)
}
return nil
}
func (w WebsiteService) ListCustomRewrite() ([]string, error) {
rewriteDir := GetOpenrestyDir(DefaultRewriteDir)
fileOp := files.NewFileOp()
if !fileOp.Stat(rewriteDir) {
return nil, nil
}
entries, err := os.ReadDir(rewriteDir)
if err != nil {
return nil, err
}
var res []string
for _, entry := range entries {
if entry.IsDir() {
continue
}
res = append(res, strings.TrimSuffix(entry.Name(), ".conf"))
}
return res, nil
}
func (w WebsiteService) UpdateSiteDir(req request.WebsiteUpdateDir) error {
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {

View file

@ -1194,19 +1194,21 @@ func GteSiteDir(alias string) string {
}
const (
SiteConf = "SiteConf"
SiteAccessLog = "access.log"
SiteErrorLog = "error.log"
WebsiteRootDir = "WebsiteRootDir"
SiteDir = "SiteDir"
SiteIndexDir = "SiteIndexDir"
SiteProxyDir = "SiteProxyDir"
SiteSSLDir = "SiteSSLDir"
SiteReWritePath = "SiteReWritePath"
SiteRedirectDir = "SiteRedirectDir"
SiteCacheDir = "SiteCacheDir"
SiteConfDir = "SiteConfDir"
SitesRootDir = "SitesRootDir"
SiteConf = "SiteConf"
SiteAccessLog = "access.log"
SiteErrorLog = "error.log"
WebsiteRootDir = "WebsiteRootDir"
SiteDir = "SiteDir"
SiteIndexDir = "SiteIndexDir"
SiteProxyDir = "SiteProxyDir"
SiteSSLDir = "SiteSSLDir"
SiteReWritePath = "SiteReWritePath"
SiteRedirectDir = "SiteRedirectDir"
SiteCacheDir = "SiteCacheDir"
SiteConfDir = "SiteConfDir"
SitesRootDir = "SitesRootDir"
DefaultDir = "DefaultDir"
DefaultRewriteDir = "DefaultRewriteDir"
)
func GetSitePath(website model.Website, confType string) string {
@ -1217,8 +1219,6 @@ func GetSitePath(website model.Website, confType string) string {
return path.Join(GteSiteDir(website.Alias), "log", "access.log")
case SiteErrorLog:
return path.Join(GteSiteDir(website.Alias), "log", "error.log")
case WebsiteRootDir:
return GetWebSiteRootDir()
case SiteDir:
return GteSiteDir(website.Alias)
case SiteIndexDir:
@ -1233,10 +1233,22 @@ func GetSitePath(website model.Website, confType string) string {
return path.Join(GteSiteDir(website.Alias), "rewrite", website.Alias+".conf")
case SiteRedirectDir:
return path.Join(GteSiteDir(website.Alias), "redirect")
}
return ""
}
func GetOpenrestyDir(confType string) string {
switch confType {
case WebsiteRootDir:
return GetWebSiteRootDir()
case SiteConfDir:
return path.Join(GetWebSiteRootDir(), "conf.d")
case SitesRootDir:
return path.Join(GetWebSiteRootDir(), "sites")
case DefaultDir:
return path.Join(GetWebSiteRootDir(), "default")
case DefaultRewriteDir:
return path.Join(GetWebSiteRootDir(), "default", "rewrite")
}
return ""
}

View file

@ -41,6 +41,8 @@ func (a *WebsiteRouter) InitRouter(Router *gin.RouterGroup) {
websiteRouter.POST("/rewrite", baseApi.GetRewriteConfig)
websiteRouter.POST("/rewrite/update", baseApi.UpdateRewriteConfig)
websiteRouter.POST("/rewrite/custom", baseApi.OperateCustomRewrite)
websiteRouter.GET("/rewrite/custom", baseApi.ListCustomRewrite)
websiteRouter.POST("/dir/update", baseApi.UpdateSiteDir)
websiteRouter.POST("/dir/permission", baseApi.UpdateSiteDirPermission)

View file

@ -350,6 +350,12 @@ export namespace Website {
content: string;
}
export interface CustomRewirte {
operate: string;
name: string;
content: string;
}
export interface DirUpdate {
id: number;
siteDir: string;

View file

@ -331,3 +331,11 @@ export const GetWebsiteDatabase = () => {
export const ChangeDatabase = (req: Website.ChangeDatabase) => {
return http.post(`/websites/databases`, req);
};
export const OperateCustomRewrite = (req: Website.CustomRewirte) => {
return http.post(`/websites/rewrite/custom`, req);
};
export const ListCustomRewrite = () => {
return http.get<string[]>(`/websites/rewrite/custom`);
};

View file

@ -2244,6 +2244,7 @@ const message = {
changeDatabase: 'Change Database',
changeDatabaseHelper1: 'Database association is used for backing up and restoring the website.',
changeDatabaseHelper2: 'Switching to another database will cause previous backups to be unrecoverable.',
saveCustom: 'Save as Template',
},
php: {
short_open_tag: 'Short tag support',

View file

@ -2091,6 +2091,7 @@ const message = {
changeDatabase: '切換資料庫',
changeDatabaseHelper1: '資料庫關聯用於備份恢復網站',
changeDatabaseHelper2: '切換其他資料庫會導致以前的備份無法恢復',
saveCustom: '另存为模版',
},
php: {
short_open_tag: '短標簽支持',

View file

@ -2089,6 +2089,7 @@ const message = {
changeDatabase: '切换数据库',
changeDatabaseHelper1: '数据库关联用于备份恢复网站',
changeDatabaseHelper2: '切换其他数据库会导致以前的备份无法恢复',
saveCustom: '另存为模版',
},
php: {
short_open_tag: '短标签支持',

View file

@ -0,0 +1,72 @@
<template>
<DrawerPro v-model="open" :header="$t('website.saveCustom')" :back="handleClose">
<el-form ref="rewriteForm" label-position="top" :model="req" :rules="rules">
<el-form-item :label="$t('commons.table.name')" prop="name">
<el-input v-model="req.name"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
<el-button type="primary" @click="submit(rewriteForm)" :disabled="loading">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</DrawerPro>
</template>
<script lang="ts" setup>
import { OperateCustomRewrite } from '@/api/modules/website';
import i18n from '@/lang';
import { FormInstance } from 'element-plus';
import { ref } from 'vue';
import { MsgSuccess } from '@/utils/message';
import { Rules } from '@/global/form-rules';
const rewriteForm = ref<FormInstance>();
const open = ref(false);
const loading = ref(false);
const req = ref({
name: '',
operate: 'create',
content: '',
});
const rules = ref({
name: [Rules.requiredInput],
});
const em = defineEmits(['close']);
const handleClose = () => {
rewriteForm.value?.resetFields();
open.value = false;
em('close', false);
};
const acceptParams = async (conetnt: string) => {
req.value.content = conetnt;
open.value = true;
};
const submit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (!valid) {
return;
}
loading.value = true;
OperateCustomRewrite(req.value)
.then(() => {
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
handleClose();
})
.finally(() => {
loading.value = false;
});
});
};
defineExpose({
acceptParams,
});
</script>

View file

@ -4,11 +4,20 @@
<el-select v-model="req.name" filterable @change="getRewriteConfig(req.name)" class="p-w-200">
<el-option :label="$t('website.current')" :value="'current'"></el-option>
<el-option
v-for="(rewrite, index) in Rewrites"
v-for="(rewrite, index) in rewrites"
:key="index"
:label="rewrite"
:value="rewrite"
></el-option>
:label="rewrite.name"
:value="rewrite.name"
>
<span>{{ rewrite.name }}</span>
<el-button
class="float-right mt-1.5"
v-if="rewrite.resource == 'custom'"
link
icon="Close"
@click="deleteCustomRewrite(rewrite.name)"
></el-button>
</el-option>
</el-select>
</el-form-item>
<el-text type="warning">{{ $t('website.rewriteHelper2') }}</el-text>
@ -20,43 +29,54 @@
<el-button type="primary" @click="submit()">
{{ $t('nginx.saveAndReload') }}
</el-button>
<el-button type="primary" @click="operateCustomRewrite()" :disabled="content == ''">
{{ $t('website.saveCustom') }}
</el-button>
</div>
<CustomRewrite ref="customRef" @close="init()" />
<OpDialog ref="deleteRef" @search="init()" />
</div>
</template>
<script lang="ts" setup>
import { computed, nextTick, onMounted, reactive, ref } from 'vue';
import { GetWebsite, GetRewriteConfig, UpdateRewriteConfig } from '@/api/modules/website';
import {
GetWebsite,
GetRewriteConfig,
UpdateRewriteConfig,
ListCustomRewrite,
OperateCustomRewrite,
} from '@/api/modules/website';
import { Rewrites } from '@/global/mimetype';
import { MsgSuccess } from '@/utils/message';
import CodemirrorPro from '@/components/codemirror-pro/index.vue';
import i18n from '@/lang';
import CustomRewrite from '@/views/website/website/config/basic/rewrite/custom/index.vue';
const loading = ref(false);
const content = ref(' ');
const codeRef = ref();
const customRef = ref();
const props = defineProps({
id: {
type: Number,
default: 0,
},
});
const id = computed(() => {
return props.id;
});
const req = reactive({
websiteID: id.value,
name: 'default',
});
const update = reactive({
websiteID: id.value,
content: 'd',
name: '',
});
const rewrites = ref([]);
const deleteRef = ref();
const getRewriteConfig = async (rewrite: string) => {
loading.value = true;
@ -68,7 +88,6 @@ const getRewriteConfig = async (rewrite: string) => {
if (res.data.content == '') {
content.value = ' ';
}
setCursorPosition();
} catch (error) {
} finally {
@ -97,7 +116,41 @@ const submit = async () => {
}
};
onMounted(() => {
const operateCustomRewrite = async () => {
customRef.value.acceptParams(content.value);
};
const deleteCustomRewrite = (name: string) => {
deleteRef.value.acceptParams({
title: i18n.global.t('commons.msg.deleteTitle'),
names: [name],
msg: i18n.global.t('commons.msg.operatorHelper', [
i18n.global.t('container.template'),
i18n.global.t('commons.button.delete'),
]),
api: OperateCustomRewrite,
params: { name: name, operate: 'delete' },
});
};
const init = () => {
ListCustomRewrite().then((res) => {
rewrites.value = [];
if (res && res.data) {
for (const d of res.data) {
rewrites.value.push({
resource: 'custom',
name: d,
});
}
}
for (const r of Rewrites) {
rewrites.value.push({
resource: 'default',
name: r,
});
}
});
GetWebsite(id.value).then((res) => {
const name = res.data.rewrite == '' ? 'default' : 'current';
if (name === 'current') {
@ -105,5 +158,9 @@ onMounted(() => {
}
getRewriteConfig(name);
});
};
onMounted(() => {
init();
});
</script>