mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/1Panel-dev/1Panel.git
synced 2026-09-20 08:03:55 +08:00
feat: add Community Edition restore UI (#13431)
This commit is contained in:
@@ -28,10 +28,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
rollbackPath = "1panel/tmp"
|
||||
upgradePath = "1panel/tmp/upgrade"
|
||||
uploadPath = "1panel/uploads"
|
||||
downloadPath = "1panel/download"
|
||||
rollbackPath = "1panel/tmp"
|
||||
communityRestorePath = "1panel/tmp/community-restore"
|
||||
upgradePath = "1panel/tmp/upgrade"
|
||||
uploadPath = "1panel/uploads"
|
||||
downloadPath = "1panel/download"
|
||||
)
|
||||
|
||||
func (u *DeviceService) Scan() dto.CleanData {
|
||||
@@ -113,12 +114,15 @@ func (u *DeviceService) Clean(req []dto.Clean) {
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, rollbackPath, "app"))
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, rollbackPath, "database"))
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, rollbackPath, "website"))
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, communityRestorePath))
|
||||
case "rollback_app":
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, rollbackPath, "app", item.Name))
|
||||
case "rollback_database":
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, rollbackPath, "database", item.Name))
|
||||
case "rollback_website":
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, rollbackPath, "website", item.Name))
|
||||
case "rollback_community_restore":
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, communityRestorePath, item.Name))
|
||||
|
||||
case "upload":
|
||||
dropFileOrDir(path.Join(global.Dir.BaseDir, uploadPath, item.Name))
|
||||
@@ -214,6 +218,7 @@ func doSystemClean(taskItem *task.Task) func(t *task.Task) error {
|
||||
dropWithTask(path.Join(global.Dir.BaseDir, rollbackPath, "app"), taskItem, &size, &fileCount)
|
||||
dropWithTask(path.Join(global.Dir.BaseDir, rollbackPath, "website"), taskItem, &size, &fileCount)
|
||||
dropWithTask(path.Join(global.Dir.BaseDir, rollbackPath, "database"), taskItem, &size, &fileCount)
|
||||
dropWithTask(path.Join(global.Dir.BaseDir, communityRestorePath), taskItem, &size, &fileCount)
|
||||
|
||||
upgrades := path.Join(global.Dir.BaseDir, upgradePath)
|
||||
oldUpgradeFiles, _ := os.ReadDir(upgrades)
|
||||
@@ -611,6 +616,7 @@ func loadRollBackTree(fileOp fileUtils.FileOp) []dto.CleanTree {
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, rollbackPath, "app"), "rollback_app", fileOp)
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, rollbackPath, "website"), "rollback_website", fileOp)
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, rollbackPath, "database"), "rollback_database", fileOp)
|
||||
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, communityRestorePath), "rollback_community_restore", fileOp)
|
||||
|
||||
return treeData
|
||||
}
|
||||
|
||||
@@ -307,6 +307,13 @@ export namespace Setting {
|
||||
status: string;
|
||||
message: string;
|
||||
}
|
||||
export interface CommunityRestoreStatus {
|
||||
state: 'Ready' | 'Running' | 'Failed';
|
||||
message: string;
|
||||
packageExist: boolean;
|
||||
packageDirectory: string;
|
||||
}
|
||||
export type CommunityRestoreMode = 'online' | 'offline';
|
||||
export interface NodeItem {
|
||||
id: number;
|
||||
groupID?: number;
|
||||
|
||||
@@ -65,6 +65,12 @@ export const getEnterpriseLicense = () => {
|
||||
export const getEnterpriseLicenseStatus = () => {
|
||||
return http.get<Setting.LicenseStatus>(`/core/enterprise/licenses/status`);
|
||||
};
|
||||
export const getCommunityRestoreStatus = () => {
|
||||
return http.get<Setting.CommunityRestoreStatus>(`/core/enterprise/licenses/community-restore/status`);
|
||||
};
|
||||
export const restoreCommunity = (mode: Setting.CommunityRestoreMode) => {
|
||||
return http.post(`/core/enterprise/licenses/community-restore`, { mode });
|
||||
};
|
||||
|
||||
// agent
|
||||
export const loadBaseDir = (node?: string) => {
|
||||
|
||||
@@ -3190,6 +3190,20 @@ const message = {
|
||||
'Please copy the device ID first to obtain a license. You can log in after importing the license.',
|
||||
licenseRequiredUserTip:
|
||||
'The Enterprise Edition license is not bound. Contact a super administrator to import the license.',
|
||||
restoreCommunity: 'Restore Community Edition',
|
||||
restoreCommunityOnline: 'Online restore',
|
||||
restoreCommunityOffline: 'Offline restore',
|
||||
restoreCommunityPackageFound:
|
||||
'A usable offline Community Edition package was detected. Offline restore is available.',
|
||||
restoreCommunityPackageMissing:
|
||||
'No usable offline Community Edition package was detected. Offline restore is currently unavailable.',
|
||||
restoreCommunityOnlineHelper:
|
||||
'The Community Edition package for the current version will be downloaded automatically, and Enterprise-only data will be cleaned up.',
|
||||
restoreCommunityOfflineHelper:
|
||||
'Place an official package no older than the current Enterprise version in {path}. Switching is enabled after a usable package is detected.',
|
||||
restoreCommunityConfirm:
|
||||
'After switching, you can sign in to Community Edition with the current Enterprise super administrator account. Enterprise-only databases will be deleted during the switch.',
|
||||
restoreCommunityStarting: 'Preparing Community Edition. The panel will restart automatically when ready.',
|
||||
levelUpPro: 'Upgrade to Commercial Edition',
|
||||
licenseSync: 'License Sync',
|
||||
knowMorePro: 'Learn More',
|
||||
|
||||
@@ -3254,6 +3254,21 @@ const message = {
|
||||
'Primero copie el ID del dispositivo para obtener una licencia. Podrá iniciar sesión después de importarla.',
|
||||
licenseRequiredUserTip:
|
||||
'La licencia Enterprise Edition no está vinculada. Contacte con un superadministrador para importar la licencia.',
|
||||
restoreCommunity: 'Restaurar Community Edition',
|
||||
restoreCommunityOnline: 'Restauración en línea',
|
||||
restoreCommunityOffline: 'Restauración sin conexión',
|
||||
restoreCommunityPackageFound:
|
||||
'Se detectó un paquete sin conexión de Community Edition válido. La restauración sin conexión está disponible.',
|
||||
restoreCommunityPackageMissing:
|
||||
'No se detectó ningún paquete sin conexión de Community Edition válido. La restauración sin conexión no está disponible actualmente.',
|
||||
restoreCommunityOnlineHelper:
|
||||
'Se descargará automáticamente el paquete Community Edition de la versión actual y se limpiarán los datos exclusivos de Enterprise.',
|
||||
restoreCommunityOfflineHelper:
|
||||
'Coloque en {path} un paquete oficial cuya versión no sea anterior a la versión Enterprise actual. El cambio se habilitará al detectar un paquete utilizable.',
|
||||
restoreCommunityConfirm:
|
||||
'Después del cambio, podrá iniciar sesión en Community Edition con la cuenta actual de superadministrador de Enterprise. Las bases de datos exclusivas de Enterprise se eliminarán durante el cambio.',
|
||||
restoreCommunityStarting:
|
||||
'Preparando Community Edition. El panel se reiniciará automáticamente cuando termine.',
|
||||
levelUpPro: 'Actualizar a la edición comercial',
|
||||
licenseSync: 'Sincronizar licencia',
|
||||
knowMorePro: 'Ver más',
|
||||
|
||||
@@ -3162,6 +3162,21 @@ const message = {
|
||||
licenseRequiredShortTip:
|
||||
'لطفاً ابتدا شناسه دستگاه را کپی کنید تا مجوز دریافت کنید. پس از وارد کردن مجوز میتوانید وارد شوید.',
|
||||
licenseRequiredUserTip: 'مجوز نسخه سازمانی متصل نیست. برای وارد کردن مجوز با ابرمدیر تماس بگیرید.',
|
||||
restoreCommunity: 'بازیابی نسخه Community',
|
||||
restoreCommunityOnline: 'بازیابی آنلاین',
|
||||
restoreCommunityOffline: 'بازیابی آفلاین',
|
||||
restoreCommunityPackageFound:
|
||||
'یک بسته آفلاین قابل استفاده Community Edition شناسایی شد. بازیابی آفلاین در دسترس است.',
|
||||
restoreCommunityPackageMissing:
|
||||
'هیچ بسته آفلاین قابل استفاده Community Edition شناسایی نشد. بازیابی آفلاین در حال حاضر در دسترس نیست.',
|
||||
restoreCommunityOnlineHelper:
|
||||
'بسته نسخه Community برای نسخه فعلی بهطور خودکار دانلود و دادههای ویژه Enterprise پاکسازی میشوند.',
|
||||
restoreCommunityOfflineHelper:
|
||||
'یک بسته رسمی با نسخهای قدیمیتر نبودن از نسخه فعلی Enterprise را در {path} قرار دهید. پس از شناسایی بسته قابل استفاده، تغییر نسخه فعال میشود.',
|
||||
restoreCommunityConfirm:
|
||||
'پس از تغییر، میتوانید با حساب ابرمدیر فعلی Enterprise وارد نسخه Community شوید. پایگاههای داده ویژه Enterprise در طول تغییر حذف میشوند.',
|
||||
restoreCommunityStarting:
|
||||
'نسخه Community در حال آمادهسازی است. پس از تکمیل، پنل بهطور خودکار راهاندازی مجدد میشود.',
|
||||
levelUpPro: 'ارتقاء به نسخه تجاری',
|
||||
licenseSync: 'همگامسازی مجوز',
|
||||
knowMorePro: 'بیشتر بدانید',
|
||||
|
||||
@@ -3196,6 +3196,20 @@ const message = {
|
||||
'まずデバイス ID をコピーしてライセンスを取得し、ライセンスをインポートするとログインできます。',
|
||||
licenseRequiredUserTip:
|
||||
'Enterprise Edition のライセンスがバインドされていません。スーパー管理者に連絡してライセンスをインポートしてください。',
|
||||
restoreCommunity: 'コミュニティ版に戻す',
|
||||
restoreCommunityOnline: 'オンライン復元',
|
||||
restoreCommunityOffline: 'オフライン復元',
|
||||
restoreCommunityPackageFound:
|
||||
'使用可能なコミュニティ版オフラインパッケージが検出されました。オフライン復元を実行できます。',
|
||||
restoreCommunityPackageMissing:
|
||||
'使用可能なコミュニティ版オフラインパッケージが検出されていないため、現在オフライン復元を実行できません。',
|
||||
restoreCommunityOnlineHelper:
|
||||
'現在のバージョンのコミュニティ版パッケージを自動的にダウンロードし、Enterprise 専用データを削除します。',
|
||||
restoreCommunityOfflineHelper:
|
||||
'現在の Enterprise バージョン以上の公式パッケージを {path} に配置してください。使用可能なパッケージが検出されると切り替えが有効になります。',
|
||||
restoreCommunityConfirm:
|
||||
'切り替え後は、現在の Enterprise スーパー管理者アカウントでコミュニティ版にログインできます。切り替え中に Enterprise 専用データベースが削除されます。',
|
||||
restoreCommunityStarting: 'コミュニティ版を準備しています。完了後、パネルは自動的に再起動します。',
|
||||
technicalAdvice: '技術的な相談',
|
||||
advice: '相談',
|
||||
levelUpPro: '商用版にアップグレード',
|
||||
|
||||
@@ -3127,6 +3127,20 @@ const message = {
|
||||
licenseRequiredShortTip: '먼저 장치 ID를 복사해 라이선스를 받은 뒤, 라이선스를 가져오면 로그인할 수 있습니다.',
|
||||
licenseRequiredUserTip:
|
||||
'Enterprise Edition 라이선스가 바인딩되지 않았습니다. 슈퍼 관리자에게 라이선스 가져오기를 요청하세요.',
|
||||
restoreCommunity: '커뮤니티 버전 복원',
|
||||
restoreCommunityOnline: '온라인 복원',
|
||||
restoreCommunityOffline: '오프라인 복원',
|
||||
restoreCommunityPackageFound:
|
||||
'사용 가능한 Community Edition 오프라인 설치 패키지가 감지되었습니다. 오프라인 복원을 실행할 수 있습니다.',
|
||||
restoreCommunityPackageMissing:
|
||||
'사용 가능한 Community Edition 오프라인 설치 패키지가 감지되지 않아 현재 오프라인 복원을 실행할 수 없습니다.',
|
||||
restoreCommunityOnlineHelper:
|
||||
'현재 버전의 커뮤니티 버전 패키지를 자동으로 다운로드하고 Enterprise 전용 데이터를 정리합니다.',
|
||||
restoreCommunityOfflineHelper:
|
||||
'현재 Enterprise 버전 이상인 공식 패키지를 {path}에 저장하세요. 사용 가능한 패키지가 감지되면 전환이 활성화됩니다.',
|
||||
restoreCommunityConfirm:
|
||||
'전환 후 현재 Enterprise 슈퍼 관리자 계정으로 커뮤니티 버전에 로그인할 수 있습니다. 전환 과정에서 Enterprise 전용 데이터베이스가 삭제됩니다.',
|
||||
restoreCommunityStarting: '커뮤니티 버전을 준비 중입니다. 완료되면 패널이 자동으로 다시 시작됩니다.',
|
||||
technicalAdvice: '기술 상담',
|
||||
advice: '상담',
|
||||
levelUpPro: '상용 버전으로 업그레이드',
|
||||
|
||||
@@ -3116,6 +3116,20 @@ const message = {
|
||||
licenseRequiredShortTip:
|
||||
'ກະລຸນາຄັດລອກ ID ອຸປະກອນກ່ອນເພື່ອຮັບລິຂະສິດ. ທ່ານສາມາດເຂົ້າລະບົບໄດ້ຫຼັງຈາກນຳເຂົ້າລິຂະສິດແລ້ວ.',
|
||||
licenseRequiredUserTip: 'ລິຂະສິດເວີຊັນ Enterprise ບໍ່ໄດ້ຖືກຜູກ. ຕິດຕໍ່ຜູ້ດູແລລະບົບສູງສຸດເພື່ອນຳເຂົ້າລິຂະສິດ.',
|
||||
restoreCommunity: 'ກູ້ຄືນ Community Edition',
|
||||
restoreCommunityOnline: 'ກູ້ຄືນອອນລາຍ',
|
||||
restoreCommunityOffline: 'ກູ້ຄືນອອບລາຍ',
|
||||
restoreCommunityPackageFound:
|
||||
'ກວດພົບແພັກເກດ Community Edition ອອບລາຍທີ່ໃຊ້ໄດ້ ແລະສາມາດດຳເນີນການກູ້ຄືນອອບລາຍໄດ້.',
|
||||
restoreCommunityPackageMissing:
|
||||
'ບໍ່ພົບແພັກເກດ Community Edition ອອບລາຍທີ່ໃຊ້ໄດ້ ຈຶ່ງຍັງບໍ່ສາມາດກູ້ຄືນອອບລາຍໄດ້.',
|
||||
restoreCommunityOnlineHelper:
|
||||
'ລະບົບຈະດາວໂຫຼດແພັກເກດ Community Edition ສຳລັບເວີຊັນປັດຈຸບັນອັດຕະໂນມັດ ແລະ ລ້າງຂໍ້ມູນສະເພາະ Enterprise.',
|
||||
restoreCommunityOfflineHelper:
|
||||
'ວາງແພັກເກດທາງການທີ່ມີເວີຊັນບໍ່ຕ່ຳກວ່າ Enterprise ປັດຈຸບັນໄວ້ໃນ {path}. ການສະຫຼັບຈະເປີດໃຊ້ເມື່ອກວດພົບແພັກເກດທີ່ໃຊ້ໄດ້.',
|
||||
restoreCommunityConfirm:
|
||||
'ຫຼັງຈາກສະຫຼັບ ສາມາດໃຊ້ບັນຊີຜູ້ດູແລສູງສຸດ Enterprise ປັດຈຸບັນເຂົ້າ Community Edition. ຖານຂໍ້ມູນສະເພາະ Enterprise ຈະຖືກລຶບໃນລະຫວ່າງການສະຫຼັບ.',
|
||||
restoreCommunityStarting: 'ກຳລັງກະກຽມ Community Edition. ແຜງຄວບຄຸມຈະເລີ່ມໃໝ່ອັດຕະໂນມັດເມື່ອສຳເລັດ.',
|
||||
levelUpPro: 'ອັບເກຣດເປັນເວີຊັນ Commercial',
|
||||
licenseSync: 'ຊິງຄ໌ລິຂະສິດ',
|
||||
knowMorePro: 'ຮຽນຮູ້ເພີ່ມເຕີມ',
|
||||
|
||||
@@ -3252,6 +3252,21 @@ const message = {
|
||||
'Sila salin ID peranti terlebih dahulu untuk mendapatkan lesen. Anda boleh log masuk selepas mengimport lesen.',
|
||||
licenseRequiredUserTip:
|
||||
'Lesen Enterprise Edition belum diikat. Hubungi pentadbir super untuk mengimport lesen.',
|
||||
restoreCommunity: 'Pulihkan Community Edition',
|
||||
restoreCommunityOnline: 'Pemulihan dalam talian',
|
||||
restoreCommunityOffline: 'Pemulihan luar talian',
|
||||
restoreCommunityPackageFound:
|
||||
'Pakej luar talian Community Edition yang boleh digunakan telah dikesan. Pemulihan luar talian tersedia.',
|
||||
restoreCommunityPackageMissing:
|
||||
'Tiada pakej luar talian Community Edition yang boleh digunakan dikesan. Pemulihan luar talian tidak tersedia pada masa ini.',
|
||||
restoreCommunityOnlineHelper:
|
||||
'Pakej Community Edition untuk versi semasa akan dimuat turun secara automatik dan data khusus Enterprise akan dibersihkan.',
|
||||
restoreCommunityOfflineHelper:
|
||||
'Letakkan pakej rasmi yang tidak lebih lama daripada versi Enterprise semasa dalam {path}. Pertukaran akan didayakan selepas pakej yang boleh digunakan dikesan.',
|
||||
restoreCommunityConfirm:
|
||||
'Selepas pertukaran, anda boleh log masuk ke Community Edition menggunakan akaun pentadbir super Enterprise semasa. Pangkalan data khusus Enterprise akan dipadamkan semasa pertukaran.',
|
||||
restoreCommunityStarting:
|
||||
'Community Edition sedang disediakan. Panel akan dimulakan semula secara automatik apabila selesai.',
|
||||
technicalAdvice: 'Konsultasi Teknikal',
|
||||
advice: 'Konsultasi',
|
||||
levelUpPro: 'Naik taraf ke Edisi Komersial',
|
||||
|
||||
@@ -3373,6 +3373,21 @@ const message = {
|
||||
'Copie primeiro o ID do dispositivo para obter a licença. Você poderá entrar depois de importá-la.',
|
||||
licenseRequiredUserTip:
|
||||
'A licença Enterprise Edition não está vinculada. Entre em contato com o superadministrador para importar a licença.',
|
||||
restoreCommunity: 'Restaurar Community Edition',
|
||||
restoreCommunityOnline: 'Restauração online',
|
||||
restoreCommunityOffline: 'Restauração offline',
|
||||
restoreCommunityPackageFound:
|
||||
'Um pacote offline utilizável da Community Edition foi detectado. A restauração offline está disponível.',
|
||||
restoreCommunityPackageMissing:
|
||||
'Nenhum pacote offline utilizável da Community Edition foi detectado. A restauração offline não está disponível no momento.',
|
||||
restoreCommunityOnlineHelper:
|
||||
'O pacote Community Edition da versão atual será baixado automaticamente e os dados exclusivos da Enterprise serão limpos.',
|
||||
restoreCommunityOfflineHelper:
|
||||
'Coloque em {path} um pacote oficial cuja versão não seja anterior à versão Enterprise atual. A troca será habilitada quando um pacote utilizável for detectado.',
|
||||
restoreCommunityConfirm:
|
||||
'Após a troca, você poderá entrar na Community Edition com a conta atual de superadministrador da Enterprise. Os bancos de dados exclusivos da Enterprise serão excluídos durante a troca.',
|
||||
restoreCommunityStarting:
|
||||
'Preparando a Community Edition. O painel será reiniciado automaticamente quando estiver pronto.',
|
||||
technicalAdvice: 'Consultoria técnica',
|
||||
advice: 'Consultoria',
|
||||
levelUpPro: 'Fazer upgrade para a edição comercial',
|
||||
|
||||
@@ -3230,6 +3230,21 @@ const message = {
|
||||
'Сначала скопируйте ID устройства, чтобы получить лицензию. После импорта лицензии вы сможете войти.',
|
||||
licenseRequiredUserTip:
|
||||
'Лицензия Enterprise Edition не привязана. Обратитесь к суперадминистратору для импорта лицензии.',
|
||||
restoreCommunity: 'Восстановить Community Edition',
|
||||
restoreCommunityOnline: 'Онлайн-восстановление',
|
||||
restoreCommunityOffline: 'Офлайн-восстановление',
|
||||
restoreCommunityPackageFound:
|
||||
'Обнаружен подходящий офлайн-пакет Community Edition. Офлайн-восстановление доступно.',
|
||||
restoreCommunityPackageMissing:
|
||||
'Подходящий офлайн-пакет Community Edition не обнаружен. Офлайн-восстановление сейчас недоступно.',
|
||||
restoreCommunityOnlineHelper:
|
||||
'Пакет Community Edition текущей версии будет загружен автоматически, а данные, предназначенные только для Enterprise, будут очищены.',
|
||||
restoreCommunityOfflineHelper:
|
||||
'Поместите официальный пакет версии не ниже текущей Enterprise в {path}. Переключение станет доступно после обнаружения подходящего пакета.',
|
||||
restoreCommunityConfirm:
|
||||
'После переключения можно войти в Community Edition с текущей учётной записью суперадминистратора Enterprise. Базы данных Enterprise будут удалены во время переключения.',
|
||||
restoreCommunityStarting:
|
||||
'Подготавливается Community Edition. После завершения панель автоматически перезапустится.',
|
||||
technicalAdvice: 'Техническая консультация',
|
||||
advice: 'Консультация',
|
||||
levelUpPro: 'Обновить до коммерческой версии',
|
||||
|
||||
@@ -3234,6 +3234,21 @@ const message = {
|
||||
'Lütfen önce cihaz kimliğini kopyalayarak lisansı alın. Lisansı içe aktardıktan sonra giriş yapabilirsiniz.',
|
||||
licenseRequiredUserTip:
|
||||
'Enterprise Edition lisansı bağlı değil. Lisansı içe aktarması için süper yöneticiyle iletişime geçin.',
|
||||
restoreCommunity: 'Community Edition sürümünü geri yükle',
|
||||
restoreCommunityOnline: 'Çevrimiçi geri yükleme',
|
||||
restoreCommunityOffline: 'Çevrimdışı geri yükleme',
|
||||
restoreCommunityPackageFound:
|
||||
'Kullanılabilir bir çevrimdışı Community Edition paketi algılandı. Çevrimdışı geri yükleme kullanılabilir.',
|
||||
restoreCommunityPackageMissing:
|
||||
'Kullanılabilir bir çevrimdışı Community Edition paketi algılanmadı. Çevrimdışı geri yükleme şu anda kullanılamıyor.',
|
||||
restoreCommunityOnlineHelper:
|
||||
'Geçerli sürümün Community Edition paketi otomatik olarak indirilecek ve Enterprise sürümüne özel veriler temizlenecektir.',
|
||||
restoreCommunityOfflineHelper:
|
||||
'Geçerli Enterprise sürümünden eski olmayan resmi bir paketi {path} dizinine yerleştirin. Kullanılabilir bir paket algılandığında geçiş etkinleştirilir.',
|
||||
restoreCommunityConfirm:
|
||||
'Geçişten sonra mevcut Enterprise süper yönetici hesabıyla Community Edition sürümüne giriş yapabilirsiniz. Enterprise sürümüne özel veritabanları geçiş sırasında silinecektir.',
|
||||
restoreCommunityStarting:
|
||||
'Community Edition hazırlanıyor. Hazır olduğunda panel otomatik olarak yeniden başlatılacaktır.',
|
||||
levelUpPro: 'Ticari sürüme yükselt',
|
||||
licenseSync: 'Lisans Senkronizasyonu',
|
||||
knowMorePro: 'Daha Fazla Bilgi Edinin',
|
||||
|
||||
@@ -2964,6 +2964,16 @@ const message = {
|
||||
licenseRequiredTip: '目前尚未匯入許可證,複製裝置 ID 後取得許可證並匯入後才能登入使用。',
|
||||
licenseRequiredShortTip: '請先複製裝置 ID 取得許可證,匯入許可證後即可登入。',
|
||||
licenseRequiredUserTip: '企業版許可證未綁定,請聯絡超級管理員匯入許可證。',
|
||||
restoreCommunity: '恢復社群版',
|
||||
restoreCommunityOnline: '線上恢復',
|
||||
restoreCommunityOffline: '離線恢復',
|
||||
restoreCommunityPackageFound: '已偵測到可用的社群版離線安裝包,可以執行離線恢復。',
|
||||
restoreCommunityPackageMissing: '未偵測到可用的社群版離線安裝包,暫時無法執行離線恢復。',
|
||||
restoreCommunityOnlineHelper: '切換後將自動下載目前版本的社群版安裝包,並清理企業版專屬資料。',
|
||||
restoreCommunityOfflineHelper:
|
||||
'請先將版本不低於目前企業版的官方安裝包放入 {path},偵測到可用安裝包後即可執行切換。',
|
||||
restoreCommunityConfirm: '切換後可使用目前企業版超級管理員帳號登入社群版,切換過程中將刪除企業版專屬資料庫。',
|
||||
restoreCommunityStarting: '正在準備社群版,完成後面板將自動重新啟動。',
|
||||
levelUpPro: '升級商業版',
|
||||
licenseSync: '許可證同步',
|
||||
knowMorePro: '了解更多',
|
||||
|
||||
@@ -2959,6 +2959,15 @@ const message = {
|
||||
licenseRequiredTip: '当前尚未导入许可证,复制设备 ID 后获取许可证并导入后才能登录使用。',
|
||||
licenseRequiredShortTip: '请先复制设备 ID 获取许可证,导入许可证后即可登录。',
|
||||
licenseRequiredUserTip: '企业版许可证未绑定,请联系超级管理员导入许可证。',
|
||||
restoreCommunity: '恢复社区版',
|
||||
restoreCommunityOnline: '在线恢复',
|
||||
restoreCommunityOffline: '离线恢复',
|
||||
restoreCommunityPackageFound: '已检测到可用的社区版离线安装包,可以执行离线恢复。',
|
||||
restoreCommunityPackageMissing: '未检测到可用的社区版离线安装包,暂时无法执行离线恢复。',
|
||||
restoreCommunityOnlineHelper: '将自动下载当前版本的社区版安装包,完成切换。',
|
||||
restoreCommunityOfflineHelper: '请先将版本不低于当前企业版的官方安装包放入 {path} 后执行切换。',
|
||||
restoreCommunityConfirm: '切换后可使用当前企业版超级管理员账号登录社区版,切换过程中将删除企业版专属数据库。',
|
||||
restoreCommunityStarting: '正在准备社区版,完成后面板将自动重启。',
|
||||
levelUpPro: '升级商业版',
|
||||
licenseSync: '许可证同步',
|
||||
knowMorePro: '了解商业版',
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<DialogPro
|
||||
v-model="open"
|
||||
:title="$t('license.restoreCommunity')"
|
||||
size="large"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
@closed="handleClosed"
|
||||
>
|
||||
<div v-loading="loading" class="restore-dialog-content">
|
||||
<el-alert
|
||||
v-if="restoreInfo.state === 'Failed'"
|
||||
:title="restoreInfo.message"
|
||||
type="error"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/>
|
||||
<el-alert :title="$t('license.restoreCommunityConfirm')" type="warning" :closable="false" show-icon />
|
||||
|
||||
<el-radio-group v-model="restoreMode" @change="changeRestoreMode">
|
||||
<el-radio value="online" border>{{ $t('license.restoreCommunityOnline') }}</el-radio>
|
||||
<el-radio value="offline" border>{{ $t('license.restoreCommunityOffline') }}</el-radio>
|
||||
</el-radio-group>
|
||||
|
||||
<span class="input-help">
|
||||
{{
|
||||
restoreMode === 'offline'
|
||||
? $t('license.restoreCommunityOfflineHelper', { path: restoreInfo.packageDirectory })
|
||||
: $t('license.restoreCommunityOnlineHelper')
|
||||
}}
|
||||
</span>
|
||||
|
||||
<el-alert
|
||||
v-if="restoreMode === 'offline'"
|
||||
v-loading="packageChecking"
|
||||
:title="
|
||||
restoreInfo.packageExist
|
||||
? $t('license.restoreCommunityPackageFound')
|
||||
: $t('license.restoreCommunityPackageMissing')
|
||||
"
|
||||
:type="restoreInfo.packageExist ? 'success' : 'warning'"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button :disabled="loading" @click="open = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
:disabled="restoreMode === 'offline' && (packageChecking || !restoreInfo.packageExist)"
|
||||
@click="submit"
|
||||
>
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</DialogPro>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onUnmounted, reactive, ref } from 'vue';
|
||||
import { getCommunityRestoreStatus, restoreCommunity } from '@/api/modules/setting';
|
||||
|
||||
const emit = defineEmits<{
|
||||
started: [];
|
||||
}>();
|
||||
|
||||
const open = ref(false);
|
||||
const loading = ref(false);
|
||||
const packageChecking = ref(false);
|
||||
const restoreMode = ref<'online' | 'offline'>('online');
|
||||
const restoreInfo = reactive({
|
||||
state: 'Ready',
|
||||
message: '',
|
||||
packageExist: false,
|
||||
packageDirectory: '',
|
||||
});
|
||||
let packageTimer: number | undefined;
|
||||
|
||||
const loadRestoreInfo = async () => {
|
||||
const res = await getCommunityRestoreStatus();
|
||||
Object.assign(restoreInfo, res.data);
|
||||
};
|
||||
|
||||
const refreshOfflinePackage = async (showLoading = false) => {
|
||||
packageChecking.value = showLoading;
|
||||
try {
|
||||
await loadRestoreInfo();
|
||||
} finally {
|
||||
if (showLoading) {
|
||||
packageChecking.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const schedulePackagePoll = () => {
|
||||
window.clearTimeout(packageTimer);
|
||||
packageTimer = window.setTimeout(pollOfflinePackage, 3000);
|
||||
};
|
||||
|
||||
const pollOfflinePackage = async () => {
|
||||
try {
|
||||
await refreshOfflinePackage();
|
||||
} finally {
|
||||
if (open.value && restoreMode.value === 'offline') {
|
||||
schedulePackagePoll();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const changeRestoreMode = async () => {
|
||||
window.clearTimeout(packageTimer);
|
||||
if (restoreMode.value !== 'offline') {
|
||||
return;
|
||||
}
|
||||
await refreshOfflinePackage(true);
|
||||
schedulePackagePoll();
|
||||
};
|
||||
|
||||
const acceptParams = async () => {
|
||||
restoreMode.value = 'online';
|
||||
open.value = true;
|
||||
await loadRestoreInfo();
|
||||
};
|
||||
|
||||
const handleClosed = () => {
|
||||
window.clearTimeout(packageTimer);
|
||||
restoreMode.value = 'online';
|
||||
loading.value = false;
|
||||
packageChecking.value = false;
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
loading.value = true;
|
||||
await restoreCommunity(restoreMode.value)
|
||||
.then(() => {
|
||||
emit('started');
|
||||
open.value = false;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
window.clearTimeout(packageTimer);
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.restore-dialog-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.restore-mode-group {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
|
||||
:deep(.el-radio) {
|
||||
width: 100%;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,11 +1,26 @@
|
||||
<template>
|
||||
<div class="flex items-center justify-center min-h-screen relative bg-gray-100">
|
||||
<div
|
||||
class="flex items-center justify-center min-h-screen relative bg-gray-100"
|
||||
v-loading="restoring"
|
||||
:element-loading-text="$t('license.restoreCommunityStarting')"
|
||||
fullscreen
|
||||
>
|
||||
<div class="absolute inset-0 bg-cover bg-center bg-no-repeat" :style="backgroundStyle"></div>
|
||||
<div
|
||||
:style="{ width: containerWidth, height: containerHeight }"
|
||||
class="bg-white shadow-lg relative z-10 border border-gray-200 flex overflow-hidden"
|
||||
id="login-container"
|
||||
>
|
||||
<el-button
|
||||
class="community-restore-link"
|
||||
type="primary"
|
||||
link
|
||||
:loading="restoring"
|
||||
:disabled="restoring"
|
||||
@click="communityRestoreRef?.acceptParams()"
|
||||
>
|
||||
{{ $t('license.restoreCommunity') }}
|
||||
</el-button>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 items-stretch w-full">
|
||||
<div v-if="showLogo" class="flex justify-center" :style="{ height: containerHeight }">
|
||||
<img
|
||||
@@ -85,6 +100,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CommunityRestoreDialog ref="communityRestoreRef" @started="handleCommunityRestoreStarted" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -93,37 +110,43 @@ import { computed, onMounted, onUnmounted, reactive, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import type { UploadFile, UploadFiles, UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
|
||||
import { genFileId } from 'element-plus';
|
||||
import { getEnterpriseLicense, uploadEnterpriseLicense } from '@/api/modules/setting';
|
||||
import { getCommunityRestoreStatus, getEnterpriseLicense, uploadEnterpriseLicense } from '@/api/modules/setting';
|
||||
import { getLoginSetting } from '@/api/modules/auth';
|
||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
import { preloadImage } from '@/utils/browser';
|
||||
import { adjustColorToRGBA } from '@/utils/color';
|
||||
import { getXpackSettingForTheme } from '@/utils/xpack';
|
||||
import { copyText } from '@/utils/clipboard';
|
||||
import i18n from '@/lang';
|
||||
import CommunityRestoreDialog from './community-restore/index.vue';
|
||||
|
||||
const router = useRouter();
|
||||
const {
|
||||
globalStore,
|
||||
entrance,
|
||||
isEnterprise,
|
||||
isEnterpriseLicenseLoaded,
|
||||
isEnterpriseLicensed,
|
||||
isFxplay,
|
||||
isIntl,
|
||||
isLoading: restoring,
|
||||
isLogin,
|
||||
isMasterProductPro,
|
||||
isOffline,
|
||||
isOnRestart,
|
||||
menuAccordion,
|
||||
openMenuTabs,
|
||||
themeConfig,
|
||||
} = useGlobalStore();
|
||||
const loading = ref(false);
|
||||
const communityRestoreRef = ref<InstanceType<typeof CommunityRestoreDialog>>();
|
||||
const uploadRef = ref<UploadInstance>();
|
||||
const uploaderFiles = ref<UploadFiles>([]);
|
||||
const licenseInfo = reactive({
|
||||
deviceID: '',
|
||||
});
|
||||
let restoreTimer: number | undefined;
|
||||
const defaultLoginImage = new URL('@/assets/images/1panel-login-enterprise.png', import.meta.url).href;
|
||||
const defaultLoginBgImage = new URL('@/assets/images/1panel-login-bg.jpg', import.meta.url).href;
|
||||
const loadedLoginImage = ref<string | null>(null);
|
||||
@@ -133,6 +156,7 @@ const imgLoaded = ref(false);
|
||||
|
||||
const FIXED_WIDTH = 1000;
|
||||
const FIXED_HEIGHT = 415;
|
||||
const RESTORE_POLL_INTERVAL = 10_000;
|
||||
const containerWidth = computed(() => `${FIXED_WIDTH}px`);
|
||||
const containerHeight = computed(() => `${FIXED_HEIGHT}px`);
|
||||
const width = ref(window.innerWidth);
|
||||
@@ -183,7 +207,7 @@ const applyLoginThemeColor = () => {
|
||||
);
|
||||
document.documentElement.style.setProperty(
|
||||
'--login-loading-mask-color',
|
||||
adjustColorToRGBA(loginBtnLinkColor, 30, 15),
|
||||
adjustColorToRGBA(loginBtnLinkColor, 0, 60),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -259,6 +283,72 @@ const loadLicenseInfo = async () => {
|
||||
isEnterpriseLicensed.value = false;
|
||||
};
|
||||
|
||||
const loadRestoreInfo = async () => {
|
||||
const res = await getCommunityRestoreStatus();
|
||||
restoring.value = res.data.state === 'Running';
|
||||
isOnRestart.value = restoring.value;
|
||||
};
|
||||
|
||||
const scheduleRestorePoll = () => {
|
||||
window.clearTimeout(restoreTimer);
|
||||
restoreTimer = window.setTimeout(pollRestoreStatus, RESTORE_POLL_INTERVAL);
|
||||
};
|
||||
|
||||
const redirectToCommunityLogin = () => {
|
||||
restoring.value = false;
|
||||
isOnRestart.value = false;
|
||||
globalStore.setLogStatus(false);
|
||||
globalStore.clearAuthInfo();
|
||||
isEnterprise.value = false;
|
||||
isEnterpriseLicensed.value = false;
|
||||
isEnterpriseLicenseLoaded.value = true;
|
||||
window.location.replace(entrance.value ? `/${entrance.value}` : '/');
|
||||
};
|
||||
|
||||
const waitForCommunityService = async () => {
|
||||
try {
|
||||
const response = await fetch(`${import.meta.env.VITE_API_URL}/core/auth/setting`, {
|
||||
credentials: 'include',
|
||||
cache: 'no-store',
|
||||
});
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
if (result?.data?.isEnterprise === false) {
|
||||
redirectToCommunityLogin();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// The core service is expected to be temporarily unavailable while binaries are switched.
|
||||
}
|
||||
scheduleRestorePoll();
|
||||
};
|
||||
|
||||
const pollRestoreStatus = async () => {
|
||||
try {
|
||||
const res = await getCommunityRestoreStatus();
|
||||
if (res.data.state === 'Failed') {
|
||||
restoring.value = false;
|
||||
isOnRestart.value = false;
|
||||
MsgError(res.data.message);
|
||||
return;
|
||||
}
|
||||
restoring.value = res.data.state === 'Running';
|
||||
isOnRestart.value = restoring.value;
|
||||
if (restoring.value) {
|
||||
scheduleRestorePoll();
|
||||
}
|
||||
} catch {
|
||||
await waitForCommunityService();
|
||||
}
|
||||
};
|
||||
|
||||
const handleCommunityRestoreStarted = () => {
|
||||
restoring.value = true;
|
||||
isOnRestart.value = true;
|
||||
scheduleRestorePoll();
|
||||
};
|
||||
|
||||
const fileOnChange = (_uploadFile: UploadFile, uploadFiles: UploadFiles) => {
|
||||
uploaderFiles.value = uploadFiles;
|
||||
};
|
||||
@@ -317,6 +407,12 @@ onMounted(async () => {
|
||||
await loadXpackLoginTheme();
|
||||
await loadBackground();
|
||||
await loadLicenseInfo();
|
||||
if (!isEnterpriseLicensed.value) {
|
||||
await loadRestoreInfo();
|
||||
if (restoring.value) {
|
||||
scheduleRestorePoll();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -324,6 +420,7 @@ onMounted(async () => {
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', updateSize);
|
||||
window.clearTimeout(restoreTimer);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -332,6 +429,18 @@ onUnmounted(() => {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.community-restore-link {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 20px;
|
||||
z-index: 1;
|
||||
color: var(--login-btn-link-color, #005eeb);
|
||||
|
||||
&:hover {
|
||||
color: var(--login-btn-link-hover-color, #0054d3);
|
||||
}
|
||||
}
|
||||
|
||||
.login-form {
|
||||
max-width: 560px;
|
||||
gap: 16px;
|
||||
@@ -449,7 +558,11 @@ onUnmounted(() => {
|
||||
background-color: var(--login-loading-mask-color) !important;
|
||||
|
||||
.el-loading-spinner .path {
|
||||
stroke: var(--login-btn-link-color);
|
||||
stroke: #ffffff;
|
||||
}
|
||||
|
||||
.el-loading-text {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -510,6 +510,8 @@ function load18n(label: string) {
|
||||
return i18n.global.t('clean.snapshot');
|
||||
case 'rollback':
|
||||
return i18n.global.t('clean.rollback');
|
||||
case 'rollback_community_restore':
|
||||
return i18n.global.t('license.restoreCommunity');
|
||||
case 'tmp_backup':
|
||||
return i18n.global.t('clean.tmpBackup');
|
||||
case 'unknown_app':
|
||||
|
||||
Reference in New Issue
Block a user