Flowfile 0.3.8__py3-none-any.whl → 0.3.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of Flowfile might be problematic. Click here for more details.

Files changed (161) hide show
  1. flowfile/__init__.py +4 -3
  2. flowfile/api.py +1 -1
  3. flowfile/web/static/assets/{CloudConnectionManager-c20a740f.js → CloudConnectionManager-d7c2c028.js} +2 -2
  4. flowfile/web/static/assets/{CloudStorageReader-960b400a.js → CloudStorageReader-d467329f.js} +11 -78
  5. flowfile/web/static/assets/{CloudStorageWriter-e3decbdd.js → CloudStorageWriter-071b8b00.js} +12 -79
  6. flowfile/web/static/assets/{CloudStorageWriter-49c9a4b2.css → CloudStorageWriter-b0ee067f.css} +24 -24
  7. flowfile/web/static/assets/ContextMenu-2dea5e27.js +41 -0
  8. flowfile/web/static/assets/{SettingsSection-9c836ecc.css → ContextMenu-4c74eef1.css} +0 -21
  9. flowfile/web/static/assets/ContextMenu-63cfa99b.css +26 -0
  10. flowfile/web/static/assets/ContextMenu-785554c4.js +41 -0
  11. flowfile/web/static/assets/ContextMenu-a51e19ea.js +41 -0
  12. flowfile/web/static/assets/ContextMenu-c13f91d0.css +26 -0
  13. flowfile/web/static/assets/{CrossJoin-41efa4cb.css → CrossJoin-1119d18e.css} +18 -18
  14. flowfile/web/static/assets/{CrossJoin-d67e2405.js → CrossJoin-cf68ec7a.js} +14 -84
  15. flowfile/web/static/assets/{DatabaseConnectionSettings-a81e0f7e.js → DatabaseConnectionSettings-435c5dd8.js} +3 -3
  16. flowfile/web/static/assets/{DatabaseManager-9ea35e84.js → DatabaseManager-349e33a8.js} +2 -2
  17. flowfile/web/static/assets/{DatabaseReader-9578bfa5.js → DatabaseReader-8075bd28.js} +14 -114
  18. flowfile/web/static/assets/{DatabaseReader-f50c6558.css → DatabaseReader-ae61773c.css} +0 -27
  19. flowfile/web/static/assets/{DatabaseWriter-19531098.js → DatabaseWriter-3e2dda89.js} +13 -74
  20. flowfile/web/static/assets/{ExploreData-5bdae813.css → ExploreData-2d0cf4db.css} +8 -14
  21. flowfile/web/static/assets/ExploreData-76ec698c.js +192 -0
  22. flowfile/web/static/assets/{ExternalSource-2297ef96.js → ExternalSource-609a265c.js} +8 -79
  23. flowfile/web/static/assets/{Filter-f211c03a.js → Filter-97cff793.js} +12 -85
  24. flowfile/web/static/assets/{Filter-a9d08ba1.css → Filter-f62091b3.css} +3 -3
  25. flowfile/web/static/assets/{Formula-4207ea31.js → Formula-09de0ec9.js} +18 -85
  26. flowfile/web/static/assets/{Formula-29f19d21.css → Formula-bb96803d.css} +4 -4
  27. flowfile/web/static/assets/{FuzzyMatch-6857de82.css → FuzzyMatch-1010f966.css} +42 -42
  28. flowfile/web/static/assets/{FuzzyMatch-bf120df0.js → FuzzyMatch-bdf70248.js} +16 -87
  29. flowfile/web/static/assets/{GraphSolver-5bb7497a.js → GraphSolver-0b5a0e05.js} +13 -159
  30. flowfile/web/static/assets/GraphSolver-f0cb7bfb.css +22 -0
  31. flowfile/web/static/assets/{Unique-b5615727.css → GroupBy-b9505323.css} +8 -8
  32. flowfile/web/static/assets/{GroupBy-92c81b65.js → GroupBy-eaddadde.js} +12 -75
  33. flowfile/web/static/assets/{Join-4e49a274.js → Join-3313371b.js} +15 -85
  34. flowfile/web/static/assets/{Join-f45eff22.css → Join-fd79b451.css} +20 -20
  35. flowfile/web/static/assets/{ManualInput-a71b52c6.css → ManualInput-3246a08d.css} +20 -20
  36. flowfile/web/static/assets/{ManualInput-90998ae8.js → ManualInput-e8bfc0be.js} +11 -82
  37. flowfile/web/static/assets/{Output-81e3e917.js → Output-7303bb09.js} +13 -243
  38. flowfile/web/static/assets/Output-ddc9079f.css +37 -0
  39. flowfile/web/static/assets/{Pivot-a3419842.js → Pivot-3b1c54ef.js} +14 -138
  40. flowfile/web/static/assets/Pivot-cf333e3d.css +22 -0
  41. flowfile/web/static/assets/PivotValidation-3bb36c8f.js +61 -0
  42. flowfile/web/static/assets/PivotValidation-891ddfb0.css +13 -0
  43. flowfile/web/static/assets/PivotValidation-c46cd420.css +13 -0
  44. flowfile/web/static/assets/PivotValidation-eaa819c0.js +61 -0
  45. flowfile/web/static/assets/{PolarsCode-72710deb.js → PolarsCode-aa12e25d.js} +13 -80
  46. flowfile/web/static/assets/Read-6b17491f.css +62 -0
  47. flowfile/web/static/assets/Read-a2bfc618.js +243 -0
  48. flowfile/web/static/assets/RecordCount-aa0dc082.js +53 -0
  49. flowfile/web/static/assets/{RecordId-10baf191.js → RecordId-48ee1a3b.js} +8 -80
  50. flowfile/web/static/assets/SQLQueryComponent-36cef432.css +27 -0
  51. flowfile/web/static/assets/SQLQueryComponent-e149dbf2.js +38 -0
  52. flowfile/web/static/assets/{Sample-3ed9a0ae.js → Sample-f06cb97a.js} +8 -77
  53. flowfile/web/static/assets/{SecretManager-0d49c0e8.js → SecretManager-37f34886.js} +2 -2
  54. flowfile/web/static/assets/{Select-8a02a0b3.js → Select-b60e6c47.js} +11 -85
  55. flowfile/web/static/assets/SettingsSection-2e4d03c4.css +21 -0
  56. flowfile/web/static/assets/SettingsSection-5c696bee.css +20 -0
  57. flowfile/web/static/assets/SettingsSection-70e5a7b1.js +53 -0
  58. flowfile/web/static/assets/SettingsSection-71e6b7e3.css +21 -0
  59. flowfile/web/static/assets/{SettingsSection-4c0f45f5.js → SettingsSection-75b6cf4f.js} +2 -40
  60. flowfile/web/static/assets/SettingsSection-e57a672e.js +45 -0
  61. flowfile/web/static/assets/{GroupBy-ab1ea74b.css → Sort-3643d625.css} +8 -8
  62. flowfile/web/static/assets/{Sort-f55c9f9d.js → Sort-51b1ee4d.js} +12 -97
  63. flowfile/web/static/assets/{TextToRows-5dbc2145.js → TextToRows-26835f8f.js} +14 -83
  64. flowfile/web/static/assets/{TextToRows-c92d1ec2.css → TextToRows-5d2c1190.css} +9 -9
  65. flowfile/web/static/assets/{UnavailableFields-a1768e52.js → UnavailableFields-88a4cd0c.js} +2 -2
  66. flowfile/web/static/assets/Union-4d0088eb.js +77 -0
  67. flowfile/web/static/assets/{Union-8d9ac7f9.css → Union-af6c3d9b.css} +6 -6
  68. flowfile/web/static/assets/{Unique-46b250da.js → Unique-7d554a62.js} +22 -91
  69. flowfile/web/static/assets/{Sort-7ccfa0fe.css → Unique-f9fb0809.css} +8 -8
  70. flowfile/web/static/assets/Unpivot-1e422df3.css +30 -0
  71. flowfile/web/static/assets/{Unpivot-25ac84cc.js → Unpivot-4668595c.js} +12 -166
  72. flowfile/web/static/assets/UnpivotValidation-0d240eeb.css +13 -0
  73. flowfile/web/static/assets/UnpivotValidation-d4f0e0e8.js +51 -0
  74. flowfile/web/static/assets/{ExploreData-40476474.js → VueGraphicWalker-5324d566.js} +4 -264
  75. flowfile/web/static/assets/VueGraphicWalker-ed5ab88b.css +6 -0
  76. flowfile/web/static/assets/{api-6ef0dcef.js → api-271ed117.js} +1 -1
  77. flowfile/web/static/assets/{api-a0abbdc7.js → api-31e4fea6.js} +1 -1
  78. flowfile/web/static/assets/{designer-186f2e71.css → designer-091bdc3f.css} +819 -184
  79. flowfile/web/static/assets/{designer-13eabd83.js → designer-bf3d9487.js} +2214 -680
  80. flowfile/web/static/assets/{documentation-b87e7f6f.js → documentation-4d0a1cea.js} +1 -1
  81. flowfile/web/static/assets/{dropDown-13564764.js → dropDown-025888df.js} +1 -1
  82. flowfile/web/static/assets/{fullEditor-fd2cd6f9.js → fullEditor-1df991ec.js} +2 -2
  83. flowfile/web/static/assets/{genericNodeSettings-71e11604.js → genericNodeSettings-d3b2b2ac.js} +3 -3
  84. flowfile/web/static/assets/{index-f6c15e76.js → index-d0518598.js} +210 -31
  85. flowfile/web/static/assets/{Output-48f81019.css → outputCsv-9cc59e0b.css} +0 -143
  86. flowfile/web/static/assets/outputCsv-d8457527.js +86 -0
  87. flowfile/web/static/assets/outputExcel-b41305c0.css +102 -0
  88. flowfile/web/static/assets/outputExcel-be89153e.js +56 -0
  89. flowfile/web/static/assets/outputParquet-cf8cf3f2.css +4 -0
  90. flowfile/web/static/assets/outputParquet-fabb445a.js +31 -0
  91. flowfile/web/static/assets/readCsv-bca3ed53.css +52 -0
  92. flowfile/web/static/assets/readCsv-e8359522.js +178 -0
  93. flowfile/web/static/assets/readExcel-dabaf51b.js +203 -0
  94. flowfile/web/static/assets/readExcel-e1b381ea.css +64 -0
  95. flowfile/web/static/assets/readParquet-cee068e2.css +19 -0
  96. flowfile/web/static/assets/readParquet-e0771ef2.js +26 -0
  97. flowfile/web/static/assets/{secretApi-dd636aa2.js → secretApi-ce823eee.js} +1 -1
  98. flowfile/web/static/assets/{selectDynamic-af36165e.js → selectDynamic-5476546e.js} +7 -7
  99. flowfile/web/static/assets/{selectDynamic-b062bc9b.css → selectDynamic-aa913ff4.css} +16 -16
  100. flowfile/web/static/assets/{vue-codemirror.esm-2847001e.js → vue-codemirror.esm-9ed00d50.js} +29 -33
  101. flowfile/web/static/assets/{vue-content-loader.es-0371da73.js → vue-content-loader.es-7bca2d9b.js} +1 -1
  102. flowfile/web/static/index.html +1 -1
  103. {flowfile-0.3.8.dist-info → flowfile-0.3.10.dist-info}/METADATA +2 -1
  104. {flowfile-0.3.8.dist-info → flowfile-0.3.10.dist-info}/RECORD +147 -117
  105. flowfile_core/configs/flow_logger.py +5 -13
  106. flowfile_core/configs/node_store/nodes.py +303 -44
  107. flowfile_core/configs/settings.py +6 -3
  108. flowfile_core/database/connection.py +5 -21
  109. flowfile_core/fileExplorer/funcs.py +239 -121
  110. flowfile_core/flowfile/code_generator/code_generator.py +36 -0
  111. flowfile_core/flowfile/flow_data_engine/flow_data_engine.py +60 -80
  112. flowfile_core/flowfile/flow_data_engine/flow_file_column/main.py +61 -0
  113. flowfile_core/flowfile/flow_data_engine/fuzzy_matching/prepare_for_fuzzy_match.py +44 -3
  114. flowfile_core/flowfile/flow_data_engine/subprocess_operations/models.py +3 -3
  115. flowfile_core/flowfile/flow_data_engine/subprocess_operations/subprocess_operations.py +33 -10
  116. flowfile_core/flowfile/flow_graph.py +223 -118
  117. flowfile_core/flowfile/flow_node/flow_node.py +56 -19
  118. flowfile_core/flowfile/flow_node/models.py +0 -2
  119. flowfile_core/flowfile/flow_node/schema_callback.py +138 -43
  120. flowfile_core/flowfile/graph_tree/graph_tree.py +250 -0
  121. flowfile_core/flowfile/graph_tree/models.py +15 -0
  122. flowfile_core/flowfile/handler.py +22 -3
  123. flowfile_core/flowfile/manage/compatibility_enhancements.py +1 -1
  124. flowfile_core/flowfile/{flow_data_engine/fuzzy_matching/settings_validator.py → schema_callbacks.py} +72 -16
  125. flowfile_core/flowfile/setting_generator/settings.py +2 -2
  126. flowfile_core/flowfile/util/execution_orderer.py +9 -0
  127. flowfile_core/flowfile/util/node_skipper.py +8 -0
  128. flowfile_core/main.py +4 -1
  129. flowfile_core/routes/routes.py +59 -10
  130. flowfile_core/schemas/input_schema.py +0 -1
  131. flowfile_core/schemas/output_model.py +5 -2
  132. flowfile_core/schemas/schemas.py +48 -3
  133. flowfile_core/schemas/transform_schema.py +28 -38
  134. flowfile_frame/__init__.py +1 -4
  135. flowfile_frame/flow_frame.py +33 -4
  136. flowfile_frame/flow_frame.pyi +2 -0
  137. flowfile_worker/__init__.py +6 -35
  138. flowfile_worker/funcs.py +7 -3
  139. flowfile_worker/main.py +5 -2
  140. flowfile_worker/models.py +3 -1
  141. flowfile_worker/routes.py +47 -5
  142. shared/__init__.py +15 -0
  143. shared/storage_config.py +243 -0
  144. flowfile/web/static/assets/GraphSolver-17fd26db.css +0 -68
  145. flowfile/web/static/assets/Pivot-f415e85f.css +0 -35
  146. flowfile/web/static/assets/Read-80dc1675.css +0 -197
  147. flowfile/web/static/assets/Read-c4059daf.js +0 -701
  148. flowfile/web/static/assets/RecordCount-c2b5e095.js +0 -122
  149. flowfile/web/static/assets/Union-f2aefdc9.js +0 -146
  150. flowfile/web/static/assets/Unpivot-246e9bbd.css +0 -77
  151. flowfile/web/static/assets/nodeTitle-988d9efe.js +0 -227
  152. flowfile/web/static/assets/nodeTitle-f4b12bcb.css +0 -134
  153. flowfile_worker/polars_fuzzy_match/matcher.py +0 -435
  154. flowfile_worker/polars_fuzzy_match/models.py +0 -36
  155. flowfile_worker/polars_fuzzy_match/pre_process.py +0 -213
  156. flowfile_worker/polars_fuzzy_match/process.py +0 -86
  157. flowfile_worker/polars_fuzzy_match/utils.py +0 -50
  158. {flowfile-0.3.8.dist-info → flowfile-0.3.10.dist-info}/LICENSE +0 -0
  159. {flowfile-0.3.8.dist-info → flowfile-0.3.10.dist-info}/WHEEL +0 -0
  160. {flowfile-0.3.8.dist-info → flowfile-0.3.10.dist-info}/entry_points.txt +0 -0
  161. {flowfile_worker/polars_fuzzy_match → flowfile_core/flowfile/graph_tree}/__init__.py +0 -0
flowfile_worker/routes.py CHANGED
@@ -17,13 +17,21 @@ from flowfile_worker.external_sources.sql_source.main import read_sql_source, wr
17
17
  router = APIRouter()
18
18
 
19
19
 
20
+ def create_and_get_default_cache_dir(flowfile_flow_id: int) -> str:
21
+ default_cache_dir = CACHE_DIR / str(flowfile_flow_id)
22
+ default_cache_dir.mkdir(parents=True, exist_ok=True)
23
+ return str(default_cache_dir)
24
+
25
+
20
26
  @router.post("/submit_query/")
21
27
  def submit_query(polars_script: models.PolarsScript, background_tasks: BackgroundTasks) -> models.Status:
22
28
  logger.info(f"Processing query with operation: {polars_script.operation_type}")
23
29
 
24
30
  try:
25
31
  polars_script.task_id = str(uuid.uuid4()) if polars_script.task_id is None else polars_script.task_id
26
- polars_script.cache_dir = polars_script.cache_dir if polars_script.cache_dir is not None else CACHE_DIR.name
32
+ default_cache_dir = create_and_get_default_cache_dir(polars_script.flowfile_flow_id)
33
+
34
+ polars_script.cache_dir = polars_script.cache_dir if polars_script.cache_dir is not None else default_cache_dir
27
35
  polars_serializable_object = polars_script.polars_serializable_object()
28
36
  file_path = os.path.join(polars_script.cache_dir, f"{polars_script.task_id}.arrow")
29
37
  result_type = "polars" if polars_script.operation_type == "store" else "other"
@@ -49,8 +57,9 @@ def store_sample(polars_script: models.PolarsScriptSample, background_tasks: Bac
49
57
  logger.info(f"Processing sample storage with size: {polars_script.sample_size}")
50
58
 
51
59
  try:
60
+ default_cache_dir = create_and_get_default_cache_dir(polars_script.flowfile_flow_id)
52
61
  polars_script.task_id = str(uuid.uuid4()) if polars_script.task_id is None else polars_script.task_id
53
- polars_script.cache_dir = polars_script.cache_dir if polars_script.cache_dir is not None else CACHE_DIR.name
62
+ polars_script.cache_dir = polars_script.cache_dir if polars_script.cache_dir is not None else default_cache_dir
54
63
  polars_serializable_object = polars_script.polars_serializable_object()
55
64
 
56
65
  file_path = os.path.join(polars_script.cache_dir, f"{polars_script.task_id}.arrow")
@@ -210,7 +219,8 @@ def store_sql_db_result(database_read_settings: DatabaseReadSettings, background
210
219
 
211
220
  try:
212
221
  task_id = str(uuid.uuid4())
213
- file_path = os.path.join(CACHE_DIR.name, f"{task_id}.arrow")
222
+ file_path = os.path.join(create_and_get_default_cache_dir(database_read_settings.flowfile_flow_id),
223
+ f"{task_id}.arrow")
214
224
  status = models.Status(background_task_id=task_id, status="Starting", file_ref=file_path,
215
225
  result_type="polars")
216
226
  status_dict[task_id] = status
@@ -246,7 +256,7 @@ def create_table(file_type: FileType, received_table: Dict, background_tasks: Ba
246
256
 
247
257
  try:
248
258
  task_id = str(uuid.uuid4())
249
- file_ref = os.path.join(CACHE_DIR.name, f"{task_id}.arrow")
259
+ file_ref = os.path.join(create_and_get_default_cache_dir(flowfile_flow_id), f"{task_id}.arrow")
250
260
 
251
261
  status = models.Status(background_task_id=task_id, status="Starting", file_ref=file_ref,
252
262
  result_type="polars")
@@ -382,8 +392,9 @@ async def add_fuzzy_join(polars_script: models.FuzzyJoinInput, background_tasks:
382
392
  """
383
393
  logger.info("Starting fuzzy join operation")
384
394
  try:
395
+ default_cache_dir = create_and_get_default_cache_dir(polars_script.flowfile_flow_id)
385
396
  polars_script.task_id = str(uuid.uuid4()) if polars_script.task_id is None else polars_script.task_id
386
- polars_script.cache_dir = polars_script.cache_dir if polars_script.cache_dir is not None else CACHE_DIR.name
397
+ polars_script.cache_dir = polars_script.cache_dir if polars_script.cache_dir is not None else default_cache_dir
387
398
  left_serializable_object = polars_script.left_df_operation.polars_serializable_object()
388
399
  right_serializable_object = polars_script.right_df_operation.polars_serializable_object()
389
400
 
@@ -405,6 +416,37 @@ async def add_fuzzy_join(polars_script: models.FuzzyJoinInput, background_tasks:
405
416
  raise HTTPException(status_code=500, detail=str(e))
406
417
 
407
418
 
419
+ @router.delete("/clear_task/{task_id}")
420
+ def clear_task(task_id: str):
421
+ """
422
+ Clear task data and status by ID.
423
+
424
+ Args:
425
+ task_id: Unique identifier of the task to clear
426
+ Returns:
427
+ dict: Success message
428
+ Raises:
429
+ HTTPException: If task not found
430
+ """
431
+
432
+ logger.info(f"Clearing task: {task_id}")
433
+ status = status_dict.get(task_id)
434
+ if not status:
435
+ logger.warning(f"Task not found for clearing: {task_id}")
436
+ raise HTTPException(status_code=404, detail="Task not found")
437
+ try:
438
+ if os.path.exists(status.file_ref):
439
+ os.remove(status.file_ref)
440
+ logger.debug(f"Removed file: {status.file_ref}")
441
+ except Exception as e:
442
+ logger.error(f"Error removing file {status.file_ref}: {str(e)}", exc_info=True)
443
+ with status_dict_lock:
444
+ status_dict.pop(task_id, None)
445
+ PROCESS_MEMORY_USAGE.pop(task_id, None)
446
+ logger.info(f"Successfully cleared task: {task_id}")
447
+ return {"message": f"Task {task_id} has been cleared."}
448
+
449
+
408
450
  @router.post("/cancel_task/{task_id}")
409
451
  def cancel_task(task_id: str):
410
452
  """Cancel a running task by ID.
shared/__init__.py ADDED
@@ -0,0 +1,15 @@
1
+ """
2
+ Shared utilities for Flowfile services.
3
+ This package contains common functionality that can be used across
4
+ flowfile_core, flowfile_worker, and other components without creating
5
+ circular dependencies.
6
+ """
7
+
8
+ from .storage_config import storage, get_cache_directory, get_temp_directory, get_flows_directory
9
+
10
+ __all__ = [
11
+ 'storage',
12
+ 'get_cache_directory',
13
+ 'get_temp_directory',
14
+ 'get_flows_directory'
15
+ ]
@@ -0,0 +1,243 @@
1
+ # shared/storage_config.py - Updated for Option 3
2
+ """
3
+ Centralized storage configuration for Flowfile.
4
+ This module can be imported by both core and worker without creating dependencies.
5
+ """
6
+ import os
7
+ from pathlib import Path
8
+ from typing import Optional, Literal
9
+
10
+ DirectoryOptions = Literal["temp_directory", "logs_directory",
11
+ "system_logs_directory", "database_directory",
12
+ "cache_directory", "flows_directory"]
13
+
14
+
15
+ class FlowfileStorage:
16
+ """Centralized storage manager for Flowfile applications."""
17
+
18
+ def __init__(self):
19
+ self._base_dir: Optional[Path] = None
20
+ self._user_data_dir: Optional[Path] = None
21
+ self._ensure_directories()
22
+
23
+ @property
24
+ def base_directory(self) -> Path:
25
+ """Get the base Flowfile storage directory (for internal container communication)."""
26
+ if self._base_dir is None:
27
+ if os.environ.get("RUNNING_IN_DOCKER") == "true":
28
+ # In Docker, internal storage stays inside /app
29
+ base_path = os.environ.get("FLOWFILE_STORAGE_DIR", "/app/internal_storage")
30
+ else:
31
+ # Local development
32
+ base_path = os.environ.get("FLOWFILE_STORAGE_DIR")
33
+ if not base_path:
34
+ home_dir = Path.home()
35
+ base_path = home_dir / ".flowfile"
36
+
37
+ self._base_dir = Path(base_path)
38
+ return self._base_dir
39
+
40
+ @property
41
+ def user_data_directory(self) -> Path:
42
+ """Get the user data directory (completely separate from application code)."""
43
+ if self._user_data_dir is None:
44
+ if os.environ.get("RUNNING_IN_DOCKER") == "true":
45
+ # In Docker, user data is at /data/user (completely outside /app)
46
+ user_data_path = os.environ.get("FLOWFILE_USER_DATA_DIR", "/data/user")
47
+ else:
48
+ # Local development - use user's home directory
49
+ user_data_path = Path.home()
50
+
51
+ self._user_data_dir = Path(user_data_path)
52
+ return self._user_data_dir
53
+
54
+ @property
55
+ def cache_directory(self) -> Path:
56
+ """Cache directory for worker-core communication (internal)."""
57
+ return self.base_directory / "cache"
58
+
59
+ def get_flow_cache_directory(self, flow_id: int) -> Path:
60
+ """Get or create a cache directory for a specific flow (internal)."""
61
+ flow_cache_dir = self.cache_directory / str(flow_id)
62
+ flow_cache_dir.mkdir(parents=True, exist_ok=True)
63
+ return flow_cache_dir
64
+
65
+ @property
66
+ def system_logs_directory(self) -> Path:
67
+ """Directory for system logs (internal)."""
68
+ return self.base_directory / "system_logs"
69
+
70
+ @property
71
+ def flows_directory(self) -> Path:
72
+ """Directory for flow storage (user-accessible)."""
73
+ if os.environ.get("RUNNING_IN_DOCKER") == "true":
74
+ # In Docker, flows are in separate user data area
75
+ return self.user_data_directory / "flows"
76
+ else:
77
+ # Local development - flows in ~/.flowfile/flows
78
+ return self.base_directory / "flows"
79
+
80
+ @property
81
+ def uploads_directory(self) -> Path:
82
+ """Directory for user uploads (user-accessible)."""
83
+ if os.environ.get("RUNNING_IN_DOCKER") == "true":
84
+ # In Docker, uploads are in separate user data area
85
+ return self.user_data_directory / "uploads"
86
+ else:
87
+ # Local development - uploads in ~/.flowfile/uploads
88
+ return self.base_directory / "uploads"
89
+
90
+ @property
91
+ def outputs_directory(self) -> Path:
92
+ """Directory for user outputs (user-accessible)."""
93
+ if os.environ.get("RUNNING_IN_DOCKER") == "true":
94
+ # In Docker, outputs are in separate user data area
95
+ return self.user_data_directory / "outputs"
96
+ else:
97
+ # Local development - outputs in ~/.flowfile/outputs
98
+ return self.base_directory / "outputs"
99
+
100
+ @property
101
+ def database_directory(self) -> Path:
102
+ """Directory for local database files (internal)."""
103
+ return self.base_directory / "database"
104
+
105
+ @property
106
+ def logs_directory(self) -> Path:
107
+ """Directory for application logs (internal)."""
108
+ return self.base_directory / "logs"
109
+
110
+ @property
111
+ def temp_directory(self) -> Path:
112
+ """Directory for temporary files (internal)."""
113
+ return self.base_directory / "temp"
114
+
115
+ @property
116
+ def temp_directory_for_flows(self) -> Path:
117
+ """Directory for temporary files specific to flows (internal)."""
118
+ return self.temp_directory / "flows"
119
+
120
+ def _ensure_directories(self) -> None:
121
+ """Create all necessary directories if they don't exist."""
122
+ # Internal directories (always created in base_directory)
123
+ internal_directories = [
124
+ self.cache_directory,
125
+ self.database_directory,
126
+ self.logs_directory,
127
+ self.temp_directory,
128
+ self.system_logs_directory,
129
+ self.temp_directory_for_flows,
130
+ ]
131
+
132
+ # User-accessible directories (location depends on environment)
133
+ user_directories = [
134
+ self.flows_directory,
135
+ self.uploads_directory,
136
+ self.outputs_directory,
137
+ ]
138
+
139
+ for directory in internal_directories + user_directories:
140
+ directory.mkdir(parents=True, exist_ok=True)
141
+
142
+ def get_cache_file_path(self, filename: str) -> Path:
143
+ """Get full path for a cache file (internal)."""
144
+ return self.cache_directory / filename
145
+
146
+ def get_flow_file_path(self, filename: str) -> Path:
147
+ """Get full path for a flow file (user-accessible)."""
148
+ return self.flows_directory / filename
149
+
150
+ def get_upload_file_path(self, filename: str) -> Path:
151
+ """Get full path for an uploaded file (user-accessible)."""
152
+ return self.uploads_directory / filename
153
+
154
+ def get_output_file_path(self, filename: str) -> Path:
155
+ """Get full path for an output file (user-accessible)."""
156
+ return self.outputs_directory / filename
157
+
158
+ def get_log_file_path(self, filename: str) -> Path:
159
+ """Get full path for an application log file (internal)."""
160
+ return self.logs_directory / filename
161
+
162
+ def get_system_log_file_path(self, filename: str) -> Path:
163
+ """Get full path for a system log file (internal)."""
164
+ return self.system_logs_directory / filename
165
+
166
+ def get_temp_file_path(self, filename: str) -> Path:
167
+ """Get full path for a temporary file (internal)."""
168
+ return self.temp_directory / filename
169
+
170
+ def cleanup_directory(self, directory_option: DirectoryOptions, storage_duration_hours: int = 24) -> None:
171
+ """Clean up any directory of the folder"""
172
+ import time
173
+ import shutil
174
+
175
+ if not hasattr(self, directory_option):
176
+ raise Exception(f"Directory does not exist in {self.base_directory}")
177
+
178
+ directory = getattr(self, directory_option)
179
+ if not isinstance(directory, Path):
180
+ raise Exception(f"Directory attribute {directory_option} is not a Path object")
181
+
182
+ if not directory.exists():
183
+ return
184
+
185
+ current_time = time.time()
186
+ cutoff_time = current_time - (storage_duration_hours * 60 * 60)
187
+
188
+ for item in directory.iterdir():
189
+ try:
190
+ if item.stat().st_mtime < cutoff_time:
191
+ if item.is_file():
192
+ item.unlink()
193
+ elif item.is_dir():
194
+ shutil.rmtree(item)
195
+ except (OSError, FileNotFoundError):
196
+ # Handle permission errors or files that disappeared
197
+ continue
198
+
199
+ def cleanup_directories(self) -> None:
200
+ """Clean up temporary files older than specified hours."""
201
+ self.cleanup_directory("temp_directory", storage_duration_hours=24)
202
+ self.cleanup_directory("cache_directory", storage_duration_hours=1)
203
+ self.cleanup_directory("logs_directory", storage_duration_hours=168)
204
+ self.cleanup_directory("system_logs_directory", storage_duration_hours=168)
205
+
206
+
207
+ storage = FlowfileStorage()
208
+
209
+
210
+ # Convenience functions for backward compatibility
211
+ def get_cache_directory() -> str:
212
+ """Get cache directory path as string."""
213
+ return str(storage.cache_directory)
214
+
215
+
216
+ def get_temp_directory() -> str:
217
+ """Get temp directory path as string."""
218
+ return str(storage.temp_directory)
219
+
220
+
221
+ def get_flows_directory() -> str:
222
+ """Get flows directory path as string."""
223
+ return str(storage.flows_directory)
224
+
225
+
226
+ def get_uploads_directory() -> str:
227
+ """Get uploads directory path as string."""
228
+ return str(storage.uploads_directory)
229
+
230
+
231
+ def get_outputs_directory() -> str:
232
+ """Get outputs directory path as string."""
233
+ return str(storage.outputs_directory)
234
+
235
+
236
+ def get_logs_directory() -> str:
237
+ """Get application logs directory path as string."""
238
+ return str(storage.logs_directory)
239
+
240
+
241
+ def get_system_logs_directory() -> str:
242
+ """Get system logs directory path as string."""
243
+ return str(storage.system_logs_directory)
@@ -1,68 +0,0 @@
1
-
2
- .context-menu[data-v-d0286fd2] {
3
- position: fixed;
4
- z-index: 1000;
5
- border: 1px solid #ccc;
6
- background-color: white;
7
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
8
- border-radius: 4px;
9
- user-select: none;
10
- }
11
- .context-menu ul[data-v-d0286fd2] {
12
- list-style: none;
13
- padding: 0;
14
- margin: 0;
15
- }
16
- .context-menu li[data-v-d0286fd2] {
17
- padding: 8px 16px;
18
- cursor: pointer;
19
- }
20
- .context-menu li.disabled[data-v-d0286fd2] {
21
- color: #ccc;
22
- cursor: not-allowed;
23
- }
24
- .context-menu li[data-v-d0286fd2]:hover:not(.disabled) {
25
- background-color: #f0f0f0;
26
- }
27
-
28
- .items-container[data-v-43acf78a] {
29
- display: flex;
30
- gap: 10px; /* Space between items */
31
- }
32
- .item-box[data-v-43acf78a] {
33
- display: flex;
34
- align-items: center;
35
- padding: 5px 10px;
36
- background-color: #f0f0f0;
37
- border-radius: 4px;
38
- font-size: 12px; /* Font size set to 12px */
39
- position: relative;
40
- }
41
- .remove-btn[data-v-43acf78a] {
42
- margin-left: 8px;
43
- cursor: pointer;
44
- color: #100f0f72;
45
- font-weight: bold;
46
- }
47
-
48
- .context-menu[data-v-5a467b52] {
49
- position: fixed;
50
- z-index: 1000;
51
- border: 1px solid #ccc;
52
- background-color: white;
53
- padding: 8px;
54
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
55
- border-radius: 4px;
56
- }
57
- .context-menu ul[data-v-5a467b52] {
58
- list-style: none;
59
- padding: 0;
60
- margin: 0;
61
- }
62
- .context-menu li[data-v-5a467b52] {
63
- padding: 8px 16px;
64
- cursor: pointer;
65
- }
66
- .context-menu li[data-v-5a467b52]:hover {
67
- background-color: #f0f0f0;
68
- }
@@ -1,35 +0,0 @@
1
-
2
- .validation-wrapper[data-v-3eb585b2] {
3
- background-color: #ffffff;
4
- }
5
- .error-message[data-v-3eb585b2] {
6
- color: #991b1b;
7
- margin: 5px 0;
8
- font-size: 12px;
9
- }
10
- .warning-icon[data-v-3eb585b2] {
11
- cursor: pointer;
12
- font-size: 24px;
13
- }
14
-
15
- .context-menu[data-v-d0f14439] {
16
- position: fixed;
17
- z-index: 1000;
18
- border: 1px solid #ccc;
19
- background-color: white;
20
- padding: 8px;
21
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
22
- border-radius: 4px;
23
- }
24
- .context-menu ul[data-v-d0f14439] {
25
- list-style: none;
26
- padding: 0;
27
- margin: 0;
28
- }
29
- .context-menu li[data-v-d0f14439] {
30
- padding: 8px 16px;
31
- cursor: pointer;
32
- }
33
- .context-menu li[data-v-d0f14439]:hover {
34
- background-color: #f0f0f0;
35
- }
@@ -1,197 +0,0 @@
1
-
2
- .selectors[data-v-e7395814] {
3
- display: flex;
4
- flex-direction: column;
5
- gap: 12px;
6
- }
7
- .input-wrapper[data-v-e7395814] {
8
- display: flex;
9
- flex-direction: column;
10
- gap: 4px;
11
- flex: 1;
12
- }
13
- label[data-v-e7395814] {
14
- font-weight: 500;
15
- color: #333;
16
- font-size: 14px;
17
- }
18
- input[data-v-e7395814] {
19
- padding: 6px;
20
- border: 1px solid #ccc;
21
- border-radius: 4px;
22
- font-size: 14px;
23
- width: 99%;
24
- }
25
- .row[data-v-e7395814] {
26
- display: flex;
27
- align-items: center;
28
- justify-content: space-between;
29
- gap: 16px;
30
- }
31
- .compact-input[data-v-e7395814] {
32
- width: 96%;
33
- padding: 6px;
34
- font-size: 14px;
35
- border: 1px solid #ccc;
36
- border-radius: 4px;
37
- }
38
- .button-container[data-v-e7395814] {
39
- display: flex;
40
- justify-content: center;
41
- margin: 16px 0;
42
- }
43
- .optional-section[data-v-e7395814] {
44
- margin-top: 20px;
45
- }
46
- .section-divider[data-v-e7395814] {
47
- margin: 16px 0;
48
- border: none;
49
- border-top: 1px solid #ddd;
50
- }
51
- .table-sizes[data-v-e7395814] {
52
- font-weight: bold;
53
- margin-bottom: 10px;
54
- }
55
- .warning-sign[data-v-e7395814] {
56
- color: #e74c3c;
57
- font-size: 16px;
58
- margin-left: 8px;
59
- }
60
- @media (max-width: 600px) {
61
- .row[data-v-e7395814] {
62
- flex-direction: column;
63
- }
64
- }
65
-
66
- [data-v-d0b76f7b] {
67
- box-sizing: border-box;
68
- }
69
- .csv-table-settings[data-v-d0b76f7b] {
70
- background: #fff;
71
- border-radius: 6px;
72
- padding: 16px;
73
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
74
- margin: 20px auto;
75
- max-width: 600px;
76
- overflow-x: hidden; /* Prevent horizontal scroll */
77
- }
78
- .row[data-v-d0b76f7b] {
79
- display: flex;
80
- align-items: center;
81
- justify-content: space-between;
82
- margin-bottom: 12px;
83
- gap: 20px;
84
- max-width: 100%; /* Prevent overflow */
85
- }
86
- label[data-v-d0b76f7b] {
87
- font-weight: 500;
88
- color: #333;
89
- font-size: 14px;
90
- flex: 1;
91
- max-width: 100%;
92
- }
93
- .el-select[data-v-d0b76f7b],
94
- .el-slider[data-v-d0b76f7b],
95
- .el-checkbox[data-v-d0b76f7b] {
96
- flex: 2;
97
- max-width: 100%; /* Prevent overflow */
98
- }
99
- .el-slider[data-v-d0b76f7b] {
100
- padding-top: 10px;
101
- }
102
- .el-checkbox[data-v-d0b76f7b] {
103
- display: flex;
104
- align-items: center;
105
- }
106
- @media (max-width: 600px) {
107
- .row[data-v-d0b76f7b] {
108
- flex-direction: column;
109
- align-items: flex-start;
110
- }
111
- .el-select[data-v-d0b76f7b],
112
- .el-slider[data-v-d0b76f7b],
113
- .el-checkbox[data-v-d0b76f7b] {
114
- width: 100%;
115
- }
116
- }
117
-
118
- .parquet-table-settings[data-v-0faf0508] {
119
- background: #f9fafb;
120
- border-radius: 8px;
121
- padding: 20px;
122
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
123
- max-width: 600px;
124
- margin: 20px auto;
125
- text-align: center; /* Center the text */
126
- }
127
- .message h2[data-v-0faf0508] {
128
- font-size: 24px;
129
- color: #3498db;
130
- margin-bottom: 10px;
131
- }
132
- .message p[data-v-0faf0508] {
133
- font-size: 16px;
134
- color: #333;
135
- }
136
-
137
- .context-menu[data-v-221ad292] {
138
- position: fixed;
139
- z-index: 1000;
140
- border: 1px solid #ccc;
141
- background-color: white;
142
- padding: 8px;
143
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
144
- border-radius: 4px;
145
- }
146
- .context-menu button[data-v-221ad292] {
147
- display: block;
148
- background: none;
149
- border: none;
150
- padding: 4px 8px;
151
- text-align: left;
152
- width: 100%;
153
- cursor: pointer;
154
- }
155
- .context-menu button[data-v-221ad292]:hover {
156
- background-color: #f0f0f0;
157
- }
158
- .file-upload-wrapper[data-v-221ad292] {
159
- position: relative;
160
- width: 100%;
161
- }
162
- .file-upload-label[data-v-221ad292] {
163
- display: flex;
164
- align-items: center;
165
- background-color: #f5f5f5;
166
- border: 1px solid #ddd;
167
- border-radius: 4px;
168
- padding: 10px 15px;
169
- color: #333;
170
- font-size: 16px;
171
- font-weight: 500;
172
- cursor: pointer;
173
- transition: background-color 0.3s ease;
174
- }
175
- .file-upload-label[data-v-221ad292]:hover {
176
- background-color: #e4e4e4;
177
- }
178
- .file-icon[data-v-221ad292] {
179
- margin-right: 10px;
180
- font-size: 20px;
181
- }
182
- .file-label-text[data-v-221ad292] {
183
- flex-grow: 1;
184
- margin-left: 10px;
185
- }
186
- input[type="text"][data-v-221ad292] {
187
- width: 100%;
188
- padding: 8px;
189
- border: 1px solid #ddd;
190
- border-radius: 4px;
191
- box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
192
- transition: border-color 0.2s ease;
193
- }
194
- input[type="text"][data-v-221ad292]:focus {
195
- border-color: #3498db;
196
- outline: none;
197
- }