feat: add config for appstore (#8524)

This commit is contained in:
ChengPlay 2025-04-30 18:39:53 +08:00 committed by GitHub
parent 4ecdbcb01f
commit f94a04b067
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 240 additions and 178 deletions

View file

@ -142,3 +142,20 @@ func loadLocalConn() (*ssh.SSHClient, error) {
}
return ssh.NewClient(connInDB)
}
// @Tags System Setting
// @Summary Load system setting by key
// @Param key path string true "key"
// @Success 200 {object} dto.SettingInfo
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /settings/get/{key} [get]
func (b *BaseApi) GetSettingByKey(c *gin.Context) {
key := c.Param("key")
if len(key) == 0 {
helper.BadRequest(c, errors.New("key is empty"))
return
}
value := settingService.GetSettingByKey(key)
helper.SuccessWithData(c, value)
}

View file

@ -129,5 +129,6 @@ type AppUpdateVersion struct {
}
type AppstoreUpdate struct {
DefaultDomain string `json:"defaultDomain"`
Scope string `json:"scope" validate:"required,oneof=UninstallDeleteImage UpgradeBackup UninstallDeleteBackup"`
Status string `json:"status"`
}

View file

@ -165,5 +165,7 @@ type AppConfig struct {
}
type AppstoreConfig struct {
DefaultDomain string `json:"defaultDomain"`
UninstallDeleteImage string `json:"uninstallDeleteImage"`
UpgradeBackup string `json:"upgradeBackup"`
UninstallDeleteBackup string `json:"uninstallDeleteBackup"`
}

View file

@ -103,7 +103,7 @@ func (s *SettingRepo) UpdateOrCreate(key, value string) error {
result := global.DB.Where("key = ?", key).First(&setting)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return global.DB.Create(&model.Setting{Key: key, Value: value}).Error
return global.DB.Debug().Create(&model.Setting{Key: key, Value: value}).Error
}
return result.Error
}

View file

@ -1160,12 +1160,22 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
func (a AppService) UpdateAppstoreConfig(req request.AppstoreUpdate) error {
settingService := NewISettingService()
return settingService.Update("AppDefaultDomain", req.DefaultDomain)
return settingService.Update(req.Scope, req.Status)
}
func (a AppService) GetAppstoreConfig() (*response.AppstoreConfig, error) {
defaultDomain, _ := settingRepo.Get(settingRepo.WithByKey("AppDefaultDomain"))
res := &response.AppstoreConfig{}
res.DefaultDomain = defaultDomain.Value
res.UninstallDeleteImage, _ = settingRepo.GetValueByKey("UninstallDeleteImage")
if res.UninstallDeleteImage == "" {
res.UninstallDeleteImage = "False"
}
res.UpgradeBackup, _ = settingRepo.GetValueByKey("UpgradeBackup")
if res.UpgradeBackup == "" {
res.UpgradeBackup = "False"
}
res.UninstallDeleteBackup, _ = settingRepo.GetValueByKey("UninstallDeleteBackup")
if res.UninstallDeleteBackup == "" {
res.UninstallDeleteBackup = "False"
}
return res, nil
}

View file

@ -22,6 +22,7 @@ type ISettingService interface {
TestConnByInfo(req dto.SSHConnData) bool
SaveConnInfo(req dto.SSHConnData) error
GetSystemProxy() (*dto.SystemProxy, error)
GetSettingByKey(key string) string
}
func NewISettingService() ISettingService {
@ -136,3 +137,13 @@ func (u *SettingService) GetSystemProxy() (*dto.SystemProxy, error) {
systemProxy.Password, _ = encrypt.StringDecrypt(passwd)
return &systemProxy, nil
}
func (u *SettingService) GetSettingByKey(key string) string {
switch key {
case "SystemIP":
value, _ := settingRepo.GetValueByKey(key)
return value
default:
return ""
}
}

View file

@ -113,7 +113,7 @@ func UseI18n() gin.HandlerFunc {
return func(context *gin.Context) {
lang := context.GetHeader("Accept-Language")
if lang == "" {
lang = "zh"
lang = "en"
}
global.I18n = i18n.NewLocalizer(bundle, lang)
}

View file

@ -14,6 +14,7 @@ func (s *SettingRouter) InitRouter(Router *gin.RouterGroup) {
settingRouter.POST("/search", baseApi.GetSettingInfo)
settingRouter.GET("/search/available", baseApi.GetSystemAvailable)
settingRouter.POST("/update", baseApi.UpdateSetting)
settingRouter.GET("/get/:key", baseApi.GetSettingByKey)
settingRouter.GET("/snapshot/load", baseApi.LoadSnapshotData)
settingRouter.POST("/snapshot", baseApi.CreateSnapshot)

View file

@ -113,7 +113,7 @@ func UseI18n() gin.HandlerFunc {
return func(context *gin.Context) {
lang := context.GetHeader("Accept-Language")
if lang == "" {
lang = "zh"
lang = "en"
}
global.I18n = i18n.NewLocalizer(bundle, lang)
}
@ -176,3 +176,7 @@ func GetMsgWithMapForCmd(key string, maps map[string]interface{}) string {
return content
}
}
func GetLanguageFromDB() {
}

View file

@ -4,6 +4,7 @@ import (
"crypto/tls"
"encoding/gob"
"fmt"
"github.com/1Panel-dev/1Panel/core/init/db"
"net"
"net/http"
"os"
@ -13,7 +14,6 @@ import (
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/i18n"
"github.com/1Panel-dev/1Panel/core/init/cron"
"github.com/1Panel-dev/1Panel/core/init/db"
"github.com/1Panel-dev/1Panel/core/init/hook"
"github.com/1Panel-dev/1Panel/core/init/lang"
"github.com/1Panel-dev/1Panel/core/init/log"
@ -29,9 +29,9 @@ import (
func Start() {
viper.Init()
db.Init()
i18n.Init()
log.Init()
db.Init()
migration.Init()
validator.Init()
lang.Init()

View file

@ -295,7 +295,14 @@ export namespace App {
}
export interface AppStoreConfig {
defaultDomain: string;
uninstallDeleteImage: string;
uninstallDeleteBackup: string;
upgradeBackup: string;
}
export interface AppStoreConfigUpdate {
scope: string;
status: string;
}
export interface CustomAppStoreConfig {

View file

@ -115,7 +115,7 @@ export const getAppStoreConfig = () => {
return http.get<App.AppStoreConfig>(`apps/store/config`);
};
export const updateAppStoreConfig = (req: App.AppStoreConfig) => {
export const updateAppStoreConfig = (req: App.AppStoreConfigUpdate) => {
return http.post(`apps/store/update`, req);
};

View file

@ -60,6 +60,9 @@ export const updateAgentSetting = (param: Setting.SettingUpdate) => {
export const getAgentSettingInfo = () => {
return http.post<Setting.SettingInfo>(`/settings/search`);
};
export const getAgentSettingByKey = (key: string) => {
return http.get<string>(`/settings/get/${key}`);
};
// core
export const getSettingInfo = () => {

View file

@ -1,5 +1,11 @@
<template>
<DrawerPro v-model="open" size="large" :header="$t('menu.msgCenter')" @close="handleClose">
<DrawerPro
v-model="open"
size="large"
:header="$t('menu.msgCenter')"
:resource="globalStore.currentNode"
@close="handleClose"
>
<template #content>
<LayoutContent v-loading="loading" :title="$t('logs.task')">
<template #rightToolBar>
@ -50,6 +56,8 @@ import { reactive, ref } from '@vue/runtime-core';
import { Log } from '@/api/interface/log';
import TaskLog from '@/components/log/task/index.vue';
import bus from '@/global/bus';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
const open = ref(false);
const handleClose = () => {

View file

@ -2130,6 +2130,9 @@ const message = {
specifyIP: 'Bind Host IP',
specifyIPHelper:
'Set the host address/network interface to bind the port (if you are not sure about this, please do not fill it in)',
uninstallDeleteBackup: 'Uninstall App Deletes Backup',
uninstallDeleteImage: 'Uninstall App Deletes Image',
upgradeBackup: 'Backup Before Upgrading App',
},
website: {
primaryDomain: 'Primary Domain',

View file

@ -2035,6 +2035,9 @@ const message = {
specifyIP: 'ホスト IP をバインド',
specifyIPHelper:
'ポートにバインドするホストアドレス/ネットワークインターフェースを設定しますこの機能がわからない場合は入力しないでください',
uninstallDeleteBackup: 'アプリをアンインストールしてバックアップを削除',
uninstallDeleteImage: 'アプリをアンインストールしてイメージを削除',
upgradeBackup: 'アプリのアップグレード前にバックアップ',
},
website: {
primaryDomain: 'プライマリドメイン',

View file

@ -2000,6 +2000,9 @@ const message = {
specifyIP: '호스트 IP 바인딩',
specifyIPHelper:
'포트 바인딩을 위한 호스트 주소/네트워크 인터페이스를 설정합니다 ( 기능을 모를 경우, 입력하지 마십시오)',
uninstallDeleteBackup: ' 제거 백업 삭제',
uninstallDeleteImage: ' 제거 이미지 삭제',
upgradeBackup: ' 업그레이드 백업',
},
website: {
primaryDomain: '기본 도메인',

View file

@ -2088,6 +2088,9 @@ const message = {
specifyIP: 'Bind IP Hos',
specifyIPHelper:
'Tetapkan alamat hos/antara muka rangkaian untuk mengikat port (jika anda tidak pasti mengenai ini, jangan isi)',
uninstallDeleteBackup: 'Cop Terhapus Semasa Nyahpasang Aplikasi',
uninstallDeleteImage: 'Imej Terhapus Semasa Nyahpasang Aplikasi',
upgradeBackup: 'Sandaran Sebelum Naik Taraf Aplikasi',
},
website: {
primaryDomain: 'Domain Utama',

View file

@ -2080,6 +2080,9 @@ const message = {
specifyIP: 'Vincular IP do Host',
specifyIPHelper:
'Defina o endereço do host/interface de rede para vincular a porta (se você não tiver certeza sobre isso, por favor, não preencha)',
uninstallDeleteBackup: 'Desinstalar Aplicativo Deleta Backup',
uninstallDeleteImage: 'Desinstalar Aplicativo Deleta Imagem',
upgradeBackup: 'Fazer Backup Antes de Atualizar o Aplicativo',
},
website: {
primaryDomain: 'Domínio principal',

View file

@ -2081,6 +2081,9 @@ const message = {
specifyIP: 'Привязать IP хоста',
specifyIPHelper:
'Установите адрес хоста/сетевого интерфейса для привязки порта (если вы не уверены в этом, пожалуйста, не заполняйте)',
uninstallDeleteBackup: 'Деинсталляция приложения удаляет резервную копию',
uninstallDeleteImage: 'Деинсталляция приложения удаляет образ',
upgradeBackup: 'Резервное копирование перед обновлением приложения',
},
website: {
primaryDomain: 'Основной домен',

View file

@ -1970,6 +1970,9 @@ const message = {
ignoreVersion: '忽略指定版本',
specifyIP: '綁定主機 IP',
specifyIPHelper: '設置端口綁定的主機地址/網卡如果你不清楚這個的作用請不要填寫',
uninstallDeleteBackup: '卸載應用刪除備份',
uninstallDeleteImage: '卸載應用刪除鏡像',
upgradeBackup: '升級應用前備份',
},
website: {
primaryDomain: '主域名',

View file

@ -1960,6 +1960,9 @@ const message = {
ignoreVersion: '忽略指定版本',
specifyIP: '绑定主机 IP',
specifyIPHelper: '设置端口绑定的主机地址/网卡如果你不清楚这个的作用请不要填写',
uninstallDeleteBackup: '卸载应用删除备份',
uninstallDeleteImage: '卸载应用删除镜像',
upgradeBackup: '升级应用前备份',
},
website: {
primaryDomain: '主域名',

View file

@ -14,8 +14,6 @@ import i18n from '@/lang';
import { onMounted, ref } from 'vue';
import { searchAppInstalled } from '@/api/modules/app';
import bus from '@/global/bus';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
let showButton = ref(false);
let buttons = [
@ -32,9 +30,6 @@ let buttons = [
path: '/apps/upgrade',
count: 0,
},
];
const settingButtons = [
{
label: i18n.global.t('commons.button.set'),
path: '/apps/setting',
@ -56,10 +51,6 @@ const search = () => {
};
onMounted(() => {
if (globalStore.isProductPro) {
buttons = buttons.concat(settingButtons);
}
search();
bus.on('upgrade', () => {
showButton.value = false;

View file

@ -47,7 +47,7 @@
import { FormInstance } from 'element-plus';
import { onBeforeUnmount, ref } from 'vue';
import { App } from '@/api/interface/app';
import { installedOp } from '@/api/modules/app';
import { getAppStoreConfig, installedOp } from '@/api/modules/app';
import i18n from '@/lang';
import bus from '@/global/bus';
import TaskLog from '@/components/log/task/index.vue';
@ -79,13 +79,14 @@ const handleClose = () => {
};
const acceptParams = async (app: App.AppInstallDto) => {
const config = await getAppStoreConfig();
deleteReq.value = {
operate: 'delete',
installId: 0,
deleteBackup: false,
deleteBackup: config.data.uninstallDeleteBackup === 'True',
forceDelete: false,
deleteDB: true,
deleteImage: false,
deleteImage: config.data.uninstallDeleteImage === 'True',
taskID: uuidv4(),
};
deleteInfo.value = '';

View file

@ -14,13 +14,19 @@
{{ $t('commons.button.edit') }}
</el-button>
</span>
<el-input v-else v-model="appConfigUpdate.webUI" :placeholder="$t('app.webUIPlaceholder')">
<template #append>
<el-button type="primary" @click="updateAppConfig">
{{ $t('commons.button.confirm') }}
</el-button>
</template>
</el-input>
<span class="flex" v-else>
<el-input v-model="webUI.domain" :placeholder="$t('app.webUIPlaceholder')">
<template #prepend>
<el-select v-model="webUI.protocol" class="pre-select">
<el-option label="http" value="http://" />
<el-option label="https" value="https://" />
</el-select>
</template>
</el-input>
<el-button type="primary" @click="updateAppConfig" class="ml-2">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</el-descriptions-item>
<el-descriptions-item v-for="(param, key) in params" :label="getLabel(param)" :key="key">
<span>{{ param.showValue && param.showValue != '' ? param.showValue : param.value }}</span>
@ -125,7 +131,7 @@ import { reactive, ref } from 'vue';
import { FormInstance } from 'element-plus';
import { Rules, checkNumberRange } from '@/global/form-rules';
import { MsgSuccess } from '@/utils/message';
import { getLabel } from '@/utils/util';
import { getLabel, splitHttp } from '@/utils/util';
import i18n from '@/lang';
interface ParamProps {
@ -141,6 +147,7 @@ interface EditForm extends App.InstallParams {
default: any;
}
const emit = defineEmits(['close']);
const open = ref(false);
const loading = ref(false);
const params = ref<EditForm[]>();
@ -164,6 +171,10 @@ const appConfigUpdate = ref<App.AppConfigUpdate>({
webUI: '',
});
const openConfig = ref(false);
const webUI = reactive({
protocol: 'http://',
domain: '',
});
const acceptParams = async (props: ParamProps) => {
submitModel.value.installId = props.id;
@ -177,6 +188,7 @@ const acceptParams = async (props: ParamProps) => {
};
const handleClose = () => {
emit('close');
open.value = false;
};
const editParam = () => {
@ -227,6 +239,11 @@ const get = async () => {
paramModel.value.isHostMode = res.data.hostMode;
paramModel.value.specifyIP = res.data.specifyIP;
appConfigUpdate.value.webUI = res.data.webUI;
if (res.data.webUI != '') {
const httpConfig = splitHttp(res.data.webUI);
webUI.domain = httpConfig.url;
webUI.protocol = httpConfig.proto;
}
appType.value = res.data.type;
} catch (error) {
} finally {
@ -272,10 +289,13 @@ const submit = async (formEl: FormInstance) => {
};
const updateAppConfig = async () => {
if (!webUI.domain || webUI.domain === '') {
return;
}
try {
await updateInstallConfig({
installID: Number(paramData.value.id),
webUI: appConfigUpdate.value.webUI,
webUI: webUI.protocol + webUI.domain,
});
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
handleClose();

View file

@ -413,13 +413,13 @@
<Uploads ref="uploadRef" />
<AppResources ref="checkRef" @close="search" />
<AppDelete ref="deleteRef" @close="search" />
<AppParams ref="appParamRef" />
<AppParams ref="appParamRef" @close="search" />
<AppUpgrade ref="upgradeRef" @close="search" />
<PortJumpDialog ref="dialogPortJumpRef" />
<AppIgnore ref="ignoreRef" @close="search" />
<ComposeLogs ref="composeLogRef" />
<TaskLog ref="taskLogRef" @close="search" />
<Detail ref="detailRef" />
<Detail ref="detailRef" @close="search" />
<IgnoreApp ref="ignoreAppRef" @close="search" />
</template>
@ -430,7 +430,6 @@ import {
syncInstalledApp,
appInstalledDeleteCheck,
getAppTags,
getAppStoreConfig,
} from '@/api/modules/app';
import { onMounted, onUnmounted, reactive, ref } from 'vue';
import i18n from '@/lang';
@ -453,6 +452,7 @@ import { toFolder } from '@/global/business';
import TaskLog from '@/components/log/task/index.vue';
import Detail from '@/views/app-store/detail/index.vue';
import IgnoreApp from '@/views/app-store/installed/ignore/create/index.vue';
import { getAgentSettingByKey } from '@/api/modules/setting';
const data = ref<any>();
const loading = ref(false);
@ -773,9 +773,9 @@ const openLog = (row: any) => {
const getConfig = async () => {
try {
const res = await getAppStoreConfig();
if (res.data.defaultDomain != '') {
defaultLink.value = res.data.defaultDomain;
const res = await getAgentSettingByKey('SystemIP');
if (res.data != '') {
defaultLink.value = res.data;
}
} catch (error) {}
};

View file

@ -85,7 +85,7 @@
</template>
<script lang="ts" setup>
import { App } from '@/api/interface/app';
import { getAppUpdateVersions, ignoreUpgrade, installedOp } from '@/api/modules/app';
import { getAppStoreConfig, getAppUpdateVersions, ignoreUpgrade, installedOp } from '@/api/modules/app';
import i18n from '@/lang';
import { ElMessageBox, FormInstance } from 'element-plus';
import { reactive, ref, onBeforeUnmount } from 'vue';
@ -156,10 +156,11 @@ const getNewCompose = (compose: string) => {
}
};
const initData = () => {
const initData = async () => {
const config = await getAppStoreConfig();
newCompose.value = '';
useNewCompose.value = false;
operateReq.backup = true;
operateReq.backup = config.data.upgradeBackup == 'True';
operateReq.pullImage = true;
operateReq.dockerCompose = '';
};

View file

@ -1,73 +0,0 @@
<template>
<DrawerPro v-model="drawerVisible" :header="$t('app.defaultWebDomain')" @close="handleClose" size="small">
<el-form ref="formRef" label-position="top" :model="form" :rules="rules" @submit.prevent v-loading="loading">
<el-form-item :label="$t('app.defaultWebDomain')" prop="defaultDomain">
<el-input v-model="form.defaultDomain"></el-input>
<span class="input-help">{{ $t('app.defaultWebDomainHepler') }}</span>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="handleClose()">{{ $t('commons.button.cancel') }}</el-button>
<el-button :disabled="loading" type="primary" @click="submit()">
{{ $t('commons.button.confirm') }}
</el-button>
</template>
</DrawerPro>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import i18n from '@/lang';
import { FormInstance } from 'element-plus';
import { Rules } from '@/global/form-rules';
import { updateAppStoreConfig } from '@/api/modules/app';
import { MsgSuccess } from '@/utils/message';
const emit = defineEmits<{ (e: 'close'): void }>();
const drawerVisible = ref();
const loading = ref();
const form = reactive({
defaultDomain: '',
});
const rules = reactive({
defaultDomain: [Rules.requiredInput, Rules.ipV4V6OrDomain],
});
const formRef = ref<FormInstance>();
interface DialogProps {
domain: string;
}
const acceptParams = (config: DialogProps): void => {
form.defaultDomain = config.domain;
drawerVisible.value = true;
};
const handleClose = () => {
drawerVisible.value = false;
emit('close');
};
const submit = async () => {
if (!formRef.value) return;
await formRef.value.validate(async (valid) => {
if (!valid) {
return;
}
loading.value = true;
try {
const req = {
defaultDomain: form.defaultDomain,
};
await updateAppStoreConfig(req);
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
} catch (error) {
} finally {
loading.value = false;
handleClose();
}
});
};
defineExpose({
acceptParams,
});
</script>

View file

@ -12,15 +12,32 @@
>
<el-row>
<el-col :xs="24" :sm="20" :md="15" :lg="12" :xl="12">
<el-form-item :label="$t('app.defaultWebDomain')" prop="defaultDomain">
<el-input v-model="config.defaultDomain" disabled>
<template #append>
<el-button @click="setDefaultDomain()" icon="Setting">
{{ $t('commons.button.set') }}
</el-button>
</template>
</el-input>
<span class="input-help">{{ $t('app.defaultWebDomainHepler') }}</span>
<el-form-item :label="$t('app.uninstallDeleteBackup')" prop="uninstallDeleteBackup">
<el-switch
v-model="config.uninstallDeleteBackup"
active-value="True"
inactive-value="False"
:loading="loading"
@change="updateConfig('UninstallDeleteBackup', config.uninstallDeleteBackup)"
/>
</el-form-item>
<el-form-item :label="$t('app.uninstallDeleteImage')" prop="uninstallDeleteImage">
<el-switch
v-model="config.uninstallDeleteImage"
active-value="True"
inactive-value="False"
:loading="loading"
@change="updateConfig('UninstallDeleteImage', config.uninstallDeleteImage)"
/>
</el-form-item>
<el-form-item :label="$t('app.upgradeBackup')" prop="upgradeBackup">
<el-switch
v-model="config.upgradeBackup"
active-value="True"
inactive-value="False"
:loading="loading"
@change="updateConfig('UpgradeBackup', config.upgradeBackup)"
/>
</el-form-item>
<CustomSetting v-if="globalStore.isProductPro" />
</el-col>
@ -28,35 +45,38 @@
</el-form>
</template>
</LayoutContent>
<DefaultDomain ref="domainRef" @close="search" />
</template>
<script setup lang="ts">
import { getAppStoreConfig, getCurrentNodeCustomAppConfig } from '@/api/modules/app';
import { Rules } from '@/global/form-rules';
import { getAppStoreConfig, getCurrentNodeCustomAppConfig, updateAppStoreConfig } from '@/api/modules/app';
import { FormRules } from 'element-plus';
import CustomSetting from '@/xpack/views/appstore/index.vue';
import DefaultDomain from './default-domain/index.vue';
import { GlobalStore } from '@/store';
import { MsgSuccess } from '@/utils/message';
import i18n from '@/lang';
const globalStore = GlobalStore();
const rules = ref<FormRules>({
defaultDomain: [Rules.domainOrIP],
});
const rules = ref<FormRules>({});
const config = ref({
defaultDomain: '',
uninstallDeleteImage: '',
uninstallDeleteBackup: '',
upgradeBackup: '',
});
const loading = ref(false);
const configForm = ref();
const domainRef = ref();
const useCustomApp = ref(false);
const isInitializing = ref(true);
const search = async () => {
loading.value = true;
try {
const res = await getAppStoreConfig();
if (res.data.defaultDomain != '') {
config.value.defaultDomain = res.data.defaultDomain;
if (res && res.data) {
isInitializing.value = true;
config.value = res.data;
setTimeout(() => {
isInitializing.value = false;
}, 0);
}
} catch (error) {
} finally {
@ -64,12 +84,6 @@ const search = async () => {
}
};
const setDefaultDomain = () => {
domainRef.value.acceptParams({
domain: config.value.defaultDomain,
});
};
const getNodeConfig = async () => {
if (globalStore.isMasterProductPro) {
return;
@ -80,6 +94,25 @@ const getNodeConfig = async () => {
}
};
const updateConfig = async (scope: string, value: string) => {
if (isInitializing.value) {
return;
}
loading.value = true;
try {
const req = {
scope: scope,
status: value,
};
await updateAppStoreConfig(req);
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
search();
} catch (error) {
} finally {
loading.value = false;
}
};
onMounted(() => {
search();
getNodeConfig();

View file

@ -23,40 +23,36 @@
</template>
</el-alert>
<el-form :model="form" ref="formRef" @submit.prevent v-loading="loading" label-position="top" :rules="rules">
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item :label="$t('setting.apiKey')" prop="apiKey">
<el-input v-model="form.apiKey" readonly>
<template #suffix>
<CopyButton type="icon" :content="form.apiKey" class="w-30" />
</template>
<template #append>
<el-button @click="resetApiKey()">
{{ $t('commons.button.reset') }}
</el-button>
</template>
</el-input>
<span class="input-help">{{ $t('setting.apiKeyHelper') }}</span>
</el-form-item>
<el-form-item :label="$t('setting.ipWhiteList')" prop="ipWhiteList">
<el-input
type="textarea"
:placeholder="$t('setting.ipWhiteListEgs')"
:rows="4"
v-model="form.ipWhiteList"
/>
<span class="input-help">{{ $t('setting.ipWhiteListHelper') }}</span>
</el-form-item>
<el-form-item :label="$t('setting.apiKeyValidityTime')" prop="apiKeyValidityTime">
<el-input :placeholder="$t('setting.apiKeyValidityTimeEgs')" v-model="form.apiKeyValidityTime">
<template #append>{{ $t('commons.units.minute') }}</template>
</el-input>
<span class="input-help">
{{ $t('setting.apiKeyValidityTimeHelper') }}
</span>
</el-form-item>
</el-col>
</el-row>
<el-form-item :label="$t('setting.apiKey')" prop="apiKey">
<el-input v-model="form.apiKey" readonly>
<template #suffix>
<CopyButton type="icon" :content="form.apiKey" class="w-30" />
</template>
<template #append>
<el-button @click="resetApiKey()">
{{ $t('commons.button.reset') }}
</el-button>
</template>
</el-input>
<span class="input-help">{{ $t('setting.apiKeyHelper') }}</span>
</el-form-item>
<el-form-item :label="$t('setting.ipWhiteList')" prop="ipWhiteList">
<el-input
type="textarea"
:placeholder="$t('setting.ipWhiteListEgs')"
:rows="4"
v-model="form.ipWhiteList"
/>
<span class="input-help">{{ $t('setting.ipWhiteListHelper') }}</span>
</el-form-item>
<el-form-item :label="$t('setting.apiKeyValidityTime')" prop="apiKeyValidityTime">
<el-input :placeholder="$t('setting.apiKeyValidityTimeEgs')" v-model="form.apiKeyValidityTime">
<template #append>{{ $t('commons.units.minute') }}</template>
</el-input>
<span class="input-help">
{{ $t('setting.apiKeyValidityTimeHelper') }}
</span>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">

View file

@ -686,7 +686,7 @@ const changeType = (type: string) => {
switch (type) {
case 'deployment':
website.value.appType = 'installed';
if (appInstalls.value && appInstalls.value.length > 0) {
if (appInstalls.value && appInstalls.value.length > 0 && appInstalls.value[0].status === 'Running') {
website.value.appInstallId = appInstalls.value[0].id;
}
break;
@ -713,7 +713,12 @@ const searchAppInstalled = (appType: string) => {
getAppInstalled({ type: appType, unused: true, all: true, page: 1, pageSize: 100 }).then((res) => {
appInstalls.value = res.data.items;
website.value.appInstallId = undefined;
if (appType == 'website' && res.data.items && res.data.items.length > 0) {
if (
appType == 'website' &&
res.data.items &&
res.data.items.length > 0 &&
res.data.items[0].status === 'Running'
) {
website.value.appInstallId = res.data.items[0].id;
}
});