mirror of
https://gitee.com/fit2cloud-feizhiyun/1Panel.git
synced 2024-12-05 05:19:08 +08:00
feat: 增加基础应用组件
This commit is contained in:
parent
e56f54f30f
commit
ff2b4d030e
@ -39,7 +39,7 @@ func (b *BaseApi) SearchAppInstalled(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BaseApi) CheckAppInstalld(c *gin.Context) {
|
||||
func (b *BaseApi) CheckAppInstalled(c *gin.Context) {
|
||||
key, ok := c.Params.Get("key")
|
||||
if !ok {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error key in path"))
|
||||
|
@ -2,6 +2,7 @@ package dto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
)
|
||||
@ -40,9 +41,14 @@ type AppInstallRequest struct {
|
||||
}
|
||||
|
||||
type CheckInstalled struct {
|
||||
IsExist bool `json:"isExist"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
IsExist bool `json:"isExist"`
|
||||
Name string `json:"name"`
|
||||
App string `json:"app"`
|
||||
Version string `json:"version"`
|
||||
Status string `json:"status"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
LastBackupAt string `json:"lastBackupAt"`
|
||||
AppInstallID uint `json:"appInstallId"`
|
||||
}
|
||||
|
||||
type AppInstalled struct {
|
||||
|
@ -54,7 +54,10 @@ func (a AppInstallRepo) GetBy(opts ...DBOption) ([]model.AppInstall, error) {
|
||||
func (a AppInstallRepo) GetFirst(opts ...DBOption) (model.AppInstall, error) {
|
||||
var install model.AppInstall
|
||||
db := getDb(opts...).Model(&model.AppInstall{})
|
||||
err := db.Preload("App").Preload("Backups").First(&install).Error
|
||||
err := db.Preload("App").Preload("Backups", func(db *gorm.DB) *gorm.DB {
|
||||
db = db.Order("created_at desc")
|
||||
return db
|
||||
}).First(&install).Error
|
||||
return install, err
|
||||
}
|
||||
|
||||
|
@ -76,10 +76,9 @@ func getTx(ctx context.Context, opts ...DBOption) *gorm.DB {
|
||||
for _, opt := range opts {
|
||||
tx = opt(tx)
|
||||
}
|
||||
} else {
|
||||
return getDb(opts...)
|
||||
return tx
|
||||
}
|
||||
return tx
|
||||
return getDb(opts...)
|
||||
}
|
||||
|
||||
func getDb(opts ...DBOption) *gorm.DB {
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -38,15 +39,29 @@ func (a AppInstallService) Page(req dto.AppInstalledRequest) (int64, []dto.AppIn
|
||||
}
|
||||
|
||||
func (a AppInstallService) CheckExist(key string) (*dto.CheckInstalled, error) {
|
||||
res := &dto.CheckInstalled{
|
||||
IsExist: false,
|
||||
}
|
||||
app, err := appRepo.GetFirst(appRepo.WithKey(key))
|
||||
if err != nil {
|
||||
return &dto.CheckInstalled{IsExist: false}, nil
|
||||
return res, nil
|
||||
}
|
||||
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithAppId(app.ID))
|
||||
if appInstall.ID != 0 {
|
||||
return &dto.CheckInstalled{Name: appInstall.Name, IsExist: true, Version: appInstall.Version}, nil
|
||||
if reflect.DeepEqual(appInstall, model.AppInstall{}) {
|
||||
return res, nil
|
||||
}
|
||||
return &dto.CheckInstalled{IsExist: false}, nil
|
||||
res.Name = appInstall.Name
|
||||
res.App = app.Name
|
||||
res.Version = appInstall.Version
|
||||
res.CreatedAt = appInstall.CreatedAt
|
||||
res.Status = appInstall.Status
|
||||
res.AppInstallID = appInstall.ID
|
||||
res.IsExist = true
|
||||
if len(appInstall.Backups) > 0 {
|
||||
res.LastBackupAt = appInstall.Backups[0].CreatedAt.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (a AppInstallService) Search(req dto.AppInstalledRequest) ([]dto.AppInstalled, error) {
|
||||
|
@ -21,7 +21,7 @@ func (a *AppRouter) InitAppRouter(Router *gin.RouterGroup) {
|
||||
appRouter.GET("/detail/:appId/:version", baseApi.GetAppDetail)
|
||||
appRouter.POST("/install", baseApi.InstallApp)
|
||||
appRouter.GET("/installed/:appInstallId/versions", baseApi.GetUpdateVersions)
|
||||
appRouter.GET("/installed/check/:key", baseApi.CheckAppInstalld)
|
||||
appRouter.GET("/installed/check/:key", baseApi.CheckAppInstalled)
|
||||
appRouter.POST("/installed", baseApi.SearchAppInstalled)
|
||||
appRouter.POST("/installed/op", baseApi.OperateInstalled)
|
||||
appRouter.POST("/installed/sync", baseApi.SyncInstalled)
|
||||
|
@ -86,6 +86,11 @@ export namespace App {
|
||||
name: string;
|
||||
version: string;
|
||||
isExist: boolean;
|
||||
app: string;
|
||||
status: string;
|
||||
createdAt: string;
|
||||
lastBackupAt: string;
|
||||
appInstallId: number;
|
||||
}
|
||||
|
||||
export interface AppInstalledOp {
|
||||
|
141
frontend/src/components/app-status/index.vue
Normal file
141
frontend/src/components/app-status/index.vue
Normal file
@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card class="app-card" v-loading="loading">
|
||||
<div class="app-content" v-if="data.isExist">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="1">
|
||||
<div>
|
||||
<el-tag effect="dark" type="success">{{ data.app }}</el-tag>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="2">
|
||||
<div>
|
||||
{{ $t('app.version') }}:
|
||||
<el-tag type="info">{{ data.version }}</el-tag>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="2">
|
||||
<div>
|
||||
{{ $t('commons.table.status') }}:
|
||||
<el-tag type="success">{{ data.status }}</el-tag>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<div>
|
||||
{{ $t('website.lastBackupAt') }}:
|
||||
<el-tag v-if="data.lastBackupAt != ''" type="info">{{ data.lastBackupAt }}</el-tag>
|
||||
<span else>{{ $t('website.null') }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-button type="primary" link @click="onOperate('restart')">{{ $t('app.restart') }}</el-button>
|
||||
<el-button type="primary" link @click="setting">{{ $t('commons.button.set') }}</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-row>
|
||||
<el-col :span="2">
|
||||
{{ $t('app.checkInstalledWarn', [data.app]) }}
|
||||
<el-tag effect="dark" type="success">{{ data.app }}</el-tag>
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
<el-link icon="Position" @click="goRouter('/apps')" type="primary">
|
||||
{{ $t('database.goInstall') }}
|
||||
</el-link>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<span></span>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { CheckAppInstalled, InstalledOp } from '@/api/modules/app';
|
||||
import i18n from '@/lang';
|
||||
import router from '@/routers';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
appKey: {
|
||||
type: String,
|
||||
default: 'nginx',
|
||||
},
|
||||
});
|
||||
|
||||
let key = ref('');
|
||||
let data = ref({
|
||||
app: '',
|
||||
version: '',
|
||||
status: '',
|
||||
lastBackupAt: '',
|
||||
appInstallId: 0,
|
||||
isExist: false,
|
||||
});
|
||||
let loading = ref(false);
|
||||
let operateReq = reactive({
|
||||
installId: 0,
|
||||
operate: '',
|
||||
});
|
||||
|
||||
const em = defineEmits(['setting']);
|
||||
const setting = () => {
|
||||
em('setting', false);
|
||||
};
|
||||
|
||||
const goRouter = async (path: string) => {
|
||||
router.push({ path: path });
|
||||
};
|
||||
|
||||
const onCheck = async () => {
|
||||
loading.value = true;
|
||||
const res = await CheckAppInstalled(key.value);
|
||||
data.value = res.data;
|
||||
operateReq.installId = res.data.appInstallId;
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
const onOperate = async (operation: string) => {
|
||||
operateReq.operate = operation;
|
||||
ElMessageBox.confirm(
|
||||
i18n.global.t('app.operatorHelper', [i18n.global.t('app.' + operation)]),
|
||||
i18n.global.t('app.' + operation),
|
||||
{
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
type: 'info',
|
||||
},
|
||||
).then(() => {
|
||||
loading.value = true;
|
||||
InstalledOp(operateReq)
|
||||
.then(() => {
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
onCheck();
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
key.value = props.appKey;
|
||||
onCheck();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.app-card {
|
||||
font-size: 14px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.app-content {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
@ -687,8 +687,11 @@ export default {
|
||||
update: '升级',
|
||||
versioneSelect: '请选择版本',
|
||||
operatorHelper: '将对选中应用进行 {0} 操作,是否继续?',
|
||||
checkInstalledWarn: '未检测到',
|
||||
gotoInstalled: '去安装',
|
||||
},
|
||||
website: {
|
||||
website: '网站',
|
||||
primaryDomain: '主域名',
|
||||
otherDomains: '其他域名',
|
||||
type: '类型',
|
||||
@ -747,5 +750,7 @@ export default {
|
||||
config: '配置',
|
||||
enableHTTPS: '启用HTTPS',
|
||||
aliasHelper: '代号是网站目录的文件夹名称',
|
||||
lastBackupAt: '上次备份时间',
|
||||
null: '无',
|
||||
},
|
||||
};
|
||||
|
@ -18,15 +18,16 @@
|
||||
|
||||
/* 防止切换出现横向滚动条 */
|
||||
overflow-x: hidden;
|
||||
background: #f0f2f5;
|
||||
// background-color: #f0f2f5;
|
||||
// background: #f0f2f5;
|
||||
.main-box {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 15px;
|
||||
padding: 5px;
|
||||
overflow: auto;
|
||||
overflow-x: hidden !important;
|
||||
background-color: #ffffff;
|
||||
// background-color: #f0f2f5;
|
||||
border-radius: 4px;
|
||||
// box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
|
||||
&::-webkit-scrollbar {
|
||||
|
@ -57,13 +57,6 @@
|
||||
/>
|
||||
</ComplexTable>
|
||||
<el-dialog v-model="open" :title="$t('commons.msg.operate')" :before-close="handleClose" width="30%">
|
||||
<!-- <el-alert
|
||||
v-if="operateReq.operate != 'update'"
|
||||
:title="getMsg(operateReq.operate)"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/> -->
|
||||
<div style="text-align: center">
|
||||
<p>{{ $t('app.versioneSelect') }}</p>
|
||||
<el-select v-model="operateReq.detailId">
|
||||
@ -198,29 +191,6 @@ const onOperate = async (operation: string) => {
|
||||
});
|
||||
};
|
||||
|
||||
// const getMsg = (op: string) => {
|
||||
// let tip = '';
|
||||
// switch (op) {
|
||||
// case 'up':
|
||||
// tip = i18n.global.t('app.up');
|
||||
// break;
|
||||
// case 'down':
|
||||
// tip = i18n.global.t('app.down');
|
||||
// break;
|
||||
// case 'restart':
|
||||
// tip = i18n.global.t('app.restart');
|
||||
// break;
|
||||
// case 'delete':
|
||||
// tip = i18n.global.t('app.deleteWarn');
|
||||
// break;
|
||||
// case 'sync':
|
||||
// tip = i18n.global.t('app.sync');
|
||||
// break;
|
||||
// default:
|
||||
// }
|
||||
// return tip;
|
||||
// };
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('app.sync'),
|
||||
|
@ -1,33 +1,42 @@
|
||||
<template>
|
||||
<LayoutContent :header="'网站'">
|
||||
<ComplexTable :pagination-config="paginationConfig" :data="data" @search="search()">
|
||||
<template #toolbar>
|
||||
<el-button type="primary" plain @click="openCreate">{{ $t('commons.button.create') }}</el-button>
|
||||
<el-button type="primary" plain @click="openGroup">{{ $t('website.group') }}</el-button>
|
||||
<!-- <el-button type="primary" plain>{{ '修改默认页' }}</el-button>
|
||||
<div ref="websiteRef">
|
||||
<LayoutContent>
|
||||
<AppStatus :app-key="'nginx'" :parentRef="websiteRef"></AppStatus>
|
||||
<br />
|
||||
<el-card>
|
||||
<ComplexTable :pagination-config="paginationConfig" :data="data" @search="search()">
|
||||
<template #toolbar>
|
||||
<el-button type="primary" plain @click="openCreate">
|
||||
{{ $t('commons.button.create') }}
|
||||
</el-button>
|
||||
<el-button type="primary" plain @click="openGroup">{{ $t('website.group') }}</el-button>
|
||||
<!-- <el-button type="primary" plain>{{ '修改默认页' }}</el-button>
|
||||
<el-button type="primary" plain>{{ '默认站点' }}</el-button> -->
|
||||
</template>
|
||||
<el-table-column :label="$t('commons.table.name')" fix show-overflow-tooltip prop="primaryDomain">
|
||||
<template #default="{ row }">
|
||||
<el-link @click="openConfig(row.id)">{{ row.primaryDomain }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.table.status')" prop="status"></el-table-column>
|
||||
<!-- <el-table-column :label="'备份'" prop="backup"></el-table-column> -->
|
||||
<el-table-column :label="'备注'" prop="remark"></el-table-column>
|
||||
<!-- <el-table-column :label="'SSL证书'" prop="ssl"></el-table-column> -->
|
||||
<fu-table-operations
|
||||
:ellipsis="1"
|
||||
:buttons="buttons"
|
||||
:label="$t('commons.table.operate')"
|
||||
fixed="right"
|
||||
fix
|
||||
/>
|
||||
</ComplexTable>
|
||||
<CreateWebSite ref="createRef" @close="search"></CreateWebSite>
|
||||
<DeleteWebsite ref="deleteRef" @close="search"></DeleteWebsite>
|
||||
<WebSiteGroup ref="groupRef"></WebSiteGroup>
|
||||
</LayoutContent>
|
||||
</template>
|
||||
<el-table-column :label="$t('commons.table.name')" fix show-overflow-tooltip prop="primaryDomain">
|
||||
<template #default="{ row }">
|
||||
<el-link @click="openConfig(row.id)">{{ row.primaryDomain }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.table.status')" prop="status"></el-table-column>
|
||||
<!-- <el-table-column :label="'备份'" prop="backup"></el-table-column> -->
|
||||
<el-table-column :label="'备注'" prop="remark"></el-table-column>
|
||||
<!-- <el-table-column :label="'SSL证书'" prop="ssl"></el-table-column> -->
|
||||
<fu-table-operations
|
||||
:ellipsis="1"
|
||||
:buttons="buttons"
|
||||
:label="$t('commons.table.operate')"
|
||||
fixed="right"
|
||||
fix
|
||||
/>
|
||||
</ComplexTable>
|
||||
</el-card>
|
||||
|
||||
<CreateWebSite ref="createRef" @close="search"></CreateWebSite>
|
||||
<DeleteWebsite ref="deleteRef" @close="search"></DeleteWebsite>
|
||||
<WebSiteGroup ref="groupRef"></WebSiteGroup>
|
||||
</LayoutContent>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@ -39,6 +48,7 @@ import DeleteWebsite from './delete/index.vue';
|
||||
import WebSiteGroup from './group/index.vue';
|
||||
import { SearchWebSites } from '@/api/modules/website';
|
||||
import { WebSite } from '@/api/interface/website';
|
||||
import AppStatus from '@/components/app-status/index.vue';
|
||||
|
||||
import i18n from '@/lang';
|
||||
import router from '@/routers';
|
||||
@ -46,6 +56,7 @@ import router from '@/routers';
|
||||
const createRef = ref();
|
||||
const deleteRef = ref();
|
||||
const groupRef = ref();
|
||||
const websiteRef = ref();
|
||||
|
||||
const paginationConfig = reactive({
|
||||
currentPage: 1,
|
||||
|
Loading…
Reference in New Issue
Block a user