mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-18 22:22:59 +08:00
feat: 增加已安装应用操作
This commit is contained in:
parent
f7263934f9
commit
4830839c91
@ -68,7 +68,7 @@ func (b *BaseApi) InstallApp(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := appService.Install(req.AppDetailId, req.Params); err != nil {
|
||||
if err := appService.Install(req.Name, req.AppDetailId, req.Params); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
@ -92,3 +92,17 @@ func (b *BaseApi) PageInstalled(c *gin.Context) {
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
func (b *BaseApi) InstallOperate(c *gin.Context) {
|
||||
var req dto.AppInstallOperate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := appService.Operate(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ type AppRequest struct {
|
||||
type AppInstallRequest struct {
|
||||
AppDetailId uint `json:"appDetailId" validate:"required"`
|
||||
Params map[string]interface{} `json:"params"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
}
|
||||
|
||||
type AppInstalled struct {
|
||||
@ -79,3 +80,16 @@ type AppInstalled struct {
|
||||
type AppInstalledRequest struct {
|
||||
PageInfo
|
||||
}
|
||||
|
||||
type AppOperate string
|
||||
|
||||
var (
|
||||
Up AppOperate = "up"
|
||||
Down AppOperate = "down"
|
||||
Restart AppOperate = "restart"
|
||||
)
|
||||
|
||||
type AppInstallOperate struct {
|
||||
InstallId uint `json:"installId" validate:"required"`
|
||||
Operate AppOperate `json:"operate" validate:"required"`
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package model
|
||||
|
||||
type AppInstall struct {
|
||||
BaseModel
|
||||
Name string `json:"name" gorm:"type:varchar(64);not null"`
|
||||
ContainerName string `json:"containerName" gorm:"type:varchar(256);not null"`
|
||||
Version string `json:"version" gorm:"type:varchar(256);not null"`
|
||||
AppId uint `json:"appId" gorm:"type:integer;not null"`
|
||||
|
@ -13,7 +13,7 @@ func (a AppInstallRepo) GetBy(opts ...DBOption) ([]model.AppInstall, error) {
|
||||
db = opt(db)
|
||||
}
|
||||
var install []model.AppInstall
|
||||
err := db.Find(&install).Error
|
||||
err := db.Preload("App").Find(&install).Error
|
||||
return install, err
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ package service
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/1Panel-dev/1Panel/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/app/model"
|
||||
"github.com/1Panel-dev/1Panel/app/repo"
|
||||
@ -147,7 +148,51 @@ func (a AppService) GetAppDetail(appId uint, version string) (dto.AppDetailDTO,
|
||||
return appDetailDTO, nil
|
||||
}
|
||||
|
||||
func (a AppService) Install(appDetailId uint, params map[string]interface{}) error {
|
||||
func (a AppService) Operate(req dto.AppInstallOperate) error {
|
||||
appInstall, err := appInstallRepo.GetBy(commonRepo.WithByID(req.InstallId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(appInstall) == 0 {
|
||||
return errors.New("not found")
|
||||
}
|
||||
|
||||
install := appInstall[0]
|
||||
dockerComposePath := path.Join(global.CONF.System.AppDir, install.App.Key, install.ContainerName, "docker-compose.yml")
|
||||
switch req.Operate {
|
||||
case dto.Up:
|
||||
out, err := compose.Up(dockerComposePath)
|
||||
if err != nil {
|
||||
return handleErr(install, err, out)
|
||||
}
|
||||
case dto.Down:
|
||||
out, err := compose.Down(dockerComposePath)
|
||||
if err != nil {
|
||||
return handleErr(install, err, out)
|
||||
}
|
||||
case dto.Restart:
|
||||
out, err := compose.Restart(dockerComposePath)
|
||||
if err != nil {
|
||||
return handleErr(install, err, out)
|
||||
}
|
||||
default:
|
||||
return errors.New("operate not support")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleErr(install model.AppInstall, err error, out string) error {
|
||||
reErr := err
|
||||
install.Message = err.Error()
|
||||
if out != "" {
|
||||
install.Message = out
|
||||
reErr = errors.New(out)
|
||||
}
|
||||
_ = appInstallRepo.Save(install)
|
||||
return reErr
|
||||
}
|
||||
|
||||
func (a AppService) Install(name string, appDetailId uint, params map[string]interface{}) error {
|
||||
appDetail, err := appDetailRepo.GetAppDetail(commonRepo.WithByID(appDetailId))
|
||||
if err != nil {
|
||||
return err
|
||||
@ -159,6 +204,7 @@ func (a AppService) Install(appDetailId uint, params map[string]interface{}) err
|
||||
}
|
||||
containerName := constant.ContainerPrefix + app.Key + "-" + common.RandStr(6)
|
||||
appInstall := model.AppInstall{
|
||||
Name: name,
|
||||
AppId: appDetail.AppId,
|
||||
AppDetailId: appDetail.ID,
|
||||
Version: appDetail.Version,
|
||||
|
@ -21,5 +21,6 @@ func (a *AppRouter) InitAppRouter(Router *gin.RouterGroup) {
|
||||
appRouter.GET("/detail/:appid/:version", baseApi.GetAppDetail)
|
||||
appRouter.POST("/install", baseApi.InstallApp)
|
||||
appRouter.POST("/installed", baseApi.PageInstalled)
|
||||
appRouter.POST("/installed/op", baseApi.InstallOperate)
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,7 @@ export namespace App {
|
||||
}
|
||||
|
||||
export interface AppInstalled extends CommonModel {
|
||||
name: string;
|
||||
containerName: string;
|
||||
version: string;
|
||||
appId: string;
|
||||
@ -75,4 +76,9 @@ export namespace App {
|
||||
ready: number;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
export interface AppInstalledOp {
|
||||
installId: number;
|
||||
operate: string;
|
||||
}
|
||||
}
|
||||
|
@ -25,3 +25,7 @@ export const InstallApp = (install: App.AppInstall) => {
|
||||
export const GetAppInstalled = (info: ReqPage) => {
|
||||
return http.post<ResPage<App.AppInstalled>>('apps/installed', info);
|
||||
};
|
||||
|
||||
export const InstalledOp = (op: App.AppInstalledOp) => {
|
||||
return http.post<any>('apps/installed/op', op);
|
||||
};
|
||||
|
@ -61,6 +61,7 @@ export default {
|
||||
createSuccess: '新建成功',
|
||||
updateSuccess: '更新成功',
|
||||
uploadSuccess: '上传成功',
|
||||
operate: '操作',
|
||||
},
|
||||
login: {
|
||||
captchaHelper: '请输入验证码',
|
||||
@ -403,5 +404,7 @@ export default {
|
||||
restart: '重启',
|
||||
up: '启动',
|
||||
down: '停止',
|
||||
name: '名称',
|
||||
description: '描述',
|
||||
},
|
||||
};
|
||||
|
@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<el-dialog v-model="open" :title="$t('app.install')" width="30%">
|
||||
<el-form ref="paramForm" label-position="left" :model="form" label-width="150px" :rules="rules">
|
||||
<el-form-item :label="$t('app.name')" prop="name">
|
||||
<el-input v-model="req.name"></el-input>
|
||||
</el-form-item>
|
||||
<div v-for="(f, index) in installData.params?.formFields" :key="index">
|
||||
<el-form-item :label="f.labelZh" :prop="f.envKey">
|
||||
<el-input
|
||||
@ -44,12 +47,15 @@ const installData = ref<InstallRrops>({
|
||||
});
|
||||
let open = ref(false);
|
||||
let form = reactive<{ [key: string]: any }>({});
|
||||
let rules = reactive<FormRules>({});
|
||||
let rules = reactive<FormRules>({
|
||||
name: [Rules.requiredInput],
|
||||
});
|
||||
let loading = false;
|
||||
const paramForm = ref<FormInstance>();
|
||||
const req = reactive({
|
||||
appDetailId: 0,
|
||||
params: {},
|
||||
name: '',
|
||||
});
|
||||
const em = defineEmits(['close']);
|
||||
const handleClose = () => {
|
||||
|
@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<ComplexTable :pagination-config="paginationConfig" :data="data" @search="search">
|
||||
<ComplexTable :pagination-config="paginationConfig" :data="data" @search="search" v-loading="loading">
|
||||
<el-table-column :label="$t('app.name')" prop="name"></el-table-column>
|
||||
<el-table-column :label="$t('app.description')" prop="description"></el-table-column>
|
||||
<el-table-column :label="$t('app.appName')" prop="appName"></el-table-column>
|
||||
<el-table-column :label="$t('app.version')" prop="version"></el-table-column>
|
||||
<el-table-column :label="$t('app.container')">
|
||||
@ -9,7 +11,18 @@
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('app.status')">
|
||||
<template #default="{ row }">
|
||||
<el-tag>{{ row.status }}</el-tag>
|
||||
<el-popover
|
||||
v-if="row.status === 'Error'"
|
||||
placement="top-start"
|
||||
:width="400"
|
||||
trigger="hover"
|
||||
:content="row.message"
|
||||
>
|
||||
<template #reference>
|
||||
<el-tag type="error">{{ row.status }}</el-tag>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-tag v-else>{{ row.status }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@ -23,14 +36,15 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { GetAppInstalled } from '@/api/modules/app';
|
||||
import { GetAppInstalled, InstalledOp } from '@/api/modules/app';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import ComplexTable from '@/components/complex-table/index.vue';
|
||||
import { dateFromat } from '@/utils/util';
|
||||
import i18n from '@/lang';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
|
||||
let data = ref<any>();
|
||||
|
||||
let loading = ref(false);
|
||||
const paginationConfig = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 20,
|
||||
@ -49,15 +63,45 @@ const search = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const operate = async (row: any, op: string) => {
|
||||
const req = {
|
||||
installId: row.id,
|
||||
operate: op,
|
||||
};
|
||||
|
||||
ElMessageBox.confirm(i18n.global.t(`${'app.' + op}`) + '?', i18n.global.t('commons.msg.operate'), {
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
type: 'warning',
|
||||
draggable: true,
|
||||
}).then(async () => {
|
||||
loading.value = true;
|
||||
InstalledOp(req)
|
||||
.then(() => {})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('app.restart'),
|
||||
click: (row: any) => {
|
||||
operate(row, 'restart');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('app.up'),
|
||||
click: (row: any) => {
|
||||
operate(row, 'up');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('app.down'),
|
||||
click: (row: any) => {
|
||||
operate(row, 'down');
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user