fix: Modify the information about the upper limit of free nodes (#8070)

This commit is contained in:
ssongliu
2025-03-05 11:18:57 +00:00
committed by GitHub
parent 80e3693c5f
commit d3efd02d69
26 changed files with 108 additions and 30 deletions
+8 -1
View File
@@ -103,8 +103,15 @@ func (u *CronjobService) LoadNextHandle(specStr string) ([]string, error) {
if strings.HasSuffix(specStr, "s") {
duration = time.Second
}
interval := strings.ReplaceAll(specStr, "@every ", "")
interval = strings.ReplaceAll(interval, "s", "")
interval = strings.ReplaceAll(interval, "m", "")
durationItem, err := strconv.Atoi(interval)
if err != nil {
return nil, err
}
for i := 0; i < 5; i++ {
nextTime := now.Add(duration)
nextTime := now.Add(time.Duration(durationItem) * duration)
nexts[i] = nextTime.Format(constant.DateTimeLayout)
now = nextTime
}
+11 -6
View File
@@ -71,11 +71,13 @@ func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate, isCron bool) er
return err
}
if !isCron {
go handleSnapshot(req, taskItem)
go func() {
_ = handleSnapshot(req, taskItem)
}()
return nil
}
handleSnapshot(req, taskItem)
return nil
return handleSnapshot(req, taskItem)
}
func (u *SnapshotService) SnapshotReCreate(id uint) error {
@@ -105,12 +107,14 @@ func (u *SnapshotService) SnapshotReCreate(id uint) error {
global.LOG.Errorf("new task for create snapshot failed, err: %v", err)
return err
}
go handleSnapshot(req, taskItem)
go func() {
_ = handleSnapshot(req, taskItem)
}()
return nil
}
func handleSnapshot(req dto.SnapshotCreate, taskItem *task.Task) {
func handleSnapshot(req dto.SnapshotCreate, taskItem *task.Task) error {
rootDir := path.Join(global.Dir.TmpDir, "system", req.Name)
itemHelper := snapHelper{SnapID: req.ID, Task: *taskItem, FileOp: files.NewFileOp(), Ctx: context.Background()}
baseDir := path.Join(rootDir, "base")
@@ -185,10 +189,11 @@ func handleSnapshot(req dto.SnapshotCreate, taskItem *task.Task) {
}
if err := taskItem.Execute(); err != nil {
_ = snapshotRepo.Update(req.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error(), "interrupt_step": taskItem.Task.CurrentStep})
return
return err
}
_ = snapshotRepo.Update(req.ID, map[string]interface{}{"status": constant.StatusSuccess, "interrupt_step": ""})
_ = os.RemoveAll(rootDir)
return nil
}
type snapHelper struct {
+2 -2
View File
@@ -746,7 +746,7 @@ func (f FileOp) TarGzCompressPro(withDir bool, src, dst, secret, exclusionRules
itemPrefix = ""
}
if len(secret) != 0 {
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read --exclude-from=<(find %s -type s -print '%s' | sed 's|^|%s/|') -zcf - %s | openssl enc -aes-256-cbc -salt -pbkdf2 -k '%s' -out %s", src, "%P\n", itemPrefix, srcItem, secret, dst)
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read --exclude-from=<(find %s -type s -printf '%s' | sed 's|^|%s/|') -zcf - %s | openssl enc -aes-256-cbc -salt -k '%s' -out %s", src, "%P\n", itemPrefix, srcItem, secret, dst)
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" %s ", secret), "******"))
} else {
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read --exclude-from=<(find %s -type s -printf '%s' | sed 's|^|%s/|') -zcf %s %s %s", src, "%P\n", itemPrefix, dst, exStr, srcItem)
@@ -764,7 +764,7 @@ func (f FileOp) TarGzExtractPro(src, dst string, secret string) error {
commands := ""
if len(secret) != 0 {
commands = fmt.Sprintf("openssl enc -d -aes-256-cbc -salt -pbkdf2 -k '%s' -in %s | tar -zxf - > /root/log", secret, src)
commands = fmt.Sprintf("openssl enc -d -aes-256-cbc -salt -k '%s' -in %s | tar -zxf - > /root/log", secret, src)
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" %s ", secret), "******"))
} else {
commands = fmt.Sprintf("tar zxvf %s", src)
+1
View File
@@ -57,6 +57,7 @@ ErrXpackTimeout: "Request timeout, network connection might be unstable, please
ErrUnbindMaster: "Detected nodes in node management, unable to unbind the current license, please remove nodes first and retry!"
ErrFreeNodeLimit: "Community version node limit reached, please go to www.lxware.cn/1panel to purchase and retry!"
ErrNodeBound: "This license is bound to another node, please check and retry!"
ErrNodeBoundLimit: "The current free node has reached its limit, please check and try again!"
ErrNoSuchNode: "Node information not found, please check and retry!"
ErrNodeUnbind: "This node is not within the license binding range, please check and retry!"
ErrNodeBind: "This node is already bound to a license, please check and retry!"
+1
View File
@@ -58,6 +58,7 @@ ErrXpackTimeout: "リクエストタイムアウト、ネットワーク接続
ErrUnbindMaster: "ノード管理にノードが存在するため、現在のライセンスを解除できません。ノードを削除してから再試行してください!"
ErrFreeNodeLimit: "コミュニティ版のノード数が無料上限に達しました、www.lxware.cn/1panel で購入して再試行してください!"
ErrNodeBound: "このライセンスは他のノードにバインドされています、確認して再試行してください!"
ErrNodeBoundLimit: "現在の無料ノードは上限に達しました。確認して再試行してください!"
ErrNoSuchNode: "そのノード情報が見つかりませんでした、確認して再試行してください!"
ErrNodeUnbind: "そのノードはライセンスのバインド範囲内ではありません、確認して再試行してください!"
ErrNodeBind: "そのノードはライセンスにバインドされています、確認して再試行してください!"
+1
View File
@@ -57,6 +57,7 @@ ErrXpackTimeout: "요청 시간 초과, 네트워크 연결이 불안정할 수
ErrUnbindMaster: "노드 관리 내에 노드가 존재함을 감지하였습니다. 현재 라이선스를 해제할 수 없습니다. 먼저 제거 후 다시 시도해 주세요!"
ErrFreeNodeLimit: "커뮤니티 에디션 노드 수가 무료 한도에 도달하였습니다. www.lxware.cn/1panel 에서 구매 후 다시 시도해 주세요!"
ErrNodeBound: "이 라이선스는 다른 노드에 바인딩되어 있습니다. 다시 확인하고 시도해 주세요!"
ErrNodeBoundLimit: "현재 무료 노드가 한도에 도달했습니다. 확인 후 다시 시도해 주세요!"
ErrNoSuchNode: "노드 정보를 찾을 수 없습니다. 다시 확인하고 시도해 주세요!"
ErrNodeUnbind: "이 노드가 라이선스 바인딩 범위에 있지 않음을 감지하였습니다. 다시 확인하고 시도해 주세요!"
ErrNodeBind: "이 노드가 이미 라이선스에 바인딩되어 있음을 감지하였습니다. 다시 확인하고 시도해 주세요!"
+1
View File
@@ -57,6 +57,7 @@ ErrXpackTimeout: "Permintaan tamat masa, sambungan rangkaian mungkin tidak stabi
ErrUnbindMaster: "Terdapat nod dalam pengurusan nod, sila keluarkan dahulu dan cuba lagi!"
ErrFreeNodeLimit: "Jumlah nod versi komuniti telah mencapai had percuma, sila lawati www.lxware.cn/1panel untuk pembelian!"
ErrNodeBound: "Lesen ini telah diikat dengan nod lain, sila semak dan cuba lagi!"
ErrNodeBoundLimit: "Nod percuma semasa telah mencapai had, sila semak dan cuba lagi!"
ErrNoSuchNode: "Maklumat nod tidak ditemui, sila semak dan cuba lagi!"
ErrNodeUnbind: "Nod di luar skop lesen dikesan, sila semak dan cuba lagi!"
ErrNodeBind: "Nod ini telah diikat dengan lesen, sila semak dan cuba lagi!"
+1
View File
@@ -57,6 +57,7 @@ ErrXpackTimeout: "Requisição expirou, a conexão de rede pode estar instável,
ErrUnbindMaster: "Foram encontrados nós na gestão de nós, remova-os primeiro e tente novamente!"
ErrFreeNodeLimit: "O número de nós da versão comunitária atingiu o limite gratuito, por favor acesse www.lxware.cn/1panel para comprar!"
ErrNodeBound: "Esta licença já está vinculada a outro nó, por favor verifique e tente novamente!"
ErrNodeBoundLimit: "O nó gratuito atual atingiu seu limite, por favor verifique e tente novamente!"
ErrNoSuchNode: "As informações deste nó não foram encontradas, por favor verifique e tente novamente!"
ErrNodeUnbind: "Nó fora do escopo da licença detectado, por favor verifique e tente novamente!"
ErrNodeBind: "Este nó já está vinculado a uma licença, por favor verifique e tente novamente!"
+1
View File
@@ -57,6 +57,7 @@ ErrXpackTimeout: "Время ожидания запроса истекло, в
ErrUnbindMaster: "В управлении узлами обнаружены узлы, сначала удалите их и повторите попытку!"
ErrFreeNodeLimit: "Количество узлов в версии сообщества достигло бесплатного лимита, посетите www.lxware.cn/1panel для покупки!"
ErrNodeBound: "Эта лицензия уже связана с другим узлом, проверьте и повторите попытку!"
ErrNodeBoundLimit: "Текущий бесплатный узел достиг предела, пожалуйста, проверьте и попробуйте снова!"
ErrNoSuchNode: "Информация об узле не найдена, проверьте и повторите попытку!"
ErrNodeUnbind: "Обнаружен узел вне области действия лицензии, проверьте и повторите попытку!"
ErrNodeBind: "Этот узел уже связан с лицензией, проверьте и повторите попытку!"
+1
View File
@@ -57,6 +57,7 @@ ErrXpackTimeout: "請求超時,網絡連接可能不穩定,請稍後再試
ErrUnbindMaster: "檢測到節點管理內存在節點,無法解綁當前許可證,請先移除後重試!"
ErrFreeNodeLimit: "社區版節點數量已達免費上限,請前往 www.lxware.cn/1panel 購買後重試!"
ErrNodeBound: "該許可證已綁定到其他節點,請檢查後重試!"
ErrNodeBoundLimit: "當前免費節點已達上限,請檢查後重試!"
ErrNoSuchNode: "未能找到該節點信息,請檢查後重試!"
ErrNodeUnbind: "檢測到該節點未在許可證綁定範圍內,請檢查後重試!"
ErrNodeBind: "檢測到該節點已綁定許可證,請檢查後重試!"
+1
View File
@@ -57,6 +57,7 @@ ErrXpackTimeout: "请求超时,网络连接可能不稳定,请稍后再试
ErrUnbindMaster: "检测到节点管理内存在节点,无法解绑当前许可证,请先移除后重试!"
ErrFreeNodeLimit: "社区版节点数量已达到免费上限,请前往 www.lxware.cn/1panel 购买后重试!"
ErrNodeBound: "该许可证已绑定到其他节点,请检查后重试!"
ErrNodeBoundLimit: "当前免费节点已经达到上限,请检查后重试!"
ErrNoSuchNode: "未能找到该节点信息,请检查后重试!"
ErrNodeUnbind: "检测到该节点未在许可证绑定范围内,请检查后重试!"
ErrNodeBind: "检测到该节点已绑定许可证,请检查后重试!"
+21 -7
View File
@@ -105,7 +105,7 @@
</DrawerPro>
<DialogPro
v-model="openRecover"
v-model="recoverDialog"
:title="$t('commons.button.recover') + ' - ' + name"
@close="handleRecoverClose"
>
@@ -159,7 +159,7 @@ const uploadPercent = ref<number>(0);
const selects = ref<any>([]);
const baseDir = ref();
const opRef = ref();
const openRecover = ref();
const currentRow = ref();
const data = ref();
const title = ref();
const paginationConfig = reactive({
@@ -173,9 +173,10 @@ const name = ref();
const detailName = ref();
const remark = ref();
const secret = ref();
const fileName = ref();
const taskLogRef = ref();
const recoverDialog = ref();
const acceptParams = async (params: DialogProps): Promise<void> => {
type.value = params.type;
name.value = params.name;
@@ -227,7 +228,7 @@ const onHandleRecover = async () => {
type: type.value,
name: name.value,
detailName: detailName.value,
file: baseDir.value + fileName.value,
file: baseDir.value + currentRow.value.name,
secret: secret.value,
taskID: newUUID(),
};
@@ -247,8 +248,21 @@ const onHandleRecover = async () => {
};
const onRecover = async (row: File.File) => {
fileName.value = row.name;
openRecover.value = true;
currentRow.value = row;
if (type.value !== 'app' && type.value !== 'website') {
ElMessageBox.confirm(
i18n.global.t('commons.msg.recoverHelper', [row.name]),
i18n.global.t('commons.button.recover'),
{
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
},
).then(async () => {
onHandleRecover();
});
return;
}
recoverDialog.value = true;
};
const uploaderFiles = ref<UploadFiles>([]);
@@ -284,7 +298,7 @@ const handleUploadClose = () => {
};
const handleRecoverClose = () => {
openRecover.value = false;
recoverDialog.value = false;
};
const handleExceed: UploadProps['onExceed'] = (files) => {
+6
View File
@@ -3180,8 +3180,14 @@ const message = {
alertTitle3: 'Alert Settings',
alertContent3:
'Provide users with custom phone numbers, daily push frequency, and daily push time configurations, making it easier for users to set up more reasonable push alerts.',
nodeTitle1: 'Multi-Node Management',
nodeContent1: 'Manage different nodes',
nodeTitle2: 'Bulk Upgrade',
nodeContent2: 'Bulk upgrade multiple nodes',
},
node: {
master: 'Main Node',
node: 'Node',
addr: 'Address',
memTotal: 'Total Memory',
+6
View File
@@ -2992,8 +2992,14 @@ const message = {
alertTitle3: 'アラート設定',
alertContent3:
'カスタム電話番号日々のプッシュ頻度日々のプッシュ時間設定を提供しユーザーがより合理的なプッシュアラートを設定しやすく',
nodeTitle1: 'マルチノード管理',
nodeContent1: '異なるノードを管理する',
nodeTitle2: 'バルクアップグレード',
nodeContent2: '複数のノードを一括アップグレード',
},
node: {
master: '主ノード',
node: 'ノード',
addr: 'アドレス',
memTotal: '総メモリ',
+6
View File
@@ -2941,8 +2941,14 @@ const message = {
alertTitle3: '알림 설정',
alertContent3:
'사용자에게 전화번호, 일일 푸시 빈도, 일일 푸시 시간 등을 사용자 정의할 있는 설정을 제공하여 보다 합리적인 푸시 알림을 설정할 있도록 합니다.',
nodeTitle1: '다중 노드 관리',
nodeContent1: '다양한 노드 관리',
nodeTitle2: '대량 업그레이드',
nodeContent2: '여러 노드를 대량 업그레이드',
},
node: {
master: ' 노드',
node: '노드',
addr: '주소',
memTotal: ' 메모리',
+6
View File
@@ -3060,8 +3060,14 @@ const message = {
alertTitle3: 'Tetapan Amaran',
alertContent3:
'Memberikan pengguna konfigurasi nombor telefon tersuai, kekerapan push harian, dan masa push harian, memudahkan pengguna untuk menetapkan amaran push yang lebih munasabah.',
nodeTitle1: 'Pengurusan Multi-Nod',
nodeContent1: 'Mengurus nod yang berbeza',
nodeTitle2: 'Peningkatan Pukal',
nodeContent2: 'Peningkatan secara pukal untuk beberapa nod',
},
node: {
master: 'Nod Utama',
node: 'Nod',
addr: 'Alamat',
memTotal: 'Jumlah Memori',
+6
View File
@@ -3064,8 +3064,14 @@ const message = {
alertTitle3: 'Configurações de Alertas',
alertContent3:
'Permite que os usuários configurem números de telefone personalizados, frequência de envio diário e horários de envio, facilitando a criação de alertas mais adequados.',
nodeTitle1: 'Gerenciamento de Múltiplos Nós',
nodeContent1: 'Gerenciar diferentes nós',
nodeTitle2: 'Atualização em Lote',
nodeContent2: 'Atualizar vários nós em lote',
},
node: {
master: ' Principal',
node: '',
addr: 'Endereço',
memTotal: 'Memória Total',
+6
View File
@@ -3053,8 +3053,14 @@ const message = {
alertTitle3: 'Настройки Уведомлений',
alertContent3:
'Предоставляет пользователям возможность настройки номеров телефонов, частоты и времени отправки уведомлений в день, что позволяет настраивать более удобные уведомления.',
nodeTitle1: 'Управление несколькими узлами',
nodeContent1: 'Управление различными узлами',
nodeTitle2: 'Массовое обновление',
nodeContent2: 'Массовое обновление нескольких узлов',
},
node: {
master: 'Главный узел',
node: 'Узел',
addr: 'Адрес',
memTotal: 'Общая память',
+6
View File
@@ -2944,8 +2944,14 @@ const message = {
alertTitle3: '告警設定',
alertContent3:
'為用戶提供自訂手機號每日推送次數每日推送時間的配寘方便用戶的設定來更加合理的進行推送告警',
nodeTitle1: '多節點管理',
nodeContent1: '管理不同的節點',
nodeTitle2: '批量升級',
nodeContent2: '多個節點批量升級',
},
node: {
master: '主節點',
node: '節點',
addr: '地址',
memTotal: '記憶體總計',
+1
View File
@@ -2932,6 +2932,7 @@ const message = {
nodeContent2: '多个节点批量升级',
},
node: {
master: '主节点',
node: '节点',
addr: '地址',
memTotal: '内存总计',
@@ -27,7 +27,7 @@
<el-dropdown-menu>
<el-dropdown-item command="local">
<el-button link icon="CircleCheck" type="success" />
{{ $t('terminal.local') }}
{{ $t('xpack.node.master') }}
</el-dropdown-item>
<el-dropdown-item
:disabled="item.status !== 'Healthy'"
@@ -116,9 +116,9 @@ const openChangeNode = () => {
const loadCurrentName = () => {
if (globalStore.currentNode) {
return globalStore.currentNode === 'local' ? i18n.global.t('terminal.local') : globalStore.currentNode;
return globalStore.currentNode === 'local' ? i18n.global.t('xpack.node.master') : globalStore.currentNode;
}
return i18n.global.t('terminal.local');
return i18n.global.t('xpack.node.master');
};
const screenWidth = ref(0);
@@ -462,7 +462,7 @@
</div>
<div v-if="!globalStore.isIntl">
<el-form-item prop="hasAlert" v-if="alertTypes.includes(dialogData.rowData!.type)">
<el-form-item prop="hasAlert">
<el-checkbox v-model="dialogData.rowData!.hasAlert" :label="$t('xpack.alert.isAlert')" />
<span class="input-help">{{ $t('xpack.alert.cronJobHelper') }}</span>
</el-form-item>
@@ -561,7 +561,6 @@ const router = useRouter();
const globalStore = GlobalStore();
const licenseRef = ref();
const { isProductPro } = storeToRefs(globalStore);
const alertTypes = ['app', 'website', 'database', 'directory', 'log', 'snapshot'];
interface DialogProps {
title: string;
@@ -612,6 +611,8 @@ const acceptParams = (params: DialogProps): void => {
dialogData.value.rowData.dbType = 'mysql';
dialogData.value.rowData.isDir = true;
}
dialogData.value.rowData.hasAlert = dialogData.value.rowData!.alertCount > 0;
dialogData.value.rowData!.alertCount = dialogData.value.rowData!.alertCount || 3;
dialogData.value.rowData!.command = dialogData.value.rowData!.command || 'sh';
dialogData.value.rowData!.isCustom =
dialogData.value.rowData!.command !== 'sh' &&
@@ -1052,7 +1053,7 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
}
dialogData.value.rowData.alertCount =
dialogData.value.rowData!.hasAlert && isProductPro.value ? dialogData.value.rowData.alertCount : 0;
dialogData.value.rowData!.hasAlert && isProductPro.value ? dialogData.value.rowData.alertCount : 3;
dialogData.value.rowData.alertTitle =
dialogData.value.rowData!.hasAlert && isProductPro.value
? i18n.global.t('cronjob.alertTitle', [
@@ -173,11 +173,11 @@
</div>
</el-row>
<el-row v-if="currentRecord?.taskID">
<TaskLog
<LogFile
:defaultButton="false"
class="w-full"
:taskID="currentRecord?.taskID"
:key="currentRecord?.taskID"
:heightDiff="200"
:heightDiff="410"
:config="{ type: 'task', taskID: currentRecord?.taskID, tail: true }"
/>
</el-row>
</el-form>
@@ -223,13 +223,13 @@ import { nextTick, onBeforeUnmount, reactive, ref } from 'vue';
import { Cronjob } from '@/api/interface/cronjob';
import { searchRecords, handleOnce, updateStatus, cleanRecords, getRecordLog } from '@/api/modules/cronjob';
import { dateFormat } from '@/utils/util';
import LogFile from '@/components/log/file/index.vue';
import i18n from '@/lang';
import { ElMessageBox } from 'element-plus';
import { MsgSuccess } from '@/utils/message';
import { listDbItems } from '@/api/modules/database';
import { listAppInstalled } from '@/api/modules/app';
import { shortcuts } from '@/utils/shortcuts';
import TaskLog from '@/components/log/task/index.vue';
const loading = ref();
const refresh = ref(false);
@@ -74,7 +74,7 @@ const onBind = async (formEl: FormInstance | undefined) => {
const loadNodes = async () => {
if (!globalStore.isMasterProductPro) {
freeNodes.value = [{ id: 0, name: i18n.global.t('terminal.local') }];
freeNodes.value = [{ id: 0, name: i18n.global.t('xpack.node.master') }];
return;
}
await listNodeOptions()
+1 -1
View File
@@ -171,7 +171,7 @@ import ClamStatus from '@/views/toolbox/clam/status/index.vue';
import SettingDialog from '@/views/toolbox/clam/setting/index.vue';
import { Toolbox } from '@/api/interface/toolbox';
import router from '@/routers';
import { transSpecToStr } from '../../cronjob/helper';
import { transSpecToStr } from '@/views/cronjob/cronjob/helper';
import { GlobalStore } from '@/store';
import { storeToRefs } from 'pinia';
@@ -181,9 +181,9 @@ import LicenseImport from '@/components/license-import/index.vue';
import { MsgError, MsgSuccess } from '@/utils/message';
import { Toolbox } from '@/api/interface/toolbox';
import { createClam, updateClam } from '@/api/modules/toolbox';
import { specOptions, transObjToSpec, transSpecToObj, weekOptions } from '../../../cronjob/helper';
import { storeToRefs } from 'pinia';
import { GlobalStore } from '@/store';
import { specOptions, transObjToSpec, transSpecToObj, weekOptions } from '@/views/cronjob/cronjob/helper';
const globalStore = GlobalStore();
const licenseRef = ref();