mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-19 08:19:15 +08:00
feat: 已安装应用增加打开安装目录功能 (#1100)
This commit is contained in:
parent
7c56ed7b16
commit
ce19107c95
@ -5,6 +5,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/i18n"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@ -39,6 +40,15 @@ func (b *BaseApi) SearchApp(c *gin.Context) {
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"应用商店同步","formatEN":"App store synchronization"}
|
||||
func (b *BaseApi) SyncApp(c *gin.Context) {
|
||||
go appService.SyncAppListFromLocal()
|
||||
res, err := appService.GetAppUpdate()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if !res.CanUpdate {
|
||||
helper.SuccessWithMsg(c, i18n.GetMsgByKey("AppStoreIsUpToDate"))
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
global.LOG.Infof("sync app list start ...")
|
||||
if err := appService.SyncAppListFromRemote(); err != nil {
|
||||
|
@ -56,6 +56,7 @@ type AppInstalledDTO struct {
|
||||
AppName string `json:"appName"`
|
||||
Icon string `json:"icon"`
|
||||
CanUpdate bool `json:"canUpdate"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
type DatabaseConn struct {
|
||||
|
@ -385,7 +385,6 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
|
||||
res.CanUpdate = true
|
||||
return res, err
|
||||
}
|
||||
res.CanUpdate = true
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@ -665,7 +664,6 @@ func (a AppService) SyncAppListFromRemote() error {
|
||||
return err
|
||||
}
|
||||
if !updateRes.CanUpdate {
|
||||
//global.LOG.Infof("The latest version is [%s] The app store is already up to date", updateRes.Version)
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
@ -836,6 +834,11 @@ func (a AppService) SyncAppListFromRemote() error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(deleteDetails) > 0 {
|
||||
if err := appDetailRepo.BatchDelete(ctx, addDetails); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, u := range updateDetails {
|
||||
if err := appDetailRepo.Update(ctx, u); err != nil {
|
||||
return err
|
||||
|
@ -646,6 +646,7 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]respons
|
||||
}
|
||||
installDTO := response.AppInstalledDTO{
|
||||
AppInstall: installed,
|
||||
Path: installed.GetPath(),
|
||||
}
|
||||
app, err := appRepo.GetFirst(commonRepo.WithByID(installed.AppId))
|
||||
if err != nil {
|
||||
|
@ -46,6 +46,13 @@ func GetErrMsg(key string, maps map[string]interface{}) string {
|
||||
return content
|
||||
}
|
||||
|
||||
func GetMsgByKey(key string) string {
|
||||
content := ginI18n.MustGetMessage(&i18n.LocalizeConfig{
|
||||
MessageID: key,
|
||||
})
|
||||
return content
|
||||
}
|
||||
|
||||
//go:embed lang/*
|
||||
var fs embed.FS
|
||||
|
||||
|
@ -17,7 +17,6 @@ ErrRepoNotValid: "Remote repository verification failed!"
|
||||
ErrNameIsExist: "Name is already exist"
|
||||
ErrDemoEnvironment: "Demo server, prohibit this operation!"
|
||||
ErrCmdTimeout: "Command execution timed out!"
|
||||
ErrInitUser: "The user initialization has been completed by the system. Please do not repeat the operation!"
|
||||
|
||||
#app
|
||||
ErrPortInUsed: "{{ .detail }} port already in use"
|
||||
@ -31,6 +30,7 @@ ErrUpdateBuWebsite: 'The application was updated successfully, but the modificat
|
||||
Err1PanelNetworkFailed: 'Default container network creation failed! {{ .detail }}'
|
||||
ErrFileParse: 'Application docker-compose file parsing failed!'
|
||||
ErrInstallDirNotFound: 'installation directory does not exist'
|
||||
AppStoreIsUpToDate: 'The app store is already up to date!'
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: "File can not read"
|
||||
@ -46,8 +46,6 @@ ErrDomainIsExist: "Domain is already exist"
|
||||
ErrAliasIsExist: "Alias is already exist"
|
||||
ErrAppDelete: 'Other Website use this App'
|
||||
ErrGroupIsUsed: 'The group is in use and cannot be deleted'
|
||||
ErrUsernameIsExist: 'Username is already exist'
|
||||
ErrUsernameIsNotExist: 'Username is not exist'
|
||||
|
||||
#ssl
|
||||
ErrSSLCannotDelete: "The certificate is being used by the website and cannot be removed"
|
||||
@ -66,7 +64,6 @@ ErrTypeOfRedis: "The recovery file type does not match the current persistence m
|
||||
#container
|
||||
ErrInUsed: "{{ .detail }} is in use and cannot be deleted"
|
||||
ErrObjectInUsed: "This object is in use and cannot be deleted"
|
||||
ErrPortRules: "Invalid port specification"
|
||||
|
||||
#runtime
|
||||
ErrDirNotFound: "The build folder does not exist! Please check file integrity!"
|
||||
|
@ -17,7 +17,6 @@ ErrRepoNotValid: "远程仓库校验失败!"
|
||||
ErrNameIsExist: "名称已存在"
|
||||
ErrDemoEnvironment: "演示服务器,禁止此操作!"
|
||||
ErrCmdTimeout: "命令执行超时!"
|
||||
ErrInitUser: "系统已完成用户初始化,请勿重复操作!"
|
||||
|
||||
#app
|
||||
ErrPortInUsed: "{{ .detail }} 端口已被占用!"
|
||||
@ -31,6 +30,7 @@ ErrUpdateBuWebsite: '应用更新成功,但是网站配置文件修改失败
|
||||
Err1PanelNetworkFailed: '默认容器网络创建失败!{{ .detail }}'
|
||||
ErrFileParse: '应用 docker-compose 文件解析失败!'
|
||||
ErrInstallDirNotFound: '安装目录不存在'
|
||||
AppStoreIsUpToDate: '应用商店已经是最新版本'
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: "此文件不支持预览"
|
||||
@ -46,8 +46,6 @@ ErrDomainIsExist: "域名已存在"
|
||||
ErrAliasIsExist: "代号已存在"
|
||||
ErrAppDelete: '其他网站使用此应用,无法删除'
|
||||
ErrGroupIsUsed: '分组正在使用中,无法删除'
|
||||
ErrUsernameIsExist: '用户名已存在'
|
||||
ErrUsernameIsNotExist: '用户不存在'
|
||||
|
||||
#ssl
|
||||
ErrSSLCannotDelete: "证书正在被网站使用,无法删除"
|
||||
@ -66,7 +64,6 @@ ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后
|
||||
#container
|
||||
ErrInUsed: "{{ .detail }} 正被使用,无法删除"
|
||||
ErrObjectInUsed: "该对象正被使用,无法删除"
|
||||
ErrPortRules: "无效的端口规格"
|
||||
|
||||
#runtime
|
||||
ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!"
|
||||
|
@ -112,6 +112,7 @@ export namespace App {
|
||||
message: string;
|
||||
icon: string;
|
||||
canUpdate: boolean;
|
||||
path: string;
|
||||
app: App;
|
||||
}
|
||||
|
||||
|
15
frontend/src/global/business.ts
Normal file
15
frontend/src/global/business.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import router from '@/routers';
|
||||
|
||||
export function canEditPort(app: any): boolean {
|
||||
if (app.key == 'openresty') {
|
||||
return false;
|
||||
}
|
||||
if (app.type == 'php') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function toFolder(folder: string) {
|
||||
router.push({ path: '/hosts/files', query: { path: folder } });
|
||||
}
|
@ -1113,6 +1113,7 @@ const message = {
|
||||
appInstallWarn:
|
||||
'The application does not release the external access port by default, you can choose to release it in the advanced settings',
|
||||
upgradeStart: 'Start upgrading! Please refresh the page later',
|
||||
toFolder: 'Open the installation directory',
|
||||
},
|
||||
website: {
|
||||
website: 'Website',
|
||||
|
@ -1106,6 +1106,7 @@ const message = {
|
||||
allowPortHelper: '允许外部端口访问会放开防火墙端口,php运行环境请勿放开',
|
||||
appInstallWarn: '应用默认不放开外部访问端口,可以在高级设置中选择放开',
|
||||
upgradeStart: '开始升级!请稍后刷新页面',
|
||||
toFolder: '打开安装目录',
|
||||
},
|
||||
website: {
|
||||
website: '网站',
|
||||
|
@ -154,8 +154,12 @@ const getAppDetail = (key: string) => {
|
||||
const sync = () => {
|
||||
loading.value = true;
|
||||
SyncApp()
|
||||
.then(() => {
|
||||
MsgSuccess(i18n.global.t('app.syncStart'));
|
||||
.then((res) => {
|
||||
if (res.message != '') {
|
||||
MsgSuccess(res.message);
|
||||
} else {
|
||||
MsgSuccess(i18n.global.t('app.syncStart'));
|
||||
}
|
||||
canUpdate.value = false;
|
||||
search(req);
|
||||
})
|
||||
|
@ -111,12 +111,11 @@ const props = withDefaults(defineProps<OperateProps>(), {
|
||||
// id: 0,
|
||||
appKey: '',
|
||||
});
|
||||
let app = ref<any>({});
|
||||
let appDetail = ref<any>({});
|
||||
let version = ref('');
|
||||
let loadingDetail = ref(false);
|
||||
let loadingApp = ref(false);
|
||||
// let appKey = ref();
|
||||
const app = ref<any>({});
|
||||
const appDetail = ref<any>({});
|
||||
const version = ref('');
|
||||
const loadingDetail = ref(false);
|
||||
const loadingApp = ref(false);
|
||||
const installRef = ref();
|
||||
|
||||
const getApp = async () => {
|
||||
@ -150,6 +149,7 @@ const openInstall = () => {
|
||||
let params = {
|
||||
params: appDetail.value.params,
|
||||
appDetailId: appDetail.value.id,
|
||||
app: app.value,
|
||||
};
|
||||
if (app.value.type === 'php') {
|
||||
router.push({ path: '/websites/runtime/php' });
|
||||
|
@ -62,7 +62,7 @@
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('container.limitHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item prop="allowPort">
|
||||
<el-form-item prop="allowPort" v-if="canEditPort(installData.app)">
|
||||
<el-checkbox v-model="req.allowPort" :label="$t('app.allowPort')" size="large" />
|
||||
<span class="input-help">{{ $t('app.allowPortHelper') }}</span>
|
||||
</el-form-item>
|
||||
@ -86,6 +86,7 @@
|
||||
import { App } from '@/api/interface/app';
|
||||
import { InstallApp } from '@/api/modules/app';
|
||||
import { Rules, checkNumberRange } from '@/global/form-rules';
|
||||
import { canEditPort } from '@/global/business';
|
||||
import { FormInstance, FormRules } from 'element-plus';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
@ -97,10 +98,12 @@ const router = useRouter();
|
||||
interface InstallRrops {
|
||||
appDetailId: number;
|
||||
params?: App.AppParams;
|
||||
app: any;
|
||||
}
|
||||
|
||||
const installData = ref<InstallRrops>({
|
||||
appDetailId: 0,
|
||||
app: {},
|
||||
});
|
||||
const open = ref(false);
|
||||
const rules = ref<FormRules>({
|
||||
@ -112,9 +115,7 @@ const rules = ref<FormRules>({
|
||||
});
|
||||
const loading = ref(false);
|
||||
const paramForm = ref<FormInstance>();
|
||||
|
||||
const form = ref<{ [key: string]: any }>({});
|
||||
|
||||
const initData = () => ({
|
||||
appDetailId: 0,
|
||||
params: form.value,
|
||||
@ -126,7 +127,6 @@ const initData = () => ({
|
||||
containerName: '',
|
||||
allowPort: false,
|
||||
});
|
||||
|
||||
const req = reactive(initData());
|
||||
|
||||
const handleClose = () => {
|
||||
|
@ -2,7 +2,7 @@
|
||||
<el-drawer :close-on-click-modal="false" v-model="open" size="40%">
|
||||
<template #header>
|
||||
<Header :header="$t('app.param')" :back="handleClose">
|
||||
<template #buttons v-if="canEdit">
|
||||
<template #buttons>
|
||||
<el-button type="primary" plain @click="editParam" :disabled="loading">
|
||||
{{ edit ? $t('app.detail') : $t('commons.button.edit') }}
|
||||
</el-button>
|
||||
@ -75,7 +75,7 @@
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('container.limitHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item prop="allowPort">
|
||||
<el-form-item prop="allowPort" v-if="canEditPort(paramData.app)">
|
||||
<el-checkbox v-model="paramModel.allowPort" :label="$t('app.allowPort')" size="large" />
|
||||
<span class="input-help">{{ $t('app.allowPortHelper') }}</span>
|
||||
</el-form-item>
|
||||
@ -103,12 +103,15 @@ import { FormInstance } from 'element-plus';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import i18n from '@/lang';
|
||||
import { canEditPort } from '@/global/business';
|
||||
|
||||
interface ParamProps {
|
||||
id: Number;
|
||||
app: any;
|
||||
}
|
||||
const paramData = ref<ParamProps>({
|
||||
id: 0,
|
||||
app: {},
|
||||
});
|
||||
|
||||
interface EditForm extends App.InstallParams {
|
||||
@ -127,13 +130,12 @@ const rules = reactive({
|
||||
params: {},
|
||||
});
|
||||
const submitModel = ref<any>({});
|
||||
const canEdit = ref(false);
|
||||
|
||||
const acceptParams = async (props: ParamProps) => {
|
||||
canEdit.value = true;
|
||||
submitModel.value.installId = props.id;
|
||||
params.value = [];
|
||||
paramData.value.id = props.id;
|
||||
paramData.value.app = props.app;
|
||||
edit.value = false;
|
||||
await get();
|
||||
open.value = true;
|
||||
@ -156,9 +158,6 @@ const get = async () => {
|
||||
const configParams = res.data.params || [];
|
||||
if (configParams && configParams.length > 0) {
|
||||
configParams.forEach((d) => {
|
||||
if (d.edit) {
|
||||
canEdit.value = true;
|
||||
}
|
||||
let value = d.value;
|
||||
if (d.type === 'number') {
|
||||
value = Number(value);
|
||||
|
@ -97,11 +97,21 @@
|
||||
:content="installed.message"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button link type="primary">详情</el-button>
|
||||
<el-button link type="primary">
|
||||
{{ $t('app.detail') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
</span>
|
||||
|
||||
<el-tooltip effect="dark" :content="$t('app.toFolder')" placement="top">
|
||||
<el-button type="primary" link @click="toFolder(installed.path)">
|
||||
<el-icon>
|
||||
<FolderOpened />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-button
|
||||
class="h-button"
|
||||
type="primary"
|
||||
@ -132,7 +142,7 @@
|
||||
plain
|
||||
round
|
||||
size="small"
|
||||
:disabled="installed.status === 'Upgrading'"
|
||||
:disabled="installed.status === 'Running'"
|
||||
@click="openOperate(installed, 'upgrade')"
|
||||
v-if="mode === 'upgrade'"
|
||||
>
|
||||
@ -210,6 +220,7 @@ import Status from '@/components/status/index.vue';
|
||||
import { getAge } from '@/utils/util';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { toFolder } from '@/global/business';
|
||||
|
||||
const data = ref<any>();
|
||||
const loading = ref(false);
|
||||
@ -392,7 +403,7 @@ const buttons = [
|
||||
{
|
||||
label: i18n.global.t('app.params'),
|
||||
click: (row: any) => {
|
||||
openParam(row.id);
|
||||
openParam(row);
|
||||
},
|
||||
disabled: (row: any) => {
|
||||
return row.status === 'DownloadErr' || row.status === 'Upgrading';
|
||||
@ -418,8 +429,8 @@ const openUploads = (key: string, name: string) => {
|
||||
uploadRef.value.acceptParams(params);
|
||||
};
|
||||
|
||||
const openParam = (installId: number) => {
|
||||
appParamRef.value.acceptParams({ id: installId });
|
||||
const openParam = (row: any) => {
|
||||
appParamRef.value.acceptParams({ app: row.app, id: row.id });
|
||||
};
|
||||
|
||||
const isAppErr = (row: any) => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user