feat: Бэкапы и загрузка бэкапов админом
This commit is contained in:
parent
c70b109851
commit
b52c0d16fa
@ -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)
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -46,7 +46,7 @@ const BackupManageTab = () => {
|
||||
renderItem={(backup) => (
|
||||
<List.Item>
|
||||
<Typography.Text>{backup.filename}</Typography.Text>
|
||||
<Typography.Text>Создан: {new Date(backup.timestamp).toLocaleString()}</Typography.Text>
|
||||
<Typography.Text>{backup.is_by_user ? "Загружена" : "Создана"}: {new Date(backup.timestamp).toLocaleString()}</Typography.Text>
|
||||
<Tooltip title="Скачать резервную копию">
|
||||
<Button
|
||||
type="primary"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user