mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-19 06:32: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)
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
return
|
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)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -92,3 +92,17 @@ func (b *BaseApi) PageInstalled(c *gin.Context) {
|
|||||||
Total: total,
|
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 {
|
type AppInstallRequest struct {
|
||||||
AppDetailId uint `json:"appDetailId" validate:"required"`
|
AppDetailId uint `json:"appDetailId" validate:"required"`
|
||||||
Params map[string]interface{} `json:"params"`
|
Params map[string]interface{} `json:"params"`
|
||||||
|
Name string `json:"name" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppInstalled struct {
|
type AppInstalled struct {
|
||||||
@ -79,3 +80,16 @@ type AppInstalled struct {
|
|||||||
type AppInstalledRequest struct {
|
type AppInstalledRequest struct {
|
||||||
PageInfo
|
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 {
|
type AppInstall struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
|
Name string `json:"name" gorm:"type:varchar(64);not null"`
|
||||||
ContainerName string `json:"containerName" gorm:"type:varchar(256);not null"`
|
ContainerName string `json:"containerName" gorm:"type:varchar(256);not null"`
|
||||||
Version string `json:"version" 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"`
|
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)
|
db = opt(db)
|
||||||
}
|
}
|
||||||
var install []model.AppInstall
|
var install []model.AppInstall
|
||||||
err := db.Find(&install).Error
|
err := db.Preload("App").Find(&install).Error
|
||||||
return install, err
|
return install, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package service
|
|||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"github.com/1Panel-dev/1Panel/app/dto"
|
"github.com/1Panel-dev/1Panel/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/app/model"
|
"github.com/1Panel-dev/1Panel/app/model"
|
||||||
"github.com/1Panel-dev/1Panel/app/repo"
|
"github.com/1Panel-dev/1Panel/app/repo"
|
||||||
@ -147,7 +148,51 @@ func (a AppService) GetAppDetail(appId uint, version string) (dto.AppDetailDTO,
|
|||||||
return appDetailDTO, nil
|
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))
|
appDetail, err := appDetailRepo.GetAppDetail(commonRepo.WithByID(appDetailId))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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)
|
containerName := constant.ContainerPrefix + app.Key + "-" + common.RandStr(6)
|
||||||
appInstall := model.AppInstall{
|
appInstall := model.AppInstall{
|
||||||
|
Name: name,
|
||||||
AppId: appDetail.AppId,
|
AppId: appDetail.AppId,
|
||||||
AppDetailId: appDetail.ID,
|
AppDetailId: appDetail.ID,
|
||||||
Version: appDetail.Version,
|
Version: appDetail.Version,
|
||||||
|
@ -21,5 +21,6 @@ func (a *AppRouter) InitAppRouter(Router *gin.RouterGroup) {
|
|||||||
appRouter.GET("/detail/:appid/:version", baseApi.GetAppDetail)
|
appRouter.GET("/detail/:appid/:version", baseApi.GetAppDetail)
|
||||||
appRouter.POST("/install", baseApi.InstallApp)
|
appRouter.POST("/install", baseApi.InstallApp)
|
||||||
appRouter.POST("/installed", baseApi.PageInstalled)
|
appRouter.POST("/installed", baseApi.PageInstalled)
|
||||||
|
appRouter.POST("/installed/op", baseApi.InstallOperate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,7 @@ export namespace App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface AppInstalled extends CommonModel {
|
export interface AppInstalled extends CommonModel {
|
||||||
|
name: string;
|
||||||
containerName: string;
|
containerName: string;
|
||||||
version: string;
|
version: string;
|
||||||
appId: string;
|
appId: string;
|
||||||
@ -75,4 +76,9 @@ export namespace App {
|
|||||||
ready: number;
|
ready: number;
|
||||||
icon: string;
|
icon: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AppInstalledOp {
|
||||||
|
installId: number;
|
||||||
|
operate: string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,3 +25,7 @@ export const InstallApp = (install: App.AppInstall) => {
|
|||||||
export const GetAppInstalled = (info: ReqPage) => {
|
export const GetAppInstalled = (info: ReqPage) => {
|
||||||
return http.post<ResPage<App.AppInstalled>>('apps/installed', info);
|
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: '新建成功',
|
createSuccess: '新建成功',
|
||||||
updateSuccess: '更新成功',
|
updateSuccess: '更新成功',
|
||||||
uploadSuccess: '上传成功',
|
uploadSuccess: '上传成功',
|
||||||
|
operate: '操作',
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
captchaHelper: '请输入验证码',
|
captchaHelper: '请输入验证码',
|
||||||
@ -403,5 +404,7 @@ export default {
|
|||||||
restart: '重启',
|
restart: '重启',
|
||||||
up: '启动',
|
up: '启动',
|
||||||
down: '停止',
|
down: '停止',
|
||||||
|
name: '名称',
|
||||||
|
description: '描述',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog v-model="open" :title="$t('app.install')" width="30%">
|
<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 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">
|
<div v-for="(f, index) in installData.params?.formFields" :key="index">
|
||||||
<el-form-item :label="f.labelZh" :prop="f.envKey">
|
<el-form-item :label="f.labelZh" :prop="f.envKey">
|
||||||
<el-input
|
<el-input
|
||||||
@ -44,12 +47,15 @@ const installData = ref<InstallRrops>({
|
|||||||
});
|
});
|
||||||
let open = ref(false);
|
let open = ref(false);
|
||||||
let form = reactive<{ [key: string]: any }>({});
|
let form = reactive<{ [key: string]: any }>({});
|
||||||
let rules = reactive<FormRules>({});
|
let rules = reactive<FormRules>({
|
||||||
|
name: [Rules.requiredInput],
|
||||||
|
});
|
||||||
let loading = false;
|
let loading = false;
|
||||||
const paramForm = ref<FormInstance>();
|
const paramForm = ref<FormInstance>();
|
||||||
const req = reactive({
|
const req = reactive({
|
||||||
appDetailId: 0,
|
appDetailId: 0,
|
||||||
params: {},
|
params: {},
|
||||||
|
name: '',
|
||||||
});
|
});
|
||||||
const em = defineEmits(['close']);
|
const em = defineEmits(['close']);
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<template>
|
<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.appName')" prop="appName"></el-table-column>
|
||||||
<el-table-column :label="$t('app.version')" prop="version"></el-table-column>
|
<el-table-column :label="$t('app.version')" prop="version"></el-table-column>
|
||||||
<el-table-column :label="$t('app.container')">
|
<el-table-column :label="$t('app.container')">
|
||||||
@ -9,7 +11,18 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$t('app.status')">
|
<el-table-column :label="$t('app.status')">
|
||||||
<template #default="{ row }">
|
<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>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
@ -23,14 +36,15 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { GetAppInstalled } from '@/api/modules/app';
|
import { GetAppInstalled, InstalledOp } from '@/api/modules/app';
|
||||||
import { onMounted, reactive, ref } from 'vue';
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
import ComplexTable from '@/components/complex-table/index.vue';
|
import ComplexTable from '@/components/complex-table/index.vue';
|
||||||
import { dateFromat } from '@/utils/util';
|
import { dateFromat } from '@/utils/util';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
|
import { ElMessageBox } from 'element-plus';
|
||||||
|
|
||||||
let data = ref<any>();
|
let data = ref<any>();
|
||||||
|
let loading = ref(false);
|
||||||
const paginationConfig = reactive({
|
const paginationConfig = reactive({
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
pageSize: 20,
|
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 = [
|
const buttons = [
|
||||||
{
|
{
|
||||||
label: i18n.global.t('app.restart'),
|
label: i18n.global.t('app.restart'),
|
||||||
|
click: (row: any) => {
|
||||||
|
operate(row, 'restart');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: i18n.global.t('app.up'),
|
label: i18n.global.t('app.up'),
|
||||||
|
click: (row: any) => {
|
||||||
|
operate(row, 'up');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: i18n.global.t('app.down'),
|
label: i18n.global.t('app.down'),
|
||||||
|
click: (row: any) => {
|
||||||
|
operate(row, 'down');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user