Flowfile 0.5.1__py3-none-any.whl → 0.5.3__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.
Files changed (329) hide show
  1. build_backends/main.py +25 -22
  2. build_backends/main_prd.py +10 -19
  3. flowfile/__init__.py +178 -74
  4. flowfile/__main__.py +10 -7
  5. flowfile/api.py +51 -57
  6. flowfile/web/__init__.py +14 -9
  7. flowfile/web/static/assets/AdminView-49392a9a.js +713 -0
  8. flowfile/web/static/assets/AdminView-f53bad23.css +129 -0
  9. flowfile/web/static/assets/CloudConnectionView-36bcd6df.css +72 -0
  10. flowfile/web/static/assets/{CloudConnectionManager-0dfba9f2.js → CloudConnectionView-f13f202b.js} +11 -11
  11. flowfile/web/static/assets/{CloudStorageReader-d5b1b6c9.js → CloudStorageReader-0023d4a5.js} +10 -8
  12. flowfile/web/static/assets/{CloudStorageReader-29d14fcc.css → CloudStorageReader-24c54524.css} +27 -27
  13. flowfile/web/static/assets/{CloudStorageWriter-b0ee067f.css → CloudStorageWriter-60547855.css} +26 -26
  14. flowfile/web/static/assets/{CloudStorageWriter-00d87aad.js → CloudStorageWriter-8e781e11.js} +10 -8
  15. flowfile/web/static/assets/{ColumnSelector-47996a16.css → ColumnSelector-371637fb.css} +2 -2
  16. flowfile/web/static/assets/{ColumnSelector-4685e75d.js → ColumnSelector-8ad68ea9.js} +3 -5
  17. flowfile/web/static/assets/{ContextMenu-c13f91d0.css → ContextMenu-26d4dd27.css} +6 -6
  18. flowfile/web/static/assets/{ContextMenu-23e909da.js → ContextMenu-31ee57f0.js} +3 -3
  19. flowfile/web/static/assets/{ContextMenu-70ae0c79.js → ContextMenu-69a74055.js} +3 -3
  20. flowfile/web/static/assets/{ContextMenu-f149cf7c.js → ContextMenu-8e2051c6.js} +3 -3
  21. flowfile/web/static/assets/{ContextMenu-4c74eef1.css → ContextMenu-8ec1729e.css} +6 -6
  22. flowfile/web/static/assets/{ContextMenu-63cfa99b.css → ContextMenu-9b310c60.css} +6 -6
  23. flowfile/web/static/assets/{CrossJoin-702a3edd.js → CrossJoin-03df6938.js} +12 -10
  24. flowfile/web/static/assets/{CrossJoin-1119d18e.css → CrossJoin-71b4cc10.css} +20 -20
  25. flowfile/web/static/assets/CustomNode-59e99a86.css +32 -0
  26. flowfile/web/static/assets/{CustomNode-b1519993.js → CustomNode-8479239b.js} +36 -24
  27. flowfile/web/static/assets/{DatabaseConnectionSettings-6f3e4ea5.js → DatabaseConnectionSettings-869e3efd.js} +5 -4
  28. flowfile/web/static/assets/{DatabaseConnectionSettings-0c04b2e5.css → DatabaseConnectionSettings-e91df89a.css} +13 -13
  29. flowfile/web/static/assets/{DatabaseReader-ae61773c.css → DatabaseReader-36898a00.css} +24 -24
  30. flowfile/web/static/assets/{DatabaseReader-d38c7295.js → DatabaseReader-c58b9552.js} +25 -15
  31. flowfile/web/static/assets/DatabaseView-6655afd6.css +57 -0
  32. flowfile/web/static/assets/{DatabaseManager-cf5ef661.js → DatabaseView-d26a9140.js} +11 -11
  33. flowfile/web/static/assets/{DatabaseWriter-2f570e53.css → DatabaseWriter-217a99f1.css} +19 -19
  34. flowfile/web/static/assets/{DatabaseWriter-b04ef46a.js → DatabaseWriter-4d05ddc7.js} +17 -10
  35. flowfile/web/static/assets/{designer-8da3ba3a.css → DesignerView-a6d0ee84.css} +614 -546
  36. flowfile/web/static/assets/{designer-9633482a.js → DesignerView-e6f5c0e8.js} +1107 -3170
  37. flowfile/web/static/assets/{documentation-ca400224.js → DocumentationView-2e78ef1b.js} +5 -5
  38. flowfile/web/static/assets/{documentation-12216a74.css → DocumentationView-fd46c656.css} +7 -7
  39. flowfile/web/static/assets/{ExploreData-2d0cf4db.css → ExploreData-10c5acc8.css} +13 -12
  40. flowfile/web/static/assets/{ExploreData-5fa10ed8.js → ExploreData-7b54caca.js} +18 -9
  41. flowfile/web/static/assets/{ExternalSource-d39af878.js → ExternalSource-3fa399b2.js} +9 -7
  42. flowfile/web/static/assets/{ExternalSource-e37b6275.css → ExternalSource-47ab05a3.css} +17 -17
  43. flowfile/web/static/assets/Filter-7494ea97.css +48 -0
  44. flowfile/web/static/assets/Filter-8cbbdbf3.js +287 -0
  45. flowfile/web/static/assets/{Formula-bb96803d.css → Formula-53d58c43.css} +7 -7
  46. flowfile/web/static/assets/{Formula-6b04fb1d.js → Formula-aac42b1e.js} +13 -11
  47. flowfile/web/static/assets/{FuzzyMatch-1010f966.css → FuzzyMatch-ad6361d6.css} +68 -69
  48. flowfile/web/static/assets/{FuzzyMatch-999521f4.js → FuzzyMatch-cd9bbfca.js} +12 -10
  49. flowfile/web/static/assets/{Pivot-cf333e3d.css → GraphSolver-c24dec17.css} +5 -5
  50. flowfile/web/static/assets/{GraphSolver-17dd2198.js → GraphSolver-c7e6780e.js} +13 -11
  51. flowfile/web/static/assets/{GroupBy-6b039e18.js → GroupBy-93c5d22b.js} +9 -7
  52. flowfile/web/static/assets/{GroupBy-b9505323.css → GroupBy-be7ac0bf.css} +10 -10
  53. flowfile/web/static/assets/{Join-fd79b451.css → Join-28b5e18f.css} +22 -22
  54. flowfile/web/static/assets/{Join-24d0f113.js → Join-a19b2de2.js} +13 -11
  55. flowfile/web/static/assets/LoginView-0df4ed0a.js +134 -0
  56. flowfile/web/static/assets/LoginView-d325d632.css +172 -0
  57. flowfile/web/static/assets/ManualInput-3702e677.css +293 -0
  58. flowfile/web/static/assets/{ManualInput-34639209.js → ManualInput-8d3374b2.js} +170 -116
  59. flowfile/web/static/assets/{MultiSelect-0e8724a3.js → MultiSelect-ad1b6243.js} +2 -2
  60. flowfile/web/static/assets/{MultiSelect.vue_vue_type_script_setup_true_lang-b0e538c2.js → MultiSelect.vue_vue_type_script_setup_true_lang-e278950d.js} +1 -1
  61. flowfile/web/static/assets/NodeDesigner-40b647c9.js +2610 -0
  62. flowfile/web/static/assets/NodeDesigner-5f53be3f.css +1429 -0
  63. flowfile/web/static/assets/{NumericInput-3d63a470.js → NumericInput-7100234c.js} +2 -2
  64. flowfile/web/static/assets/{NumericInput.vue_vue_type_script_setup_true_lang-e0edeccc.js → NumericInput.vue_vue_type_script_setup_true_lang-5130219f.js} +5 -2
  65. flowfile/web/static/assets/{Output-283fe388.css → Output-35e97000.css} +6 -6
  66. flowfile/web/static/assets/{Output-edea9802.js → Output-f5efd2aa.js} +12 -9
  67. flowfile/web/static/assets/{GraphSolver-f0cb7bfb.css → Pivot-0eda81b4.css} +5 -5
  68. flowfile/web/static/assets/{Pivot-61d19301.js → Pivot-d981d23c.js} +11 -9
  69. flowfile/web/static/assets/PivotValidation-0e905b1a.css +13 -0
  70. flowfile/web/static/assets/{PivotValidation-f97fec5b.js → PivotValidation-39386e95.js} +3 -3
  71. flowfile/web/static/assets/PivotValidation-41b57ad6.css +13 -0
  72. flowfile/web/static/assets/{PivotValidation-de9f43fe.js → PivotValidation-63de1f73.js} +3 -3
  73. flowfile/web/static/assets/{PolarsCode-650322d1.css → PolarsCode-2b1f1f23.css} +4 -4
  74. flowfile/web/static/assets/{PolarsCode-bc3c9984.js → PolarsCode-f9d69217.js} +18 -9
  75. flowfile/web/static/assets/PopOver-b22f049e.js +939 -0
  76. flowfile/web/static/assets/PopOver-d96599db.css +33 -0
  77. flowfile/web/static/assets/{Read-e808b239.css → Read-36e7bd51.css} +12 -12
  78. flowfile/web/static/assets/{Read-64a3f259.js → Read-aec2e377.js} +14 -11
  79. flowfile/web/static/assets/{RecordCount-3d5039be.js → RecordCount-78ed6845.js} +6 -4
  80. flowfile/web/static/assets/{RecordId-597510e0.js → RecordId-2156e890.js} +8 -6
  81. flowfile/web/static/assets/{SQLQueryComponent-36cef432.css → SQLQueryComponent-1c2f26b4.css} +5 -5
  82. flowfile/web/static/assets/{SQLQueryComponent-df51adbe.js → SQLQueryComponent-48c72f5b.js} +3 -3
  83. flowfile/web/static/assets/{Sample-4be0a507.js → Sample-1352ca74.js} +6 -4
  84. flowfile/web/static/assets/SecretSelector-22b5ff89.js +113 -0
  85. flowfile/web/static/assets/SecretSelector-6329f743.css +43 -0
  86. flowfile/web/static/assets/{SecretManager-4839be57.js → SecretsView-17df66ee.js} +35 -36
  87. flowfile/web/static/assets/SecretsView-aa291340.css +38 -0
  88. flowfile/web/static/assets/{Select-9b72f201.js → Select-0aee4c54.js} +9 -7
  89. flowfile/web/static/assets/{SettingsSection-f0f75a42.js → SettingsSection-0784e157.js} +3 -3
  90. flowfile/web/static/assets/{SettingsSection-71e6b7e3.css → SettingsSection-07fbbc39.css} +4 -4
  91. flowfile/web/static/assets/{SettingsSection-5c696bee.css → SettingsSection-26fe48d4.css} +4 -4
  92. flowfile/web/static/assets/{SettingsSection-2e4d03c4.css → SettingsSection-8f980839.css} +4 -4
  93. flowfile/web/static/assets/{SettingsSection-e1e9c953.js → SettingsSection-cd341bb6.js} +3 -3
  94. flowfile/web/static/assets/{SettingsSection-7ded385d.js → SettingsSection-f2002a6d.js} +3 -3
  95. flowfile/web/static/assets/{SingleSelect-6c777aac.js → SingleSelect-460cc0ea.js} +2 -2
  96. flowfile/web/static/assets/{SingleSelect.vue_vue_type_script_setup_true_lang-33e3ff9b.js → SingleSelect.vue_vue_type_script_setup_true_lang-30741bb2.js} +1 -1
  97. flowfile/web/static/assets/{SliderInput-7cb93e62.js → SliderInput-5d926864.js} +7 -4
  98. flowfile/web/static/assets/SliderInput-f2e4f23c.css +4 -0
  99. flowfile/web/static/assets/{Sort-6cbde21a.js → Sort-3cdc971b.js} +9 -7
  100. flowfile/web/static/assets/{Unique-f9fb0809.css → Sort-8a871341.css} +10 -10
  101. flowfile/web/static/assets/{TextInput-d9a40c11.js → TextInput-a2d0bfbd.js} +2 -2
  102. flowfile/web/static/assets/{TextInput.vue_vue_type_script_setup_true_lang-5896c375.js → TextInput.vue_vue_type_script_setup_true_lang-abad1ca2.js} +5 -2
  103. flowfile/web/static/assets/{TextToRows-5d2c1190.css → TextToRows-12afb4f4.css} +10 -10
  104. flowfile/web/static/assets/{TextToRows-c4fcbf4d.js → TextToRows-918945f7.js} +11 -10
  105. flowfile/web/static/assets/{ToggleSwitch-4ef91d19.js → ToggleSwitch-f0ef5196.js} +2 -2
  106. flowfile/web/static/assets/{ToggleSwitch.vue_vue_type_script_setup_true_lang-38478c20.js → ToggleSwitch.vue_vue_type_script_setup_true_lang-5605c793.js} +1 -1
  107. flowfile/web/static/assets/{UnavailableFields-5edd5322.css → UnavailableFields-54d2f518.css} +6 -6
  108. flowfile/web/static/assets/{UnavailableFields-a03f512c.js → UnavailableFields-bdad6144.js} +4 -4
  109. flowfile/web/static/assets/{Union-af6c3d9b.css → Union-d6a8d7d5.css} +7 -7
  110. flowfile/web/static/assets/{Union-bfe9b996.js → Union-e8ab8c86.js} +8 -6
  111. flowfile/web/static/assets/{Unique-5d023a27.js → Unique-8cd4f976.js} +13 -10
  112. flowfile/web/static/assets/{Sort-3643d625.css → Unique-9fb2f567.css} +10 -10
  113. flowfile/web/static/assets/{Unpivot-1e422df3.css → Unpivot-710a2948.css} +7 -7
  114. flowfile/web/static/assets/{Unpivot-91cc5354.js → Unpivot-8da14095.js} +10 -8
  115. flowfile/web/static/assets/{UnpivotValidation-7ee2de44.js → UnpivotValidation-6f7d89ff.js} +3 -3
  116. flowfile/web/static/assets/UnpivotValidation-d5ca3b7b.css +13 -0
  117. flowfile/web/static/assets/{VueGraphicWalker-e51b9924.js → VueGraphicWalker-3fb312e1.js} +4 -4
  118. flowfile/web/static/assets/{VueGraphicWalker-ed5ab88b.css → VueGraphicWalker-430f0b86.css} +1 -1
  119. flowfile/web/static/assets/{api-cf1221f0.js → api-24483f0d.js} +1 -1
  120. flowfile/web/static/assets/{api-c1bad5ca.js → api-8b81fa73.js} +1 -1
  121. flowfile/web/static/assets/{dropDown-35135ba8.css → dropDown-3d8dc5fa.css} +40 -40
  122. flowfile/web/static/assets/{dropDown-614b998d.js → dropDown-ac0fda9d.js} +3 -3
  123. flowfile/web/static/assets/{fullEditor-f7971590.js → fullEditor-5497a84a.js} +11 -10
  124. flowfile/web/static/assets/{fullEditor-178376bb.css → fullEditor-a0be62b3.css} +74 -62
  125. flowfile/web/static/assets/{genericNodeSettings-924759c7.css → genericNodeSettings-3b2507ea.css} +10 -10
  126. flowfile/web/static/assets/{genericNodeSettings-4fe5f36b.js → genericNodeSettings-99014e1d.js} +5 -5
  127. flowfile/web/static/assets/index-07dda503.js +38 -0
  128. flowfile/web/static/assets/index-3ba44389.js +2696 -0
  129. flowfile/web/static/assets/{index-50508d4d.css → index-e6289dd0.css} +1945 -569
  130. flowfile/web/static/assets/{index-5429bbf8.js → index-fb6493ae.js} +41626 -40867
  131. flowfile/web/static/assets/node.types-2c15bb7e.js +82 -0
  132. flowfile/web/static/assets/nodeInput-0eb13f1a.js +2 -0
  133. flowfile/web/static/assets/{outputCsv-076b85ab.js → outputCsv-8f8ba42d.js} +3 -3
  134. flowfile/web/static/assets/outputCsv-b9a072af.css +2499 -0
  135. flowfile/web/static/assets/{outputExcel-0fd17dbe.js → outputExcel-393f4fef.js} +3 -3
  136. flowfile/web/static/assets/{outputExcel-b41305c0.css → outputExcel-f5d272b2.css} +26 -26
  137. flowfile/web/static/assets/{outputParquet-b61e0847.js → outputParquet-07c81f65.js} +4 -4
  138. flowfile/web/static/assets/outputParquet-54597c3c.css +4 -0
  139. flowfile/web/static/assets/{readCsv-a8bb8b61.js → readCsv-07f6d9ad.js} +3 -3
  140. flowfile/web/static/assets/{readCsv-c767cb37.css → readCsv-3bfac4c3.css} +15 -15
  141. flowfile/web/static/assets/{readExcel-806d2826.css → readExcel-3db6b763.css} +13 -13
  142. flowfile/web/static/assets/{readExcel-67b4aee0.js → readExcel-ed69bc8f.js} +5 -5
  143. flowfile/web/static/assets/{readParquet-48c81530.css → readParquet-c5244ad5.css} +4 -4
  144. flowfile/web/static/assets/{readParquet-92ce1dbc.js → readParquet-e3ed4528.js} +3 -3
  145. flowfile/web/static/assets/secrets.api-002e7d7e.js +65 -0
  146. flowfile/web/static/assets/{selectDynamic-92e25ee3.js → selectDynamic-80b92899.js} +5 -5
  147. flowfile/web/static/assets/{selectDynamic-aa913ff4.css → selectDynamic-f2fb394f.css} +21 -20
  148. flowfile/web/static/assets/{vue-codemirror.esm-41b0e0d7.js → vue-codemirror.esm-0965f39f.js} +31 -640
  149. flowfile/web/static/assets/{vue-content-loader.es-2c8e608f.js → vue-content-loader.es-c506ad97.js} +1 -1
  150. flowfile/web/static/index.html +2 -2
  151. {flowfile-0.5.1.dist-info → flowfile-0.5.3.dist-info}/METADATA +2 -3
  152. flowfile-0.5.3.dist-info/RECORD +402 -0
  153. flowfile_core/__init__.py +13 -6
  154. flowfile_core/auth/jwt.py +51 -16
  155. flowfile_core/auth/models.py +32 -7
  156. flowfile_core/auth/password.py +89 -0
  157. flowfile_core/auth/secrets.py +8 -6
  158. flowfile_core/configs/__init__.py +9 -7
  159. flowfile_core/configs/flow_logger.py +15 -14
  160. flowfile_core/configs/node_store/__init__.py +72 -4
  161. flowfile_core/configs/node_store/nodes.py +155 -172
  162. flowfile_core/configs/node_store/user_defined_node_registry.py +108 -27
  163. flowfile_core/configs/settings.py +28 -15
  164. flowfile_core/database/connection.py +7 -6
  165. flowfile_core/database/init_db.py +96 -2
  166. flowfile_core/database/models.py +3 -1
  167. flowfile_core/fileExplorer/__init__.py +17 -0
  168. flowfile_core/fileExplorer/funcs.py +123 -57
  169. flowfile_core/fileExplorer/utils.py +10 -11
  170. flowfile_core/flowfile/_extensions/real_time_interface.py +10 -8
  171. flowfile_core/flowfile/analytics/analytics_processor.py +26 -24
  172. flowfile_core/flowfile/analytics/graphic_walker.py +11 -12
  173. flowfile_core/flowfile/analytics/utils.py +1 -1
  174. flowfile_core/flowfile/code_generator/code_generator.py +358 -244
  175. flowfile_core/flowfile/connection_manager/_connection_manager.py +6 -5
  176. flowfile_core/flowfile/connection_manager/models.py +1 -1
  177. flowfile_core/flowfile/database_connection_manager/db_connections.py +60 -44
  178. flowfile_core/flowfile/database_connection_manager/models.py +1 -1
  179. flowfile_core/flowfile/extensions.py +17 -12
  180. flowfile_core/flowfile/flow_data_engine/cloud_storage_reader.py +34 -32
  181. flowfile_core/flowfile/flow_data_engine/create/funcs.py +115 -83
  182. flowfile_core/flowfile/flow_data_engine/flow_data_engine.py +481 -423
  183. flowfile_core/flowfile/flow_data_engine/flow_file_column/interface.py +2 -2
  184. flowfile_core/flowfile/flow_data_engine/flow_file_column/main.py +92 -52
  185. flowfile_core/flowfile/flow_data_engine/flow_file_column/polars_type.py +12 -11
  186. flowfile_core/flowfile/flow_data_engine/flow_file_column/type_registry.py +6 -6
  187. flowfile_core/flowfile/flow_data_engine/flow_file_column/utils.py +26 -30
  188. flowfile_core/flowfile/flow_data_engine/fuzzy_matching/prepare_for_fuzzy_match.py +31 -20
  189. flowfile_core/flowfile/flow_data_engine/join/__init__.py +1 -1
  190. flowfile_core/flowfile/flow_data_engine/join/utils.py +11 -9
  191. flowfile_core/flowfile/flow_data_engine/join/verify_integrity.py +14 -15
  192. flowfile_core/flowfile/flow_data_engine/pivot_table.py +5 -7
  193. flowfile_core/flowfile/flow_data_engine/polars_code_parser.py +95 -82
  194. flowfile_core/flowfile/flow_data_engine/read_excel_tables.py +66 -65
  195. flowfile_core/flowfile/flow_data_engine/sample_data.py +27 -21
  196. flowfile_core/flowfile/flow_data_engine/subprocess_operations/__init__.py +1 -1
  197. flowfile_core/flowfile/flow_data_engine/subprocess_operations/models.py +13 -11
  198. flowfile_core/flowfile/flow_data_engine/subprocess_operations/subprocess_operations.py +190 -127
  199. flowfile_core/flowfile/flow_data_engine/threaded_processes.py +8 -8
  200. flowfile_core/flowfile/flow_data_engine/utils.py +99 -67
  201. flowfile_core/flowfile/flow_graph.py +918 -571
  202. flowfile_core/flowfile/flow_graph_utils.py +31 -49
  203. flowfile_core/flowfile/flow_node/flow_node.py +330 -233
  204. flowfile_core/flowfile/flow_node/models.py +53 -41
  205. flowfile_core/flowfile/flow_node/schema_callback.py +14 -19
  206. flowfile_core/flowfile/graph_tree/graph_tree.py +41 -41
  207. flowfile_core/flowfile/handler.py +80 -30
  208. flowfile_core/flowfile/manage/compatibility_enhancements.py +209 -126
  209. flowfile_core/flowfile/manage/io_flowfile.py +54 -57
  210. flowfile_core/flowfile/node_designer/__init__.py +15 -13
  211. flowfile_core/flowfile/node_designer/_type_registry.py +34 -37
  212. flowfile_core/flowfile/node_designer/custom_node.py +162 -36
  213. flowfile_core/flowfile/node_designer/ui_components.py +135 -34
  214. flowfile_core/flowfile/schema_callbacks.py +71 -51
  215. flowfile_core/flowfile/setting_generator/__init__.py +0 -1
  216. flowfile_core/flowfile/setting_generator/setting_generator.py +6 -5
  217. flowfile_core/flowfile/setting_generator/settings.py +64 -53
  218. flowfile_core/flowfile/sources/external_sources/base_class.py +12 -10
  219. flowfile_core/flowfile/sources/external_sources/custom_external_sources/external_source.py +27 -17
  220. flowfile_core/flowfile/sources/external_sources/custom_external_sources/sample_users.py +9 -9
  221. flowfile_core/flowfile/sources/external_sources/factory.py +0 -1
  222. flowfile_core/flowfile/sources/external_sources/sql_source/models.py +45 -31
  223. flowfile_core/flowfile/sources/external_sources/sql_source/sql_source.py +198 -73
  224. flowfile_core/flowfile/sources/external_sources/sql_source/utils.py +250 -196
  225. flowfile_core/flowfile/util/calculate_layout.py +9 -13
  226. flowfile_core/flowfile/util/execution_orderer.py +25 -17
  227. flowfile_core/flowfile/util/node_skipper.py +4 -4
  228. flowfile_core/flowfile/utils.py +19 -21
  229. flowfile_core/main.py +26 -19
  230. flowfile_core/routes/auth.py +284 -11
  231. flowfile_core/routes/cloud_connections.py +25 -25
  232. flowfile_core/routes/logs.py +21 -29
  233. flowfile_core/routes/public.py +3 -3
  234. flowfile_core/routes/routes.py +70 -34
  235. flowfile_core/routes/secrets.py +25 -27
  236. flowfile_core/routes/user_defined_components.py +483 -4
  237. flowfile_core/run_lock.py +0 -1
  238. flowfile_core/schemas/__init__.py +4 -6
  239. flowfile_core/schemas/analysis_schemas/graphic_walker_schemas.py +55 -55
  240. flowfile_core/schemas/cloud_storage_schemas.py +59 -53
  241. flowfile_core/schemas/input_schema.py +231 -144
  242. flowfile_core/schemas/output_model.py +49 -34
  243. flowfile_core/schemas/schemas.py +116 -89
  244. flowfile_core/schemas/transform_schema.py +518 -263
  245. flowfile_core/schemas/yaml_types.py +21 -7
  246. flowfile_core/secret_manager/secret_manager.py +17 -13
  247. flowfile_core/types.py +29 -9
  248. flowfile_core/utils/arrow_reader.py +7 -6
  249. flowfile_core/utils/excel_file_manager.py +3 -3
  250. flowfile_core/utils/fileManager.py +7 -7
  251. flowfile_core/utils/fl_executor.py +8 -10
  252. flowfile_core/utils/utils.py +4 -4
  253. flowfile_core/utils/validate_setup.py +5 -4
  254. flowfile_frame/__init__.py +106 -51
  255. flowfile_frame/adapters.py +2 -9
  256. flowfile_frame/adding_expr.py +73 -32
  257. flowfile_frame/cloud_storage/frame_helpers.py +27 -23
  258. flowfile_frame/cloud_storage/secret_manager.py +12 -26
  259. flowfile_frame/config.py +2 -5
  260. flowfile_frame/expr.py +311 -218
  261. flowfile_frame/expr.pyi +160 -159
  262. flowfile_frame/expr_name.py +23 -23
  263. flowfile_frame/flow_frame.py +571 -476
  264. flowfile_frame/flow_frame.pyi +123 -104
  265. flowfile_frame/flow_frame_methods.py +227 -246
  266. flowfile_frame/group_frame.py +50 -20
  267. flowfile_frame/join.py +2 -2
  268. flowfile_frame/lazy.py +129 -87
  269. flowfile_frame/lazy_methods.py +83 -30
  270. flowfile_frame/list_name_space.py +55 -50
  271. flowfile_frame/selectors.py +148 -68
  272. flowfile_frame/series.py +9 -7
  273. flowfile_frame/utils.py +19 -21
  274. flowfile_worker/__init__.py +12 -7
  275. flowfile_worker/configs.py +11 -19
  276. flowfile_worker/create/__init__.py +14 -9
  277. flowfile_worker/create/funcs.py +114 -77
  278. flowfile_worker/create/models.py +46 -43
  279. flowfile_worker/create/pl_types.py +14 -15
  280. flowfile_worker/create/read_excel_tables.py +34 -41
  281. flowfile_worker/create/utils.py +22 -19
  282. flowfile_worker/external_sources/s3_source/main.py +18 -51
  283. flowfile_worker/external_sources/s3_source/models.py +34 -27
  284. flowfile_worker/external_sources/sql_source/main.py +8 -5
  285. flowfile_worker/external_sources/sql_source/models.py +13 -9
  286. flowfile_worker/flow_logger.py +10 -8
  287. flowfile_worker/funcs.py +214 -155
  288. flowfile_worker/main.py +11 -17
  289. flowfile_worker/models.py +35 -28
  290. flowfile_worker/process_manager.py +2 -3
  291. flowfile_worker/routes.py +121 -90
  292. flowfile_worker/secrets.py +9 -6
  293. flowfile_worker/spawner.py +80 -49
  294. flowfile_worker/utils.py +3 -2
  295. shared/__init__.py +2 -7
  296. shared/storage_config.py +25 -13
  297. test_utils/postgres/commands.py +3 -2
  298. test_utils/postgres/fixtures.py +9 -9
  299. test_utils/s3/commands.py +1 -1
  300. test_utils/s3/data_generator.py +3 -4
  301. test_utils/s3/demo_data_generator.py +4 -7
  302. test_utils/s3/fixtures.py +7 -5
  303. tools/migrate/__init__.py +1 -1
  304. tools/migrate/__main__.py +16 -29
  305. tools/migrate/legacy_schemas.py +251 -190
  306. tools/migrate/migrate.py +193 -181
  307. tools/migrate/tests/conftest.py +1 -3
  308. tools/migrate/tests/test_migrate.py +36 -41
  309. tools/migrate/tests/test_migration_e2e.py +28 -29
  310. tools/migrate/tests/test_node_migrations.py +50 -20
  311. flowfile/web/static/assets/CloudConnectionManager-2dfdce2f.css +0 -86
  312. flowfile/web/static/assets/CustomNode-74a37f74.css +0 -32
  313. flowfile/web/static/assets/DatabaseManager-30fa27e5.css +0 -64
  314. flowfile/web/static/assets/Filter-9b6d08db.js +0 -164
  315. flowfile/web/static/assets/Filter-f62091b3.css +0 -20
  316. flowfile/web/static/assets/ManualInput-3246a08d.css +0 -96
  317. flowfile/web/static/assets/PivotValidation-891ddfb0.css +0 -13
  318. flowfile/web/static/assets/PivotValidation-c46cd420.css +0 -13
  319. flowfile/web/static/assets/SliderInput-b8fb6a8c.css +0 -4
  320. flowfile/web/static/assets/UnpivotValidation-0d240eeb.css +0 -13
  321. flowfile/web/static/assets/nodeInput-5d0d6b79.js +0 -41
  322. flowfile/web/static/assets/outputCsv-9cc59e0b.css +0 -2499
  323. flowfile/web/static/assets/outputParquet-cf8cf3f2.css +0 -4
  324. flowfile/web/static/assets/secretApi-68435402.js +0 -46
  325. flowfile/web/static/assets/vue-codemirror-bccfde04.css +0 -32
  326. flowfile-0.5.1.dist-info/RECORD +0 -388
  327. {flowfile-0.5.1.dist-info → flowfile-0.5.3.dist-info}/WHEEL +0 -0
  328. {flowfile-0.5.1.dist-info → flowfile-0.5.3.dist-info}/entry_points.txt +0 -0
  329. {flowfile-0.5.1.dist-info → flowfile-0.5.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,18 +1,16 @@
1
- import sys
2
1
  import importlib.util
3
2
  import inspect
4
- from pathlib import Path
5
- from typing import Dict, Type, List
6
3
  import logging
4
+ import sys
5
+ from pathlib import Path
7
6
 
8
- from flowfile_core.flowfile.node_designer.custom_node import CustomNodeBase, NodeSettings
7
+ from flowfile_core.flowfile.node_designer.custom_node import CustomNodeBase
9
8
  from shared import storage
10
9
 
11
-
12
10
  logger = logging.getLogger(__name__)
13
11
 
14
12
 
15
- def get_all_custom_nodes() -> Dict[str, Type[CustomNodeBase]]:
13
+ def get_all_custom_nodes() -> dict[str, type[CustomNodeBase]]:
16
14
  """
17
15
  Scan the user-defined nodes directory and import all CustomNodeBase subclasses.
18
16
 
@@ -55,12 +53,9 @@ def get_all_custom_nodes() -> Dict[str, Type[CustomNodeBase]]:
55
53
  for name, obj in inspect.getmembers(module):
56
54
  # Check if it's a class and a subclass of CustomNodeBase
57
55
  # but not CustomNodeBase itself
58
- if (inspect.isclass(obj) and
59
- issubclass(obj, CustomNodeBase) and
60
- obj is not CustomNodeBase):
61
-
56
+ if inspect.isclass(obj) and issubclass(obj, CustomNodeBase) and obj is not CustomNodeBase:
62
57
  # Use the node_name attribute if it exists, otherwise use class name
63
- node_name = getattr(obj, 'node_name', name)
58
+ node_name = getattr(obj, "node_name", name)
64
59
  custom_nodes[node_name] = obj
65
60
  print(f"Loaded custom node: {node_name} from {file_path.name}")
66
61
 
@@ -72,7 +67,7 @@ def get_all_custom_nodes() -> Dict[str, Type[CustomNodeBase]]:
72
67
  return custom_nodes
73
68
 
74
69
 
75
- def get_all_custom_nodes_with_validation() -> Dict[str, Type[CustomNodeBase]]:
70
+ def get_all_custom_nodes_with_validation() -> dict[str, type[CustomNodeBase]]:
76
71
  """
77
72
  Enhanced version that validates the nodes before adding them.
78
73
  """
@@ -97,22 +92,19 @@ def get_all_custom_nodes_with_validation() -> Dict[str, Type[CustomNodeBase]]:
97
92
  spec.loader.exec_module(module)
98
93
 
99
94
  for name, obj in inspect.getmembers(module):
100
- if (inspect.isclass(obj) and
101
- issubclass(obj, CustomNodeBase) and
102
- obj is not CustomNodeBase):
103
-
95
+ if inspect.isclass(obj) and issubclass(obj, CustomNodeBase) and obj is not CustomNodeBase:
104
96
  try:
105
97
  _obj = obj()
106
98
  # Validate that the node has required attributes
107
- if not hasattr(_obj, 'node_name'):
99
+ if not hasattr(_obj, "node_name"):
108
100
  logger.error(f"Warning: {name} missing node_name attribute")
109
101
  raise ValueError(f"Node {name} must implement a node_name attribute")
110
102
 
111
- if not hasattr(_obj, 'settings_schema'):
103
+ if not hasattr(_obj, "settings_schema"):
112
104
  logger.error(f"Warning: {name} missing settings_schema attribute")
113
105
  raise ValueError(f"Node {name} must implement a settings_schema attribute")
114
106
 
115
- if not hasattr(_obj, 'process'):
107
+ if not hasattr(_obj, "process"):
116
108
  logger.error(f"Warning: {name} missing process method")
117
109
  raise ValueError(f"Node {name} must implement a process method")
118
110
  if not (storage.user_defined_nodes_icons / _obj.node_icon).exists():
@@ -137,7 +129,7 @@ def get_all_custom_nodes_with_validation() -> Dict[str, Type[CustomNodeBase]]:
137
129
  return custom_nodes
138
130
 
139
131
 
140
- def get_custom_nodes_lazy() -> List[Type[CustomNodeBase]]:
132
+ def get_custom_nodes_lazy() -> list[type[CustomNodeBase]]:
141
133
  """
142
134
  Returns a list of custom node classes without instantiating them.
143
135
  Useful for registration or catalog purposes.
@@ -162,10 +154,12 @@ def get_custom_nodes_lazy() -> List[Type[CustomNodeBase]]:
162
154
  spec.loader.exec_module(module)
163
155
 
164
156
  for name, obj in inspect.getmembers(module):
165
- if (inspect.isclass(obj) and
166
- issubclass(obj, CustomNodeBase) and
167
- obj is not CustomNodeBase and
168
- obj.__module__ == module.__name__): # Only get classes defined in this module
157
+ if (
158
+ inspect.isclass(obj)
159
+ and issubclass(obj, CustomNodeBase)
160
+ and obj is not CustomNodeBase
161
+ and obj.__module__ == module.__name__
162
+ ): # Only get classes defined in this module
169
163
  nodes.append(obj)
170
164
 
171
165
  except Exception as e:
@@ -176,18 +170,105 @@ def get_custom_nodes_lazy() -> List[Type[CustomNodeBase]]:
176
170
 
177
171
 
178
172
  # Example usage function that matches your original pattern
179
- def add_custom_node(node_class: Type[CustomNodeBase], registry: Dict[str, Type[CustomNodeBase]]):
173
+ def add_custom_node(node_class: type[CustomNodeBase], registry: dict[str, type[CustomNodeBase]]):
180
174
  """Add a single custom node to the registry."""
181
- if hasattr(node_class, 'node_name'):
175
+ if hasattr(node_class, "node_name"):
182
176
  registry[node_class.node_name] = node_class
183
177
  else:
184
178
  registry[node_class.__name__] = node_class
185
179
 
186
180
 
187
- def get_all_nodes_from_standard_location() -> Dict[str, Type[CustomNodeBase]]:
181
+ def get_all_nodes_from_standard_location() -> dict[str, type[CustomNodeBase]]:
188
182
  """
189
183
  Main function to get all custom nodes from the standard location.
190
184
  This matches your original function signature.
191
185
  """
192
186
 
193
187
  return get_all_custom_nodes_with_validation()
188
+
189
+
190
+ def load_single_node_from_file(file_path: Path) -> type[CustomNodeBase] | None:
191
+ """
192
+ Load a single custom node from a specific file.
193
+
194
+ Args:
195
+ file_path: Path to the Python file containing the custom node
196
+
197
+ Returns:
198
+ The custom node class if found and valid, None otherwise
199
+ """
200
+ if not file_path.exists():
201
+ logger.error(f"File not found: {file_path}")
202
+ return None
203
+
204
+ try:
205
+ # Create a unique module name to avoid conflicts with cached modules
206
+ module_name = f"custom_node_{file_path.stem}_{id(file_path)}"
207
+
208
+ # Remove old module from sys.modules if it exists (for reloading)
209
+ old_module_name = file_path.stem
210
+ if old_module_name in sys.modules:
211
+ del sys.modules[old_module_name]
212
+
213
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
214
+
215
+ if spec and spec.loader:
216
+ module = importlib.util.module_from_spec(spec)
217
+ sys.modules[module_name] = module
218
+ spec.loader.exec_module(module)
219
+
220
+ for name, obj in inspect.getmembers(module):
221
+ if inspect.isclass(obj) and issubclass(obj, CustomNodeBase) and obj is not CustomNodeBase:
222
+ try:
223
+ _obj = obj()
224
+ # Validate required attributes
225
+ if not hasattr(_obj, "node_name"):
226
+ raise ValueError(f"Node {name} must have a node_name attribute")
227
+ if not hasattr(_obj, "settings_schema"):
228
+ raise ValueError(f"Node {name} must have a settings_schema attribute")
229
+ if not hasattr(_obj, "process"):
230
+ raise ValueError(f"Node {name} must have a process method")
231
+
232
+ logger.info(f"✓ Loaded: {_obj.node_name} from {file_path.name}")
233
+ return obj
234
+ except Exception as e:
235
+ logger.error(f"Error validating node {name}: {e}")
236
+ raise
237
+
238
+ except SyntaxError as e:
239
+ logger.error(f"Syntax error in {file_path}: {e}")
240
+ raise
241
+ except ImportError as e:
242
+ logger.error(f"Import error in {file_path}: {e}")
243
+ raise
244
+ except Exception as e:
245
+ logger.error(f"Unexpected error loading {file_path}: {e}")
246
+ raise
247
+
248
+ return None
249
+
250
+
251
+ def unload_node_by_name(node_name: str) -> bool:
252
+ """
253
+ Remove a node from sys.modules cache by its name.
254
+ This helps ensure the node can be cleanly reloaded later.
255
+
256
+ Args:
257
+ node_name: The name of the node to unload
258
+
259
+ Returns:
260
+ True if any modules were removed, False otherwise
261
+ """
262
+ # Convert node name to potential module names
263
+ module_stem = node_name.lower().replace(" ", "_")
264
+
265
+ # Find and remove any matching modules from sys.modules
266
+ modules_to_remove = [
267
+ key for key in sys.modules.keys() if key == module_stem or key.startswith(f"custom_node_{module_stem}")
268
+ ]
269
+
270
+ for mod_name in modules_to_remove:
271
+ del sys.modules[mod_name]
272
+ logger.info(f"Removed module from cache: {mod_name}")
273
+
274
+ return len(modules_to_remove) > 0
@@ -1,16 +1,14 @@
1
-
2
1
  # flowfile_core/flowfile_core/configs/settings.py
3
- import platform
2
+ import argparse
4
3
  import os
4
+ import platform
5
5
  import tempfile
6
- import argparse
7
6
 
8
7
  from passlib.context import CryptContext
9
8
  from starlette.config import Config
10
- from shared.storage_config import storage
11
9
 
12
10
  from flowfile_core.configs.utils import MutableBool
13
-
11
+ from shared.storage_config import storage
14
12
 
15
13
  # Constants for server and worker configuration
16
14
  DEFAULT_SERVER_HOST = "0.0.0.0"
@@ -27,12 +25,8 @@ OFFLOAD_TO_WORKER: MutableBool = MutableBool(os.environ.get("FLOWFILE_OFFLOAD_TO
27
25
  def parse_args():
28
26
  """Parse command line arguments"""
29
27
  parser = argparse.ArgumentParser(description="Flowfile Backend Server")
30
- parser.add_argument(
31
- "--host", type=str, default=DEFAULT_SERVER_HOST, help="Host to bind to"
32
- )
33
- parser.add_argument(
34
- "--port", type=int, default=DEFAULT_SERVER_PORT, help="Port to bind to"
35
- )
28
+ parser.add_argument("--host", type=str, default=DEFAULT_SERVER_HOST, help="Host to bind to")
29
+ parser.add_argument("--port", type=int, default=DEFAULT_SERVER_PORT, help="Port to bind to")
36
30
  parser.add_argument(
37
31
  "--worker-port",
38
32
  type=int,
@@ -84,8 +78,9 @@ args = parse_args()
84
78
 
85
79
  SERVER_HOST = args.host if args.host is not None else DEFAULT_SERVER_HOST
86
80
  SERVER_PORT = args.port if args.port is not None else DEFAULT_SERVER_PORT
87
- WORKER_PORT = args.worker_port if args.worker_port is not None else int(os.getenv("FLOWFILE_WORKER_PORT",
88
- DEFAULT_WORKER_PORT))
81
+ WORKER_PORT = (
82
+ args.worker_port if args.worker_port is not None else int(os.getenv("FLOWFILE_WORKER_PORT", DEFAULT_WORKER_PORT))
83
+ )
89
84
  WORKER_HOST = os.getenv("WORKER_HOST", "0.0.0.0" if platform.system() != "Windows" else "127.0.0.1")
90
85
 
91
86
  config = Config(".env")
@@ -93,9 +88,27 @@ DEBUG: bool = config("DEBUG", cast=bool, default=False)
93
88
  FILE_LOCATION = config("FILE_LOCATION", cast=str, default=".\\files\\")
94
89
  AVAILABLE_RAM = config("AVAILABLE_RAM", cast=int, default=8)
95
90
  WORKER_URL = config("FLOWFILE_WORKER_URL", cast=str, default=get_default_worker_url(WORKER_PORT))
96
- IS_RUNNING_IN_DOCKER = os.getenv('RUNNING_IN_DOCKER', 'false').lower() == 'true'
97
91
  TEMP_DIR = storage.temp_directory
98
92
 
93
+ # FLOWFILE_MODE: Determines the runtime environment
94
+ # Possible values: "electron" (desktop app), "package" (Python package), "docker" (container)
95
+ FLOWFILE_MODE = os.getenv("FLOWFILE_MODE", "electron")
96
+
97
+ def is_docker_mode() -> bool:
98
+ """Check if running in Docker container mode"""
99
+ return FLOWFILE_MODE == "docker"
100
+
101
+ def is_electron_mode() -> bool:
102
+ """Check if running in Electron desktop app mode"""
103
+ return FLOWFILE_MODE == "electron"
104
+
105
+ def is_package_mode() -> bool:
106
+ """Check if running as Python package"""
107
+ return FLOWFILE_MODE == "package"
108
+
109
+ # Legacy compatibility - will be removed in future versions
110
+ IS_RUNNING_IN_DOCKER = is_docker_mode()
111
+
99
112
  ALGORITHM = "HS256"
100
113
  ACCESS_TOKEN_EXPIRE_MINUTES = 120
101
- PWD_CONTEXT = CryptContext(schemes=["bcrypt"], deprecated="auto")
114
+ PWD_CONTEXT = CryptContext(schemes=["bcrypt"], deprecated="auto")
@@ -1,9 +1,11 @@
1
- from sqlalchemy import create_engine
2
- from contextlib import contextmanager
3
- from sqlalchemy.orm import sessionmaker
4
1
  import os
5
2
  import sys
3
+ from contextlib import contextmanager
6
4
  from pathlib import Path
5
+
6
+ from sqlalchemy import create_engine
7
+ from sqlalchemy.orm import sessionmaker
8
+
7
9
  from flowfile_core.configs import logger
8
10
  from shared.storage_config import storage
9
11
 
@@ -44,8 +46,7 @@ def get_database_path() -> Path:
44
46
 
45
47
  # Create database engine
46
48
  engine = create_engine(
47
- get_database_url(),
48
- connect_args={"check_same_thread": False} if "sqlite" in get_database_url() else {}
49
+ get_database_url(), connect_args={"check_same_thread": False} if "sqlite" in get_database_url() else {}
49
50
  )
50
51
 
51
52
  # Create session factory
@@ -77,5 +78,5 @@ def get_database_info():
77
78
  "url": get_database_url(),
78
79
  "path": str(get_database_path()) if get_database_path() else None,
79
80
  "app_data_dir": str(get_app_data_dir()),
80
- "platform": sys.platform
81
+ "platform": sys.platform,
81
82
  }
@@ -1,13 +1,53 @@
1
1
  # Generate a random secure password and hash it
2
+ import os
2
3
  import secrets
3
4
  import string
5
+ import logging
4
6
  from sqlalchemy.orm import Session
7
+ from sqlalchemy import text
5
8
  from flowfile_core.database import models as db_models
6
9
  from flowfile_core.database.connection import engine, SessionLocal
10
+ from flowfile_core.auth.password import get_password_hash
7
11
 
8
12
  from passlib.context import CryptContext
9
13
  pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
10
14
 
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ def run_migrations():
19
+ """Run database migrations to update schema for existing databases."""
20
+ with engine.connect() as conn:
21
+ # Check if users table exists
22
+ result = conn.execute(text(
23
+ "SELECT name FROM sqlite_master WHERE type='table' AND name='users'"
24
+ ))
25
+ if not result.fetchone():
26
+ logger.info("Users table does not exist, will be created with new schema")
27
+ return
28
+
29
+ # Check existing columns
30
+ result = conn.execute(text("PRAGMA table_info(users)"))
31
+ columns = [row[1] for row in result.fetchall()]
32
+
33
+ # Add is_admin column if missing
34
+ if 'is_admin' not in columns:
35
+ logger.info("Adding is_admin column to users table")
36
+ conn.execute(text("ALTER TABLE users ADD COLUMN is_admin BOOLEAN DEFAULT 0"))
37
+ conn.commit()
38
+ logger.info("Migration complete: is_admin column added")
39
+
40
+ # Add must_change_password column if missing
41
+ if 'must_change_password' not in columns:
42
+ logger.info("Adding must_change_password column to users table")
43
+ conn.execute(text("ALTER TABLE users ADD COLUMN must_change_password BOOLEAN DEFAULT 0"))
44
+ conn.commit()
45
+ logger.info("Migration complete: must_change_password column added")
46
+
47
+
48
+ # Run migrations BEFORE create_all to update existing tables
49
+ run_migrations()
50
+ # Then create any new tables (this will include is_admin for new databases)
11
51
  db_models.Base.metadata.create_all(bind=engine)
12
52
 
13
53
 
@@ -21,19 +61,73 @@ def create_default_local_user(db: Session):
21
61
  username="local_user",
22
62
  email="local@flowfile.app",
23
63
  full_name="Local User",
24
- hashed_password=hashed_password
64
+ hashed_password=hashed_password,
65
+ must_change_password=False # Local user doesn't need to change password
25
66
  )
26
67
  db.add(local_user)
27
68
  db.commit()
28
69
  return True
29
- else:
70
+ return False
71
+
72
+
73
+ def create_docker_admin_user(db: Session):
74
+ """
75
+ Create admin user for Docker mode from environment variables.
76
+ Only runs when FLOWFILE_MODE=docker.
77
+ Reads FLOWFILE_ADMIN_USER and FLOWFILE_ADMIN_PASSWORD from environment.
78
+ """
79
+ # Only run in Docker mode
80
+ if os.environ.get("FLOWFILE_MODE") != "docker":
30
81
  return False
31
82
 
83
+ # Read environment variables
84
+ admin_username = os.environ.get("FLOWFILE_ADMIN_USER")
85
+ admin_password = os.environ.get("FLOWFILE_ADMIN_PASSWORD")
86
+
87
+ # Skip if either is not set
88
+ if not admin_username or not admin_password:
89
+ logger.warning(
90
+ "Docker mode detected but FLOWFILE_ADMIN_USER or FLOWFILE_ADMIN_PASSWORD "
91
+ "not set. Admin user will not be created."
92
+ )
93
+ return False
94
+
95
+ # Check if user already exists
96
+ existing_user = db.query(db_models.User).filter(
97
+ db_models.User.username == admin_username
98
+ ).first()
99
+
100
+ if existing_user:
101
+ # Ensure existing admin user has is_admin=True
102
+ if not existing_user.is_admin:
103
+ existing_user.is_admin = True
104
+ db.commit()
105
+ logger.info(f"Admin user '{admin_username}' updated with admin privileges.")
106
+ else:
107
+ logger.info(f"Admin user '{admin_username}' already exists with admin privileges.")
108
+ return False
109
+
110
+ # Create user with hashed password and admin privileges
111
+ hashed_password = get_password_hash(admin_password)
112
+ admin_user = db_models.User(
113
+ username=admin_username,
114
+ email=f"{admin_username}@flowfile.app",
115
+ full_name="Admin User",
116
+ hashed_password=hashed_password,
117
+ is_admin=True,
118
+ must_change_password=True # Force password change on first login
119
+ )
120
+ db.add(admin_user)
121
+ db.commit()
122
+ logger.info(f"Admin user '{admin_username}' created successfully.")
123
+ return True
124
+
32
125
 
33
126
  def init_db():
34
127
  db = SessionLocal()
35
128
  try:
36
129
  create_default_local_user(db)
130
+ create_docker_admin_user(db)
37
131
  finally:
38
132
  db.close()
39
133
 
@@ -1,4 +1,4 @@
1
- from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Text, DateTime
1
+ from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, Text
2
2
  from sqlalchemy.ext.declarative import declarative_base
3
3
  from sqlalchemy.sql import func
4
4
 
@@ -14,6 +14,8 @@ class User(Base):
14
14
  full_name = Column(String)
15
15
  hashed_password = Column(String)
16
16
  disabled = Column(Boolean, default=False)
17
+ is_admin = Column(Boolean, default=False)
18
+ must_change_password = Column(Boolean, default=True)
17
19
 
18
20
 
19
21
  class Secret(Base):
@@ -0,0 +1,17 @@
1
+ from .funcs import (
2
+ FileExplorer,
3
+ FileInfo,
4
+ SecureFileExplorer,
5
+ get_files_from_directory,
6
+ validate_file_path,
7
+ validate_path_under_cwd,
8
+ )
9
+
10
+ __all__ = [
11
+ "FileExplorer",
12
+ "FileInfo",
13
+ "SecureFileExplorer",
14
+ "get_files_from_directory",
15
+ "validate_file_path",
16
+ "validate_path_under_cwd",
17
+ ]