fix: Fix some of the problems of node switching (#8573)

This commit is contained in:
ssongliu 2025-05-08 18:01:42 +08:00 committed by GitHub
parent 099ca7ff6c
commit 69d24a8b90
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 118 additions and 71 deletions

View file

@ -32,7 +32,7 @@ func (b *BaseApi) LoadDashboardOsInfo(c *gin.Context) {
// @Security Timestamp // @Security Timestamp
// @Router /dashboard/app/launcher [get] // @Router /dashboard/app/launcher [get]
func (b *BaseApi) LoadAppLauncher(c *gin.Context) { func (b *BaseApi) LoadAppLauncher(c *gin.Context) {
data, err := dashboardService.LoadAppLauncher() data, err := dashboardService.LoadAppLauncher(c)
if err != nil { if err != nil {
helper.InternalServer(c, err) helper.InternalServer(c, err)
return return

View file

@ -140,9 +140,8 @@ type AppLauncher struct {
Name string `json:"name"` Name string `json:"name"`
Icon string `json:"icon"` Icon string `json:"icon"`
Limit int `json:"limit"` Limit int `json:"limit"`
ShortDescZh string `json:"shortDescZh"` Description string `json:"description"`
ShortDescEn string `json:"shortDescEn"` Recommend int `json:"recommend"`
Recommend int `json:"recomend"`
IsInstall bool `json:"isInstall"` IsInstall bool `json:"isInstall"`
IsRecommend bool `json:"isRecommend"` IsRecommend bool `json:"isRecommend"`

View file

@ -21,6 +21,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/cmd" "github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common" "github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/copier" "github.com/1Panel-dev/1Panel/agent/utils/copier"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/v3/cpu" "github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk" "github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/host" "github.com/shirou/gopsutil/v3/host"
@ -37,7 +38,7 @@ type IDashboardService interface {
LoadCurrentInfoForNode() *dto.NodeCurrent LoadCurrentInfoForNode() *dto.NodeCurrent
LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent
LoadAppLauncher() ([]dto.AppLauncher, error) LoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher, error)
ChangeShow(req dto.SettingUpdate) error ChangeShow(req dto.SettingUpdate) error
ListLauncherOption(filter string) ([]dto.LauncherOption, error) ListLauncherOption(filter string) ([]dto.LauncherOption, error)
Restart(operation string) error Restart(operation string) error
@ -263,7 +264,7 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
return &currentInfo return &currentInfo
} }
func (u *DashboardService) LoadAppLauncher() ([]dto.AppLauncher, error) { func (u *DashboardService) LoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher, error) {
var ( var (
data []dto.AppLauncher data []dto.AppLauncher
recommendList []dto.AppLauncher recommendList []dto.AppLauncher
@ -289,9 +290,8 @@ func (u *DashboardService) LoadAppLauncher() ([]dto.AppLauncher, error) {
itemData.Name = app.Name itemData.Name = app.Name
itemData.Icon = app.Icon itemData.Icon = app.Icon
itemData.Limit = app.Limit itemData.Limit = app.Limit
itemData.ShortDescEn = app.ShortDescEn
itemData.ShortDescZh = app.ShortDescZh
itemData.Recommend = app.Recommend itemData.Recommend = app.Recommend
itemData.Description = app.GetDescription(ctx)
break break
} }
} }

View file

@ -283,6 +283,11 @@ func snapBaseData(snap snapHelper, targetDir string) error {
if err != nil { if err != nil {
return err return err
} }
err = snap.FileOp.CopyFile("/usr/local/bin/1pctl", targetDir)
snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1pctl"), err)
if err != nil {
return err
}
} }
err := snap.FileOp.CopyFile("/usr/local/bin/1panel-agent", targetDir) err := snap.FileOp.CopyFile("/usr/local/bin/1panel-agent", targetDir)
@ -291,12 +296,6 @@ func snapBaseData(snap snapHelper, targetDir string) error {
return err return err
} }
err = snap.FileOp.CopyFile("/usr/local/bin/1pctl", targetDir)
snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1pctl"), err)
if err != nil {
return err
}
if global.IsMaster { if global.IsMaster {
err = snap.FileOp.CopyFile("/etc/systemd/system/1panel-core.service", targetDir) err = snap.FileOp.CopyFile("/etc/systemd/system/1panel-core.service", targetDir)
snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel-core.service"), err) snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel-core.service"), err)

View file

@ -246,12 +246,12 @@ func backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error {
} }
} }
err = itemHelper.FileOp.CopyFile("/usr/local/bin/1pctl", baseDir)
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1pctl"), err)
if err != nil {
return err
}
if global.IsMaster { if global.IsMaster {
err = itemHelper.FileOp.CopyFile("/usr/local/bin/1pctl", baseDir)
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1pctl"), err)
if err != nil {
return err
}
err = itemHelper.FileOp.CopyFile("/usr/local/bin/1panel-core", baseDir) err = itemHelper.FileOp.CopyFile("/usr/local/bin/1panel-core", baseDir)
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-core"), err) itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-core"), err)
if err != nil { if err != nil {
@ -328,13 +328,12 @@ func recoverBaseData(src string, itemHelper *snapRecoverHelper) error {
itemHelper.Task.Log("---------------------- 6 / 10 ----------------------") itemHelper.Task.Log("---------------------- 6 / 10 ----------------------")
itemHelper.Task.LogStart(i18n.GetMsgByKey("SnapBaseInfo")) itemHelper.Task.LogStart(i18n.GetMsgByKey("SnapBaseInfo"))
err := itemHelper.FileOp.CopyFile(path.Join(src, "1pctl"), "/usr/local/bin")
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1pctl"), err)
if err != nil {
return err
}
if global.IsMaster { if global.IsMaster {
err := itemHelper.FileOp.CopyFile(path.Join(src, "1pctl"), "/usr/local/bin")
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1pctl"), err)
if err != nil {
return err
}
err = itemHelper.FileOp.CopyFile(path.Join(src, "1panel-core"), "/usr/local/bin") err = itemHelper.FileOp.CopyFile(path.Join(src, "1panel-core"), "/usr/local/bin")
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-core"), err) itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-core"), err)
if err != nil { if err != nil {
@ -346,7 +345,7 @@ func recoverBaseData(src string, itemHelper *snapRecoverHelper) error {
return err return err
} }
} }
err = itemHelper.FileOp.CopyFile(path.Join(src, "1panel-agent"), "/usr/local/bin") err := itemHelper.FileOp.CopyFile(path.Join(src, "1panel-agent"), "/usr/local/bin")
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-agent"), err) itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-agent"), err)
if err != nil { if err != nil {
return err return err

View file

@ -25,6 +25,9 @@
</el-upload> </el-upload>
</el-col> </el-col>
</el-row> </el-row>
<div v-if="oldLicense">
<el-checkbox v-model="isForce">{{ $t('license.updateForce') }}</el-checkbox>
</div>
<el-button <el-button
type="primary" type="primary"
class="mt-3 w-52 custom-button" class="mt-3 w-52 custom-button"
@ -57,6 +60,7 @@ const open = ref(false);
const uploadRef = ref<UploadInstance>(); const uploadRef = ref<UploadInstance>();
const uploaderFiles = ref<UploadFiles>([]); const uploaderFiles = ref<UploadFiles>([]);
const isImport = ref(); const isImport = ref();
const isForce = ref();
const oldLicense = ref(); const oldLicense = ref();
interface DialogProps { interface DialogProps {
@ -109,6 +113,7 @@ const submit = async () => {
if (!isImport.value) { if (!isImport.value) {
formData.append('currentNode', globalStore.currentNode); formData.append('currentNode', globalStore.currentNode);
} }
formData.append('isForce', isForce.value);
loading.value = true; loading.value = true;
await uploadLicense(oldLicense.value, formData) await uploadLicense(oldLicense.value, formData)
.then(async () => { .then(async () => {

View file

@ -21,6 +21,8 @@ import i18n from '@/lang';
import { MsgError, MsgWarning } from '@/utils/message'; import { MsgError, MsgWarning } from '@/utils/message';
import { jumpToPath } from '@/utils/util'; import { jumpToPath } from '@/utils/util';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
const router = useRouter(); const router = useRouter();
const open = ref(); const open = ref();
@ -39,8 +41,12 @@ const acceptParams = async (params: DialogProps): Promise<void> => {
let protocol = params.protocol === 'https' ? 'https' : 'http'; let protocol = params.protocol === 'https' ? 'https' : 'http';
const res = await getAgentSettingInfo(); const res = await getAgentSettingInfo();
if (!res.data.systemIP) { if (!res.data.systemIP) {
open.value = true; if (!globalStore.currentNodeAddr) {
return; open.value = true;
return;
} else {
res.data.systemIP = globalStore.currentNodeAddr;
}
} }
if (res.data.systemIP.indexOf(':') === -1) { if (res.data.systemIP.indexOf(':') === -1) {
if (params.ip && params.ip === 'ipv6') { if (params.ip && params.ip === 'ipv6') {

View file

@ -602,7 +602,9 @@ const message = {
containerConnHelper: containerConnHelper:
'This connection address is used by applications running on the PHP execution environment/container installation.', 'This connection address is used by applications running on the PHP execution environment/container installation.',
remoteConn: 'External Connection', remoteConn: 'External Connection',
remoteConnHelper2: 'Use this address for non-container or external connections', remoteConnHelper2: 'Use this address for non-container environments or external connections.',
remoteConnHelper3:
'The default access address is the host IP. To modify it, go to the "Default Access Address" configuration item in the panel settings page.',
localIP: 'Local IP', localIP: 'Local IP',
}, },
aiTools: { aiTools: {
@ -1922,6 +1924,7 @@ const message = {
forceUnbind: 'Force Unbind', forceUnbind: 'Force Unbind',
forceUnbindHelper: forceUnbindHelper:
'Forcing unbind will ignore any errors that occur during the unbinding process and ultimately release the license binding.', 'Forcing unbind will ignore any errors that occur during the unbinding process and ultimately release the license binding.',
updateForce: 'Force update (ignore all errors during unbinding to ensure final operation succeeds)',
trialInfo: 'Version', trialInfo: 'Version',
authorizationId: 'Authorization ID', authorizationId: 'Authorization ID',
authorizedUser: 'Authorized User', authorizedUser: 'Authorized User',

View file

@ -590,8 +590,9 @@ const message = {
containerConnHelper: containerConnHelper:
'この接続アドレスはWebサイトのランタイムPHPなどまたはコンテナで実行されているアプリケーションで使用できます', 'この接続アドレスはWebサイトのランタイムPHPなどまたはコンテナで実行されているアプリケーションで使用できます',
remoteConn: '外部接続', remoteConn: '外部接続',
remoteConnHelper2: remoteConnHelper2: 'コンテナ環境以外または外部接続にはこのアドレスを使用してください',
'この接続アドレスは非コンテナまたは外部アプリケーションで実行されているアプリケーションで使用できます', remoteConnHelper3:
'デフォルトアクセスアドレスはホストIPです変更するにはパネル設定ページのデフォルトアクセスアドレス設定項目へ移動してください',
localIP: 'ローカルIP', localIP: 'ローカルIP',
}, },
aiTools: { aiTools: {
@ -1831,6 +1832,7 @@ const message = {
forceUnbind: '強制バインド解除', forceUnbind: '強制バインド解除',
forceUnbindHelper: forceUnbindHelper:
'強制的にバインド解除を行うと解除プロセス中に発生するエラーを無視し最終的にライセンスのバインドを解除します', '強制的にバインド解除を行うと解除プロセス中に発生するエラーを無視し最終的にライセンスのバインドを解除します',
updateForce: '強制更新アンバインド中のすべてのエラーを無視し最終操作の成功を保証します',
trialInfo: 'バージョン', trialInfo: 'バージョン',
authorizationId: 'サブスクリプション承認ID', authorizationId: 'サブスクリプション承認ID',
authorizedUser: '認定ユーザー', authorizedUser: '認定ユーザー',

View file

@ -587,8 +587,9 @@ const message = {
containerConnHelper: containerConnHelper:
' 연결 주소는 웹사이트 런타임(PHP ) 또는 컨테이너에서 실행 중인 애플리케이션에서 사용할 있습니다.', ' 연결 주소는 웹사이트 런타임(PHP ) 또는 컨테이너에서 실행 중인 애플리케이션에서 사용할 있습니다.',
remoteConn: '외부 연결', remoteConn: '외부 연결',
remoteConnHelper2: remoteConnHelper2: '컨테이너 환경이 아닌 경우 또는 외부 연결에는 주소를 사용하십시오.',
' 연결 주소는 컨테이너 외부 또는 외부 애플리케이션에서 실행 중인 애플리케이션에서 사용할 있습니다.', remoteConnHelper3:
'기본 접근 주소는 호스트 IP입니다. 수정하려면 패널 설정 페이지의 "기본 접근 주소" 구성 항목으로 이동하세요.',
localIP: '로컬 IP', localIP: '로컬 IP',
}, },
aiTools: { aiTools: {
@ -1801,6 +1802,7 @@ const message = {
forceUnbind: '강제 바인딩 해제', forceUnbind: '강제 바인딩 해제',
forceUnbindHelper: forceUnbindHelper:
'강제 바인딩 해제를 수행하면 해제 과정에서 발생하는 오류를 무시하고 궁극적으로 라이센스 바인딩을 해제합니다.', '강제 바인딩 해제를 수행하면 해제 과정에서 발생하는 오류를 무시하고 궁극적으로 라이센스 바인딩을 해제합니다.',
updateForce: '강제 업데이트 (바인딩 해제 과정의 모든 오류를 무시하고 최종 작업 성공을 보장합니다)',
trialInfo: '버전', trialInfo: '버전',
authorizationId: '구독 인증 ID', authorizationId: '구독 인증 ID',
authorizedUser: '인증된 사용자', authorizedUser: '인증된 사용자',

View file

@ -602,8 +602,9 @@ const message = {
containerConnHelper: containerConnHelper:
'Alamat sambungan ini boleh digunakan oleh aplikasi yang berjalan pada runtime laman web (PHP, dll.) atau kontena.', 'Alamat sambungan ini boleh digunakan oleh aplikasi yang berjalan pada runtime laman web (PHP, dll.) atau kontena.',
remoteConn: 'Sambungan luaran', remoteConn: 'Sambungan luaran',
remoteConnHelper2: remoteConnHelper2: 'Gunakan alamat ini untuk persekitaran bukan kontena atau sambungan luar.',
'Alamat sambungan ini boleh digunakan oleh aplikasi yang berjalan di luar kontena atau aplikasi luaran.', remoteConnHelper3:
'Alamat akses lalai ialah IP hos. Untuk mengubahnya, pergi ke item konfigurasi "Alamat Akses Lalai" pada halaman tetapan panel.',
localIP: 'IP Tempatan', localIP: 'IP Tempatan',
}, },
aiTools: { aiTools: {
@ -1889,6 +1890,7 @@ const message = {
forceUnbind: 'Paksakan Nyahikat', forceUnbind: 'Paksakan Nyahikat',
forceUnbindHelper: forceUnbindHelper:
'Memaksa nyahikat akan mengabaikan sebarang ralat yang berlaku semasa proses nyahikat dan akhirnya melepaskan ikatan lesen.', 'Memaksa nyahikat akan mengabaikan sebarang ralat yang berlaku semasa proses nyahikat dan akhirnya melepaskan ikatan lesen.',
updateForce: 'Kemas kini paksa (abaikan semua ralat semasa nyahikatan untuk memastikan operasi akhir berjaya)',
trialInfo: 'Versi', trialInfo: 'Versi',
authorizationId: 'ID Kebenaran Langganan', authorizationId: 'ID Kebenaran Langganan',
authorizedUser: 'Pengguna yang Dibenarkan', authorizedUser: 'Pengguna yang Dibenarkan',

View file

@ -599,8 +599,9 @@ const message = {
containerConnHelper: containerConnHelper:
'Este endereço de conexão pode ser utilizado por aplicações que estão em execução nos ambientes do site (PHP, etc.) ou no contêiner.', 'Este endereço de conexão pode ser utilizado por aplicações que estão em execução nos ambientes do site (PHP, etc.) ou no contêiner.',
remoteConn: 'Conexão externa', remoteConn: 'Conexão externa',
remoteConnHelper2: remoteConnHelper2: 'Use este endereço para ambientes não-container ou conexões externas.',
'Este endereço de conexão pode ser utilizado por aplicações que estão fora do contêiner ou por aplicações externas.', remoteConnHelper3:
'O endereço de acesso padrão é o IP do host. Para modificá-lo, acesse o item de configuração "Endereço de Acesso Padrão" na página de configurações do painel.',
localIP: 'IP local', localIP: 'IP local',
}, },
aiTools: { aiTools: {
@ -1876,6 +1877,8 @@ const message = {
forceUnbind: 'Forçar Desvinculação', forceUnbind: 'Forçar Desvinculação',
forceUnbindHelper: forceUnbindHelper:
'Forçar a desvinculação ignorará quaisquer erros que ocorram durante o processo de desvinculação e, em última análise, liberará a vinculação da licença.', 'Forçar a desvinculação ignorará quaisquer erros que ocorram durante o processo de desvinculação e, em última análise, liberará a vinculação da licença.',
updateForce:
'Atualização forçada (ignora todos os erros durante o desvinculamento para garantir o sucesso da operação final)',
trialInfo: 'Versão', trialInfo: 'Versão',
authorizationId: 'ID de autorização', authorizationId: 'ID de autorização',
authorizedUser: 'Usuário autorizado', authorizedUser: 'Usuário autorizado',

View file

@ -597,8 +597,9 @@ const message = {
containerConnHelper: containerConnHelper:
'Этот адрес подключения может использоваться приложениями, работающими в среде выполнения веб-сайта (PHP и т.д.) или контейнере.', 'Этот адрес подключения может использоваться приложениями, работающими в среде выполнения веб-сайта (PHP и т.д.) или контейнере.',
remoteConn: 'Внешнее подключение', remoteConn: 'Внешнее подключение',
remoteConnHelper2: remoteConnHelper2: 'Используйте этот адрес для неконтейнерных сред или внешних подключений.',
'Этот адрес подключения может использоваться приложениями, работающими вне контейнера или внешними приложениями.', remoteConnHelper3:
'Адрес доступа по умолчанию - это IP хоста. Для изменения перейдите к пункту конфигурации "Адрес доступа по умолчанию" на странице настроек панели.',
localIP: 'Локальный IP', localIP: 'Локальный IP',
}, },
aiTools: { aiTools: {
@ -1875,6 +1876,8 @@ const message = {
forceUnbind: 'Принудительное отвязывание', forceUnbind: 'Принудительное отвязывание',
forceUnbindHelper: forceUnbindHelper:
'Принудительное отвязывание будет игнорировать любые ошибки, возникающие в процессе отвязывания, и в конечном итоге освободит привязку лицензии.', 'Принудительное отвязывание будет игнорировать любые ошибки, возникающие в процессе отвязывания, и в конечном итоге освободит привязку лицензии.',
updateForce:
'Принудительное обновление (игнорировать все ошибки при отвязке для гарантии успешного завершения операции)',
trialInfo: 'Версия', trialInfo: 'Версия',
authorizationId: 'ID авторизации подписки', authorizationId: 'ID авторизации подписки',
authorizedUser: 'Авторизованный пользователь', authorizedUser: 'Авторизованный пользователь',

View file

@ -583,7 +583,8 @@ const message = {
connAddress: '地址', connAddress: '地址',
containerConnHelper: 'PHP 執行環境/容器安裝的應用程式使用此連接地址', containerConnHelper: 'PHP 執行環境/容器安裝的應用程式使用此連接地址',
remoteConn: '外部連接', remoteConn: '外部連接',
remoteConnHelper2: '非容器或外部連接使用此地址', remoteConnHelper2: '非容器環境或外部連接需使用此地址',
remoteConnHelper3: '預設存取地址為主機IP修改請前往面板設定頁面的預設存取地址配置項',
localIP: '本機 IP', localIP: '本機 IP',
}, },
aiTools: { aiTools: {
@ -1785,6 +1786,7 @@ const message = {
versionConstraint: '{0} 版本買斷', versionConstraint: '{0} 版本買斷',
forceUnbind: '強制解除綁定', forceUnbind: '強制解除綁定',
forceUnbindHelper: '強制解除綁定將忽略解除過程中產生的錯誤最終解除許可證綁定', forceUnbindHelper: '強制解除綁定將忽略解除過程中產生的錯誤最終解除許可證綁定',
updateForce: '強制更新忽略解除綁定過程中的所有錯誤確保最終操作成功',
trialInfo: '版本', trialInfo: '版本',
authorizationId: '訂閱授權 ID', authorizationId: '訂閱授權 ID',
authorizedUser: '被授權方', authorizedUser: '被授權方',

View file

@ -581,7 +581,8 @@ const message = {
connAddress: '地址', connAddress: '地址',
containerConnHelper: 'PHP 运行环境/容器安装的应用使用此连接地址', containerConnHelper: 'PHP 运行环境/容器安装的应用使用此连接地址',
remoteConn: '外部连接', remoteConn: '外部连接',
remoteConnHelper2: '非容器或外部连接使用此地址', remoteConnHelper2: '非容器环境或外部连接需使用此地址',
remoteConnHelper3: '默认访问地址为主机IP修改请前往面板设置页面的默认访问地址配置项',
localIP: '本机 IP', localIP: '本机 IP',
}, },
aiTools: { aiTools: {
@ -1778,6 +1779,7 @@ const message = {
versionConstraint: '{0} 版本买断', versionConstraint: '{0} 版本买断',
forceUnbind: '强制解绑', forceUnbind: '强制解绑',
forceUnbindHelper: '强制解绑会忽略解绑过程中产生的错误并最终解除许可证绑定', forceUnbindHelper: '强制解绑会忽略解绑过程中产生的错误并最终解除许可证绑定',
updateForce: '强制更新忽略解绑过程中的所有错误确保最终操作成功',
trialInfo: '版本', trialInfo: '版本',
authorizationId: '订阅授权 ID', authorizationId: '订阅授权 ID',
authorizedUser: '被授权方', authorizedUser: '被授权方',

View file

@ -165,6 +165,7 @@ const changeNode = (command: string) => {
} }
if (command == 'local') { if (command == 'local') {
globalStore.currentNode = command || 'local'; globalStore.currentNode = command || 'local';
globalStore.currentNodeAddr = '';
globalStore.isOffline = false; globalStore.isOffline = false;
router.push({ name: 'home' }).then(() => { router.push({ name: 'home' }).then(() => {
window.location.reload(); window.location.reload();
@ -186,6 +187,7 @@ const changeNode = (command: string) => {
return; return;
} }
globalStore.currentNode = command || 'local'; globalStore.currentNode = command || 'local';
globalStore.currentNodeAddr = item.addr;
globalStore.isOffline = item.isOffline; globalStore.isOffline = item.isOffline;
router.push({ name: 'home' }).then(() => { router.push({ name: 'home' }).then(() => {
window.location.reload(); window.location.reload();

View file

@ -44,6 +44,7 @@ export interface GlobalState {
errStatus: string; errStatus: string;
currentNode: string; currentNode: string;
currentNodeAddr: string;
isOffline: boolean; isOffline: boolean;
} }

View file

@ -47,6 +47,7 @@ const GlobalStore = defineStore({
errStatus: '', errStatus: '',
currentNode: 'local', currentNode: 'local',
currentNodeAddr: '',
isOffline: false, isOffline: false,
}), }),
getters: { getters: {

View file

@ -3,7 +3,7 @@
<docker-status v-model:isActive="isActive" v-model:isExist="isExist" @search="search" /> <docker-status v-model:isActive="isActive" v-model:isExist="isExist" @search="search" />
<LayoutContent :title="$t('menu.container')" v-if="isExist" :class="{ mask: !isActive }"> <LayoutContent :title="$t('menu.container')" v-if="isExist" :class="{ mask: !isActive }">
<template #search> <template #search v-if="tags.length !== 0">
<div class="card-interval" v-if="isExist && isActive"> <div class="card-interval" v-if="isExist && isActive">
<div v-for="item in tags" :key="item.key" class="inline"> <div v-for="item in tags" :key="item.key" class="inline">
<el-button <el-button
@ -492,14 +492,30 @@ const searchWithAppShow = (item: any) => {
const loadContainerCount = async () => { const loadContainerCount = async () => {
await loadContainerStatus().then((res) => { await loadContainerStatus().then((res) => {
tags.value = []; tags.value = [];
tags.value.push({ key: 'all', count: res.data.all }); if (res.data.all) {
tags.value.push({ key: 'running', count: res.data.running }); tags.value.push({ key: 'all', count: res.data.all });
tags.value.push({ key: 'paused', count: res.data.paused }); }
tags.value.push({ key: 'restarting', count: res.data.restarting }); if (res.data.all) {
tags.value.push({ key: 'removing', count: res.data.removing }); tags.value.push({ key: 'running', count: res.data.running });
tags.value.push({ key: 'created', count: res.data.created }); }
tags.value.push({ key: 'dead', count: res.data.dead }); if (res.data.all) {
tags.value.push({ key: 'exited', count: res.data.exited }); tags.value.push({ key: 'paused', count: res.data.paused });
}
if (res.data.all) {
tags.value.push({ key: 'restarting', count: res.data.restarting });
}
if (res.data.all) {
tags.value.push({ key: 'removing', count: res.data.removing });
}
if (res.data.all) {
tags.value.push({ key: 'created', count: res.data.created });
}
if (res.data.all) {
tags.value.push({ key: 'dead', count: res.data.dead });
}
if (res.data.all) {
tags.value.push({ key: 'exited', count: res.data.exited });
}
}); });
}; };

View file

@ -181,12 +181,8 @@ const loadAccess = async () => {
}; };
const loadSystemIP = async () => { const loadSystemIP = async () => {
if (globalStore.currentNode !== 'local') {
form.systemIP = globalStore.currentNode || i18n.global.t('database.localIP');
return;
}
const res = await getAgentSettingInfo(); const res = await getAgentSettingInfo();
form.systemIP = res.data.systemIP || i18n.global.t('database.localIP'); form.systemIP = res.data.systemIP || globalStore.currentNodeAddr || i18n.global.t('database.localIP');
}; };
const loadPassword = async () => { const loadPassword = async () => {

View file

@ -181,12 +181,8 @@ const loadAccess = async () => {
}; };
const loadSystemIP = async () => { const loadSystemIP = async () => {
if (globalStore.currentNode !== 'local') {
form.systemIP = globalStore.currentNode || i18n.global.t('database.localIP');
return;
}
const res = await getAgentSettingInfo(); const res = await getAgentSettingInfo();
form.systemIP = res.data.systemIP || i18n.global.t('database.localIP'); form.systemIP = res.data.systemIP || globalStore.currentNodeAddr || i18n.global.t('database.localIP');
}; };
const loadPassword = async () => { const loadPassword = async () => {

View file

@ -183,12 +183,8 @@ const loadPassword = async () => {
}; };
const loadSystemIP = async () => { const loadSystemIP = async () => {
if (globalStore.currentNode !== 'local') {
form.systemIP = globalStore.currentNode || i18n.global.t('database.localIP');
return;
}
const res = await getAgentSettingInfo(); const res = await getAgentSettingInfo();
form.systemIP = res.data.systemIP || i18n.global.t('database.localIP'); form.systemIP = res.data.systemIP || globalStore.currentNodeAddr || i18n.global.t('database.localIP');
}; };
function loadRedisInfo(isContainer: boolean) { function loadRedisInfo(isContainer: boolean) {

View file

@ -37,9 +37,7 @@
</div> </div>
<div class="h-app-desc"> <div class="h-app-desc">
<span> <span>
{{ {{ app.description }}
language == 'zh' || language == 'tw' ? app.shortDescZh : app.shortDescEn
}}
</span> </span>
</div> </div>
</div> </div>
@ -183,12 +181,10 @@ import { MsgSuccess } from '@/utils/message';
import { ref } from 'vue'; import { ref } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { toFolder } from '@/global/business'; import { toFolder } from '@/global/business';
import { getLanguage } from '@/utils/util';
const router = useRouter(); const router = useRouter();
const globalStore = GlobalStore(); const globalStore = GlobalStore();
const language = getLanguage();
let loading = ref(false); let loading = ref(false);
let apps = ref([]); let apps = ref([]);
const options = ref([]); const options = ref([]);

View file

@ -12,6 +12,19 @@
<el-option v-for="item in freeNodes" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in freeNodes" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-card class="mt-5" v-if="form.nodeID && form.nodeID !== 0">
<div class="mb-2">
<span>{{ $t('xpack.node.syncInfo') }}</span>
</div>
<el-form-item prop="syncListItem">
<el-checkbox-group v-model="form.syncListItem">
<el-checkbox :label="$t('xpack.node.syncProxy')" value="SyncSystemProxy" />
<el-checkbox :label="$t('xpack.node.syncAlertSetting')" value="SyncAlertSetting" />
<el-checkbox :label="$t('xpack.node.syncCustomApp')" value="SyncCustomApp" />
</el-checkbox-group>
<span class="input-help">{{ $t('xpack.node.syncHelper') }}</span>
</el-form-item>
</el-card>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button> <el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
@ -43,6 +56,8 @@ const freeNodes = ref([]);
const form = reactive({ const form = reactive({
nodeID: null, nodeID: null,
licenseID: null, licenseID: null,
syncList: '',
syncListItem: ['SyncSystemProxy', 'SyncAlertSetting', 'SyncCustomApp'],
}); });
const formRef = ref<FormInstance>(); const formRef = ref<FormInstance>();
@ -59,6 +74,7 @@ const onBind = async (formEl: FormInstance | undefined) => {
formEl.validate(async (valid) => { formEl.validate(async (valid) => {
if (!valid) return; if (!valid) return;
loading.value = true; loading.value = true;
form.syncList = form.syncListItem?.join(',') || '';
await bindLicense(form.licenseID, form.nodeID) await bindLicense(form.licenseID, form.nodeID)
.then(() => { .then(() => {
loading.value = false; loading.value = false;

View file

@ -255,7 +255,7 @@ const buttons = [
{ {
label: i18n.global.t('commons.button.edit'), label: i18n.global.t('commons.button.edit'),
click: (row: any) => { click: (row: any) => {
licenseRef.value.acceptParams({ oldLicense: row.licenseName }); licenseRef.value.acceptParams({ oldLicense: row.licenseName, isImport: true });
}, },
}, },
{ {