From b52c0d16fa41ff75083d36fc336902aeb052d733 Mon Sep 17 00:00:00 2001 From: andrei Date: Wed, 2 Jul 2025 10:38:07 +0500 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=91=D1=8D=D0=BA=D0=B0=D0=BF=D1=8B=20?= =?UTF-8?q?=D0=B8=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=B1=D1=8D=D0=BA=D0=B0=D0=BF=D0=BE=D0=B2=20=D0=B0=D0=B4=D0=BC?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/app/controllers/backup_router.py | 6 +++++- api/app/infrastructure/backup_service.py | 13 +++++++------ web-app/src/Api/baseQuery.js | 3 +-- .../AppointmentFormModal/AppointmentFormModal.jsx | 2 +- .../Components/BackupManageTab/BackupManageTab.jsx | 2 +- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/api/app/controllers/backup_router.py b/api/app/controllers/backup_router.py index d9262f6..a5a4cf7 100644 --- a/api/app/controllers/backup_router.py +++ b/api/app/controllers/backup_router.py @@ -71,7 +71,11 @@ async def upload_backup( db: AsyncSession = Depends(get_db), user=Depends(require_admin), ): - backup_service = BackupService(db) + backup_service = BackupService( + db, + backup_dir=settings.BACKUP_DIR, + app_files_dir=settings.FILE_UPLOAD_DIR, + ) return await backup_service.upload_backup(file, user) diff --git a/api/app/infrastructure/backup_service.py b/api/app/infrastructure/backup_service.py index b814cbc..2543416 100644 --- a/api/app/infrastructure/backup_service.py +++ b/api/app/infrastructure/backup_service.py @@ -89,8 +89,7 @@ class BackupService: file.file.seek(0) self.validate_file_type(file) - if not self.validate_backup_archive(file_bytes, - expected_app_files_dir_name=os.path.basename(self.app_files_dir)): + if not self.validate_backup_archive(file_bytes, self.app_files_dir): raise HTTPException(400, "Неверная структура архива резервной копии") filename = self.generate_filename(file) @@ -102,6 +101,7 @@ class BackupService: filename=filename, path=backup_path, user_id=user.id, + is_by_user=True, ) await self.backup_repository.create(backup_record) return self.model_to_entity(backup_record) @@ -132,12 +132,15 @@ class BackupService: try: with tarfile.open(fileobj=io.BytesIO(file_bytes), mode="r:gz") as tar: members = tar.getnames() + if "db_dump.sql" not in members: return False + if not any(name.startswith(expected_app_files_dir_name) for name in members): return False + return True - except Exception: + except Exception as e: return False @staticmethod @@ -156,9 +159,7 @@ class BackupService: mime = magic.Magic(mime=True) file_type = mime.from_buffer(file.file.read(1024)) file.file.seek(0) - content = file.file.read() - print(content.decode('utf-8')) - if file_type not in ["application/zip", "application/gzip"]: + if file_type not in ["application/zip", "application/gzip", "application/x-gzip"]: raise HTTPException(400, "Неправильный формат файла") @staticmethod diff --git a/web-app/src/Api/baseQuery.js b/web-app/src/Api/baseQuery.js index e61757d..4fdff3c 100644 --- a/web-app/src/Api/baseQuery.js +++ b/web-app/src/Api/baseQuery.js @@ -9,8 +9,7 @@ export const baseQuery = fetchBaseQuery({ if (token) { headers.set('Authorization', `Bearer ${token}`); } - - if (endpoint === 'uploadAppointmentFile') { + if (endpoint === 'uploadAppointmentFile' || endpoint === 'uploadBackup') { const mutation = getState()?.api?.mutations?.[Object.keys(getState()?.api?.mutations || {})[0]]; if (mutation?.body instanceof FormData) { headers.delete('Content-Type'); diff --git a/web-app/src/Components/Dummies/AppointmentFormModal/AppointmentFormModal.jsx b/web-app/src/Components/Dummies/AppointmentFormModal/AppointmentFormModal.jsx index baea0c8..7ba5703 100644 --- a/web-app/src/Components/Dummies/AppointmentFormModal/AppointmentFormModal.jsx +++ b/web-app/src/Components/Dummies/AppointmentFormModal/AppointmentFormModal.jsx @@ -202,7 +202,7 @@ const AppointmentFormModal = () => { fileList={appointmentFormModalUI.draftFiles} beforeUpload={(file) => { appointmentFormModalUI.handleAddFile(file); - return false; // Prevent auto-upload + return false; }} onRemove={(file) => appointmentFormModalUI.handleRemoveFile(file)} accept=".pdf,.doc,.docx,.jpg,.jpeg,.png" diff --git a/web-app/src/Components/Pages/AdminPage/Components/BackupManageTab/BackupManageTab.jsx b/web-app/src/Components/Pages/AdminPage/Components/BackupManageTab/BackupManageTab.jsx index 12bc0d8..dacd98f 100644 --- a/web-app/src/Components/Pages/AdminPage/Components/BackupManageTab/BackupManageTab.jsx +++ b/web-app/src/Components/Pages/AdminPage/Components/BackupManageTab/BackupManageTab.jsx @@ -46,7 +46,7 @@ const BackupManageTab = () => { renderItem={(backup) => ( {backup.filename} - Создан: {new Date(backup.timestamp).toLocaleString()} + {backup.is_by_user ? "Загружена" : "Создана"}: {new Date(backup.timestamp).toLocaleString()}