11# Standard library imports
2+ from posixpath import relpath
23import os
34import time
45import shutil
1415from opengeodeweb_back import geode_functions , utils_functions
1516from .models import blueprint_models
1617from . import schemas
18+ from opengeodeweb_microservice .database .data import Data
19+ from opengeodeweb_microservice .database .connection import get_session
1720
1821routes = flask .Blueprint ("routes" , __name__ , url_prefix = "/opengeodeweb_back" )
1922
@@ -280,22 +283,46 @@ def export_project() -> flask.Response:
280283 utils_functions .validate_request (flask .request , schemas_dict ["export_project" ])
281284 params = schemas .ExportProject .from_dict (flask .request .get_json ())
282285
283- data_folder_path : str = flask .current_app .config ["DATA_FOLDER_PATH" ]
284- upload_folder : str = flask .current_app .config ["UPLOAD_FOLDER" ]
285- os .makedirs (upload_folder , exist_ok = True )
286+ project_folder : str = flask .current_app .config ["DATA_FOLDER_PATH" ]
287+ os .makedirs (project_folder , exist_ok = True )
288+
289+ if not params .filename :
290+ flask .abort (400 , "filename is required" )
291+ filename : str = werkzeug .utils .secure_filename (os .path .basename (params .filename ))
292+ export_zip_path = os .path .join (project_folder , filename )
293+
294+ with get_session () as session :
295+ entries = [
296+ {
297+ "id" : entry .id ,
298+ "input_file" : entry .input_file ,
299+ "additional_files" : entry .additional_files ,
300+ }
301+ for entry in session .query (Data ).all ()
302+ ]
303+
304+ with zipfile .ZipFile (export_zip_path , "w" , compression = zipfile .ZIP_DEFLATED ) as zip_file :
305+ database_root_path = os .path .join (project_folder , "project.db" )
306+ if os .path .isfile (database_root_path ):
307+ zip_file .write (database_root_path , "project.db" )
308+
309+ for entry in entries :
310+ base_dir = os .path .join (project_folder , entry ["id" ])
311+
312+ input_file = entry ["input_file" ]
313+ if input_file :
314+ in_path = os .path .join (base_dir , input_file )
315+ if os .path .isfile (in_path ):
316+ zip_file .write (in_path , os .path .join (entry ["id" ], input_file ))
317+
318+ for relative_path in entry ["additional_files" ] or []:
319+ add_path = os .path .join (base_dir , relative_path )
320+ if os .path .isfile (add_path ):
321+ zip_file .write (add_path , os .path .join (entry ["id" ], relative_path ))
286322
287- filename : str = params .filename or f"project_{ int (time .time ())} .zip"
288- export_zip_path = os .path .join (upload_folder , filename )
289-
290- with zipfile .ZipFile (export_zip_path , "w" , compression = 8 ) as zip_file :
291- pattern = os .path .join (data_folder_path , "**" , "*" )
292- for full_path in glob .glob (pattern , recursive = True ):
293- if os .path .isfile (full_path ):
294- archive_name = os .path .relpath (full_path , start = data_folder_path )
295- zip_file .write (full_path , archive_name )
296323 zip_file .writestr ("snapshot.json" , flask .json .dumps (params .snapshot ))
297324
298- return utils_functions .send_file (upload_folder , [export_zip_path ], filename )
325+ return utils_functions .send_file (project_folder , [export_zip_path ], filename )
299326
300327
301328@routes .route (
@@ -313,31 +340,30 @@ def import_project() -> flask.Response:
313340 if not filename .lower ().endswith (".zip" ):
314341 flask .abort (400 , "Uploaded file must be a .zip" )
315342
316- data_folder_path : str = flask .current_app .config ["DATA_FOLDER_PATH" ]
317- os .makedirs (data_folder_path , exist_ok = True )
318- for entry in os .listdir (data_folder_path ):
319- entry_path = os .path .join (data_folder_path , entry )
320- try :
321- if os .path .isdir (entry_path ):
322- shutil .rmtree (entry_path , ignore_errors = True )
323- else :
324- os .remove (entry_path )
325- except FileNotFoundError :
326- pass
327- except PermissionError :
328- flask .abort (423 , "Project files are locked; cannot overwrite" )
343+ project_folder_path : str = flask .current_app .config ["DATA_FOLDER_PATH" ]
344+ try :
345+ if os .path .exists (project_folder_path ):
346+ shutil .rmtree (project_folder_path )
347+ os .makedirs (project_folder_path , exist_ok = True )
348+ except PermissionError :
349+ flask .abort (423 , "Project files are locked; cannot overwrite" )
329350
330351 zip_file .stream .seek (0 )
331- with zipfile .ZipFile (zip_file .stream ) as zf :
332- base = os .path .abspath (data_folder_path )
333- for member in zf .namelist ():
334- target = os .path .abspath (os .path .normpath (os .path .join (base , member )))
335- if not (target == base or target .startswith (base + os .sep )):
352+ with zipfile .ZipFile (zip_file .stream ) as zip_archive :
353+ project_folder = os .path .abspath (project_folder_path )
354+ for member in zip_archive .namelist ():
355+ target = os .path .abspath (os .path .normpath (os .path .join (project_folder , member )))
356+ if not (target == project_folder or target .startswith (project_folder + os .sep )):
336357 flask .abort (400 , "Zip contains unsafe paths" )
337- zf .extractall (data_folder_path )
358+ zip_archive .extractall (project_folder )
359+
360+ database_root_path = os .path .join (project_folder , "project.db" )
361+ if not os .path .isfile (database_root_path ):
362+ flask .abort (400 , "Missing project.db at project root" )
363+
338364 snapshot = {}
339365 try :
340- raw = zf .read ("snapshot.json" ).decode ("utf-8" )
366+ raw = zip_archive .read ("snapshot.json" ).decode ("utf-8" )
341367 snapshot = flask .json .loads (raw )
342368 except KeyError :
343369 snapshot = {}
0 commit comments