dmart 1.4.40.post8__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 (489) hide show
  1. dmart/__init__.py +7 -0
  2. dmart/alembic/README +1 -0
  3. dmart/alembic/__init__.py +0 -0
  4. dmart/alembic/env.py +91 -0
  5. dmart/alembic/notes.txt +11 -0
  6. dmart/alembic/script.py.mako +28 -0
  7. dmart/alembic/scripts/__init__.py +0 -0
  8. dmart/alembic/scripts/calculate_checksums.py +77 -0
  9. dmart/alembic/scripts/migration_f7a4949eed19.py +28 -0
  10. dmart/alembic/versions/0f3d2b1a7c21_add_authz_materialized_views.py +87 -0
  11. dmart/alembic/versions/10d2041b94d4_last_checksum_history.py +62 -0
  12. dmart/alembic/versions/1cf4e1ee3cb8_ext_permission_with_filter_fields_values.py +33 -0
  13. dmart/alembic/versions/26bfe19b49d4_rm_failedloginattempts.py +42 -0
  14. dmart/alembic/versions/3c8bca2219cc_add_otp_table.py +38 -0
  15. dmart/alembic/versions/6675fd9dfe42_remove_unique_from_sessions_table.py +36 -0
  16. dmart/alembic/versions/71bc1df82e6a_adding_user_last_login_at.py +43 -0
  17. dmart/alembic/versions/74288ccbd3b5_initial.py +264 -0
  18. dmart/alembic/versions/7520a89a8467_rm_activesession_table.py +39 -0
  19. dmart/alembic/versions/848b623755a4_make_created_nd_updated_at_required.py +138 -0
  20. dmart/alembic/versions/8640dcbebf85_add_notes_to_users.py +32 -0
  21. dmart/alembic/versions/91c94250232a_adding_fk_on_owner_shortname.py +104 -0
  22. dmart/alembic/versions/98ecd6f56f9a_ext_meta_with_owner_group_shortname.py +66 -0
  23. dmart/alembic/versions/9aae9138c4ef_indexing_created_at_updated_at.py +80 -0
  24. dmart/alembic/versions/__init__.py +0 -0
  25. dmart/alembic/versions/b53f916b3f6d_json_to_jsonb.py +492 -0
  26. dmart/alembic/versions/eb5f1ec65156_adding_user_locked_to_device.py +36 -0
  27. dmart/alembic/versions/f7a4949eed19_adding_query_policies_to_meta.py +60 -0
  28. dmart/alembic.ini +117 -0
  29. dmart/api/__init__.py +0 -0
  30. dmart/api/info/__init__.py +0 -0
  31. dmart/api/info/router.py +109 -0
  32. dmart/api/managed/__init__.py +0 -0
  33. dmart/api/managed/router.py +1541 -0
  34. dmart/api/managed/utils.py +1879 -0
  35. dmart/api/public/__init__.py +0 -0
  36. dmart/api/public/router.py +758 -0
  37. dmart/api/qr/__init__.py +0 -0
  38. dmart/api/qr/router.py +108 -0
  39. dmart/api/user/__init__.py +0 -0
  40. dmart/api/user/model/__init__.py +0 -0
  41. dmart/api/user/model/errors.py +14 -0
  42. dmart/api/user/model/requests.py +165 -0
  43. dmart/api/user/model/responses.py +11 -0
  44. dmart/api/user/router.py +1413 -0
  45. dmart/api/user/service.py +270 -0
  46. dmart/bundler.py +52 -0
  47. dmart/cli.py +1133 -0
  48. dmart/config/__init__.py +0 -0
  49. dmart/config/channels.json +11 -0
  50. dmart/config/notification.json +17 -0
  51. dmart/config.env.sample +27 -0
  52. dmart/config.ini.sample +7 -0
  53. dmart/conftest.py +13 -0
  54. dmart/curl.sh +196 -0
  55. dmart/cxb/__init__.py +0 -0
  56. dmart/cxb/assets/@codemirror-Rn7_6DkE.js +10 -0
  57. dmart/cxb/assets/@edraj-CS4NwVbD.js +1 -0
  58. dmart/cxb/assets/@floating-ui-BwwcF-xh.js +1 -0
  59. dmart/cxb/assets/@formatjs-yKEsAtjs.js +1 -0
  60. dmart/cxb/assets/@fortawesome-DRW1UCdr.js +9 -0
  61. dmart/cxb/assets/@jsonquerylang-laKNoFFq.js +12 -0
  62. dmart/cxb/assets/@lezer-za4Q-8Ew.js +1 -0
  63. dmart/cxb/assets/@marijn-DXwl3gUT.js +1 -0
  64. dmart/cxb/assets/@popperjs-l0sNRNKZ.js +1 -0
  65. dmart/cxb/assets/@replit--ERk53eB.js +1 -0
  66. dmart/cxb/assets/@roxi-CGMFK4i8.js +6 -0
  67. dmart/cxb/assets/@typewriter-cCzskkIv.js +17 -0
  68. dmart/cxb/assets/@zerodevx-BlBZjKxu.js +1 -0
  69. dmart/cxb/assets/@zerodevx-CVEpe6WZ.css +1 -0
  70. dmart/cxb/assets/BreadCrumbLite-DAhOx38v.js +1 -0
  71. dmart/cxb/assets/EntryRenderer-CCqV8Rkg.js +32 -0
  72. dmart/cxb/assets/EntryRenderer-DXytdFp9.css +1 -0
  73. dmart/cxb/assets/ListView-BQelo7vZ.js +16 -0
  74. dmart/cxb/assets/ListView-U8of-_c-.css +1 -0
  75. dmart/cxb/assets/Prism--hMplq-p.js +3 -0
  76. dmart/cxb/assets/Prism-Uh6uStUw.css +1 -0
  77. dmart/cxb/assets/Table2Cols-BsbwicQm.js +1 -0
  78. dmart/cxb/assets/_..-BvT6vdHa.css +1 -0
  79. dmart/cxb/assets/_...404_-fuLH_rX9.js +2 -0
  80. dmart/cxb/assets/_...fallback_-Ba_NLmAE.js +1 -0
  81. dmart/cxb/assets/_module-3HrtKAWo.js +3 -0
  82. dmart/cxb/assets/_module-DFKFq0AM.js +4 -0
  83. dmart/cxb/assets/_module-Dgq0ZVtz.js +1 -0
  84. dmart/cxb/assets/ajv-Cpj98o6Y.js +1 -0
  85. dmart/cxb/assets/axios-CG2WSiiR.js +6 -0
  86. dmart/cxb/assets/clsx-B-dksMZM.js +1 -0
  87. dmart/cxb/assets/codemirror-wrapped-line-indent-DPhKvljI.js +1 -0
  88. dmart/cxb/assets/compare-C3AjiGFR.js +1 -0
  89. dmart/cxb/assets/compute-scroll-into-view-Bl8rNFhg.js +1 -0
  90. dmart/cxb/assets/consolite-DlCuI0F9.js +1 -0
  91. dmart/cxb/assets/crelt-C8TCjufn.js +1 -0
  92. dmart/cxb/assets/date-fns-l0sNRNKZ.js +1 -0
  93. dmart/cxb/assets/deepmerge-rn4rBaHU.js +1 -0
  94. dmart/cxb/assets/dmart_services-AL6-IdDE.js +1 -0
  95. dmart/cxb/assets/downloadFile-D08i0YDh.js +1 -0
  96. dmart/cxb/assets/easy-signal-BiPFIK3O.js +1 -0
  97. dmart/cxb/assets/esm-env-rsSWfq8L.js +1 -0
  98. dmart/cxb/assets/export-OF_rTiXu.js +1 -0
  99. dmart/cxb/assets/fast-deep-equal-l0sNRNKZ.js +1 -0
  100. dmart/cxb/assets/fast-diff-C-IidNf4.js +1 -0
  101. dmart/cxb/assets/fast-uri-l0sNRNKZ.js +1 -0
  102. dmart/cxb/assets/flowbite-svelte-BLvjb-sa.js +1 -0
  103. dmart/cxb/assets/flowbite-svelte-CD54FDqW.css +1 -0
  104. dmart/cxb/assets/flowbite-svelte-icons-BI8GVhw_.js +1 -0
  105. dmart/cxb/assets/github-slugger-CQ4oX9Ud.js +1 -0
  106. dmart/cxb/assets/global-igKv-1g9.js +1 -0
  107. dmart/cxb/assets/hookar-BMRD9G9H.js +1 -0
  108. dmart/cxb/assets/immutable-json-patch-DtRO2E_S.js +1 -0
  109. dmart/cxb/assets/import-1vE3gBat.js +1 -0
  110. dmart/cxb/assets/index-B-eTh-ZX.js +1 -0
  111. dmart/cxb/assets/index-BSsK-X71.js +1 -0
  112. dmart/cxb/assets/index-BVyxzKtH.js +1 -0
  113. dmart/cxb/assets/index-BdeNM69f.js +1 -0
  114. dmart/cxb/assets/index-CC-A1ipE.js +1 -0
  115. dmart/cxb/assets/index-CQohGiYB.js +1 -0
  116. dmart/cxb/assets/index-ChjnkpdZ.js +4 -0
  117. dmart/cxb/assets/index-DLP7csA4.js +1 -0
  118. dmart/cxb/assets/index-DTfhnhwd.js +1 -0
  119. dmart/cxb/assets/index-DdXRK7n9.js +2 -0
  120. dmart/cxb/assets/index-DtiCmB4o.js +1 -0
  121. dmart/cxb/assets/index-NBrXBlLA.css +2 -0
  122. dmart/cxb/assets/index-X1uNehO7.js +1 -0
  123. dmart/cxb/assets/index-nrQW6Nrr.js +1 -0
  124. dmart/cxb/assets/info-B986lRiM.js +1 -0
  125. dmart/cxb/assets/intl-messageformat-Dc5UU-HB.js +3 -0
  126. dmart/cxb/assets/jmespath-l0sNRNKZ.js +1 -0
  127. dmart/cxb/assets/json-schema-traverse-l0sNRNKZ.js +1 -0
  128. dmart/cxb/assets/json-source-map-DRgZidqy.js +5 -0
  129. dmart/cxb/assets/jsonpath-plus-l0sNRNKZ.js +1 -0
  130. dmart/cxb/assets/jsonrepair-B30Dx381.js +8 -0
  131. dmart/cxb/assets/lodash-es-DZVAA2ox.js +1 -0
  132. dmart/cxb/assets/marked-DKjyhwJX.js +56 -0
  133. dmart/cxb/assets/marked-gfm-heading-id-U5zO829x.js +2 -0
  134. dmart/cxb/assets/marked-mangle-CDMeiHC6.js +1 -0
  135. dmart/cxb/assets/memoize-one-BdPwpGay.js +1 -0
  136. dmart/cxb/assets/natural-compare-lite-Bg2Xcf-o.js +7 -0
  137. dmart/cxb/assets/pagination-svelte-D5CyoiE_.js +13 -0
  138. dmart/cxb/assets/pagination-svelte-v10nAbbM.css +1 -0
  139. dmart/cxb/assets/plantuml-encoder-C47mzt9T.js +1 -0
  140. dmart/cxb/assets/prismjs-DTUiLGJu.js +9 -0
  141. dmart/cxb/assets/profile-BUf-tKMe.js +1 -0
  142. dmart/cxb/assets/query-CNmXTsgf.js +1 -0
  143. dmart/cxb/assets/queryHelpers-C9iBWwqe.js +1 -0
  144. dmart/cxb/assets/scroll-into-view-if-needed-KR58zyjF.js +1 -0
  145. dmart/cxb/assets/spaces-0oyGvpii.js +1 -0
  146. dmart/cxb/assets/style-mod-Bs6eFhZE.js +3 -0
  147. dmart/cxb/assets/svelte-B2XmcTi_.js +4 -0
  148. dmart/cxb/assets/svelte-awesome-COLlx0DN.css +1 -0
  149. dmart/cxb/assets/svelte-awesome-DhnMA6Q_.js +1 -0
  150. dmart/cxb/assets/svelte-datatables-net-CY7LBj6I.js +1 -0
  151. dmart/cxb/assets/svelte-floating-ui-BlS3sOAQ.js +1 -0
  152. dmart/cxb/assets/svelte-i18n-CT2KkQaN.js +3 -0
  153. dmart/cxb/assets/svelte-jsoneditor-BzfX6Usi.css +1 -0
  154. dmart/cxb/assets/svelte-jsoneditor-CUGSvWId.js +25 -0
  155. dmart/cxb/assets/svelte-select-CegQKzqH.css +1 -0
  156. dmart/cxb/assets/svelte-select-CjHAt_85.js +6 -0
  157. dmart/cxb/assets/tailwind-merge-CJvxXMcu.js +1 -0
  158. dmart/cxb/assets/tailwind-variants-Cj20BoQ3.js +1 -0
  159. dmart/cxb/assets/toast-B9WDyfyI.js +1 -0
  160. dmart/cxb/assets/tslib-pJfR_DrR.js +1 -0
  161. dmart/cxb/assets/typewriter-editor-DkTVIJdm.js +25 -0
  162. dmart/cxb/assets/user-DeK_NB5v.js +1 -0
  163. dmart/cxb/assets/vanilla-picker-l5rcX3cq.js +8 -0
  164. dmart/cxb/assets/w3c-keyname-Vcq4gwWv.js +1 -0
  165. dmart/cxb/config.json +11 -0
  166. dmart/cxb/config.sample.json +11 -0
  167. dmart/cxb/favicon.ico +0 -0
  168. dmart/cxb/favicon.png +0 -0
  169. dmart/cxb/index.html +28 -0
  170. dmart/data_adapters/__init__.py +0 -0
  171. dmart/data_adapters/adapter.py +16 -0
  172. dmart/data_adapters/base_data_adapter.py +467 -0
  173. dmart/data_adapters/file/__init__.py +0 -0
  174. dmart/data_adapters/file/adapter.py +2043 -0
  175. dmart/data_adapters/file/adapter_helpers.py +1013 -0
  176. dmart/data_adapters/file/archive.py +150 -0
  177. dmart/data_adapters/file/create_index.py +331 -0
  178. dmart/data_adapters/file/create_users_folders.py +52 -0
  179. dmart/data_adapters/file/custom_validations.py +68 -0
  180. dmart/data_adapters/file/drop_index.py +40 -0
  181. dmart/data_adapters/file/health_check.py +560 -0
  182. dmart/data_adapters/file/redis_services.py +1110 -0
  183. dmart/data_adapters/helpers.py +27 -0
  184. dmart/data_adapters/sql/__init__.py +0 -0
  185. dmart/data_adapters/sql/adapter.py +3218 -0
  186. dmart/data_adapters/sql/adapter_helpers.py +491 -0
  187. dmart/data_adapters/sql/create_tables.py +451 -0
  188. dmart/data_adapters/sql/create_users_folders.py +53 -0
  189. dmart/data_adapters/sql/db_to_json_migration.py +485 -0
  190. dmart/data_adapters/sql/health_check_sql.py +232 -0
  191. dmart/data_adapters/sql/json_to_db_migration.py +454 -0
  192. dmart/data_adapters/sql/update_query_policies.py +101 -0
  193. dmart/data_generator.py +81 -0
  194. dmart/dmart.py +761 -0
  195. dmart/get_settings.py +7 -0
  196. dmart/hypercorn_config.toml +3 -0
  197. dmart/info.json +1 -0
  198. dmart/languages/__init__.py +0 -0
  199. dmart/languages/arabic.json +15 -0
  200. dmart/languages/english.json +16 -0
  201. dmart/languages/kurdish.json +14 -0
  202. dmart/languages/loader.py +12 -0
  203. dmart/login_creds.sh +7 -0
  204. dmart/login_creds.sh.sample +7 -0
  205. dmart/main.py +563 -0
  206. dmart/manifest.sh +12 -0
  207. dmart/migrate.py +24 -0
  208. dmart/models/__init__.py +0 -0
  209. dmart/models/api.py +203 -0
  210. dmart/models/core.py +597 -0
  211. dmart/models/enums.py +255 -0
  212. dmart/password_gen.py +8 -0
  213. dmart/plugins/__init__.py +0 -0
  214. dmart/plugins/action_log/__init__.py +0 -0
  215. dmart/plugins/action_log/config.json +13 -0
  216. dmart/plugins/action_log/plugin.py +121 -0
  217. dmart/plugins/admin_notification_sender/__init__.py +0 -0
  218. dmart/plugins/admin_notification_sender/config.json +13 -0
  219. dmart/plugins/admin_notification_sender/plugin.py +124 -0
  220. dmart/plugins/ldap_manager/__init__.py +0 -0
  221. dmart/plugins/ldap_manager/config.json +12 -0
  222. dmart/plugins/ldap_manager/dmart.schema +146 -0
  223. dmart/plugins/ldap_manager/plugin.py +100 -0
  224. dmart/plugins/ldap_manager/slapd.conf +53 -0
  225. dmart/plugins/local_notification/__init__.py +0 -0
  226. dmart/plugins/local_notification/config.json +13 -0
  227. dmart/plugins/local_notification/plugin.py +123 -0
  228. dmart/plugins/realtime_updates_notifier/__init__.py +0 -0
  229. dmart/plugins/realtime_updates_notifier/config.json +12 -0
  230. dmart/plugins/realtime_updates_notifier/plugin.py +58 -0
  231. dmart/plugins/redis_db_update/__init__.py +0 -0
  232. dmart/plugins/redis_db_update/config.json +13 -0
  233. dmart/plugins/redis_db_update/plugin.py +188 -0
  234. dmart/plugins/resource_folders_creation/__init__.py +0 -0
  235. dmart/plugins/resource_folders_creation/config.json +12 -0
  236. dmart/plugins/resource_folders_creation/plugin.py +81 -0
  237. dmart/plugins/system_notification_sender/__init__.py +0 -0
  238. dmart/plugins/system_notification_sender/config.json +13 -0
  239. dmart/plugins/system_notification_sender/plugin.py +188 -0
  240. dmart/plugins/update_access_controls/__init__.py +0 -0
  241. dmart/plugins/update_access_controls/config.json +12 -0
  242. dmart/plugins/update_access_controls/plugin.py +9 -0
  243. dmart/publish.sh +57 -0
  244. dmart/pylint.sh +16 -0
  245. dmart/pyrightconfig.json +7 -0
  246. dmart/redis_connections.sh +13 -0
  247. dmart/reload.sh +56 -0
  248. dmart/run.sh +3 -0
  249. dmart/run_notification_campaign.py +85 -0
  250. dmart/sample/spaces/applications/.dm/meta.space.json +30 -0
  251. dmart/sample/spaces/applications/api/.dm/meta.folder.json +1 -0
  252. dmart/sample/spaces/applications/api/.dm/query_all_applications/meta.content.json +1 -0
  253. dmart/sample/spaces/applications/api/.dm/test_by_saad/attachments.media/meta.warframe.json +1 -0
  254. dmart/sample/spaces/applications/api/.dm/test_by_saad/attachments.media/warframe.png +0 -0
  255. dmart/sample/spaces/applications/api/.dm/test_by_saad/meta.content.json +1 -0
  256. dmart/sample/spaces/applications/api/.dm/user_profile/meta.content.json +1 -0
  257. dmart/sample/spaces/applications/api/applications/.dm/create_log/meta.content.json +1 -0
  258. dmart/sample/spaces/applications/api/applications/.dm/create_public_logs/meta.content.json +1 -0
  259. dmart/sample/spaces/applications/api/applications/.dm/meta.folder.json +1 -0
  260. dmart/sample/spaces/applications/api/applications/.dm/query_all_translated_data/meta.content.json +1 -0
  261. dmart/sample/spaces/applications/api/applications/.dm/query_logs/meta.content.json +1 -0
  262. dmart/sample/spaces/applications/api/applications/.dm/query_translated_enums/meta.content.json +1 -0
  263. dmart/sample/spaces/applications/api/applications/.dm/query_translated_others/meta.content.json +1 -0
  264. dmart/sample/spaces/applications/api/applications/.dm/query_translated_resolution/meta.content.json +1 -0
  265. dmart/sample/spaces/applications/api/applications/create_log.json +1 -0
  266. dmart/sample/spaces/applications/api/applications/create_public_logs.json +1 -0
  267. dmart/sample/spaces/applications/api/applications/query_all_translated_data.json +1 -0
  268. dmart/sample/spaces/applications/api/applications/query_logs.json +1 -0
  269. dmart/sample/spaces/applications/api/applications/query_translated_enums.json +1 -0
  270. dmart/sample/spaces/applications/api/applications/query_translated_others.json +1 -0
  271. dmart/sample/spaces/applications/api/applications/query_translated_resolution.json +1 -0
  272. dmart/sample/spaces/applications/api/applications.json +1 -0
  273. dmart/sample/spaces/applications/api/management/.dm/create_subaccount/meta.content.json +1 -0
  274. dmart/sample/spaces/applications/api/management/.dm/meta.folder.json +1 -0
  275. dmart/sample/spaces/applications/api/management/.dm/update_password/meta.content.json +1 -0
  276. dmart/sample/spaces/applications/api/management/create_subaccount.json +53 -0
  277. dmart/sample/spaces/applications/api/management/update_password.json +1 -0
  278. dmart/sample/spaces/applications/api/management.json +1 -0
  279. dmart/sample/spaces/applications/api/query_all_applications.json +15 -0
  280. dmart/sample/spaces/applications/api/test_by_saad.json +1 -0
  281. dmart/sample/spaces/applications/api/user/.dm/meta.folder.json +1 -0
  282. dmart/sample/spaces/applications/api/user/.dm/test_by_saad/meta.content.json +1 -0
  283. dmart/sample/spaces/applications/api/user/.dm/user_profile/meta.content.json +1 -0
  284. dmart/sample/spaces/applications/api/user/test_by_saad.json +1 -0
  285. dmart/sample/spaces/applications/api/user/user_profile.json +1 -0
  286. dmart/sample/spaces/applications/api/user_profile.json +1 -0
  287. dmart/sample/spaces/applications/api.json +1 -0
  288. dmart/sample/spaces/applications/collections/.dm/meta.folder.json +19 -0
  289. dmart/sample/spaces/applications/collections.json +1 -0
  290. dmart/sample/spaces/applications/configurations/.dm/meta.folder.json +1 -0
  291. dmart/sample/spaces/applications/configurations/time_out.json +1 -0
  292. dmart/sample/spaces/applications/configurations.json +19 -0
  293. dmart/sample/spaces/applications/errors.json +1 -0
  294. dmart/sample/spaces/applications/logs/.dm/meta.folder.json +1 -0
  295. dmart/sample/spaces/applications/logs.json +1 -0
  296. dmart/sample/spaces/applications/queries/.dm/meta.folder.json +1 -0
  297. dmart/sample/spaces/applications/queries/.dm/order/meta.content.json +1 -0
  298. dmart/sample/spaces/applications/queries/order.json +1 -0
  299. dmart/sample/spaces/applications/queries.json +1 -0
  300. dmart/sample/spaces/applications/schema/.dm/api/meta.schema.json +1 -0
  301. dmart/sample/spaces/applications/schema/.dm/configuration/meta.schema.json +1 -0
  302. dmart/sample/spaces/applications/schema/.dm/error/meta.schema.json +1 -0
  303. dmart/sample/spaces/applications/schema/.dm/log/meta.schema.json +1 -0
  304. dmart/sample/spaces/applications/schema/.dm/meta.folder.json +1 -0
  305. dmart/sample/spaces/applications/schema/.dm/query/meta.schema.json +16 -0
  306. dmart/sample/spaces/applications/schema/.dm/translation/meta.schema.json +1 -0
  307. dmart/sample/spaces/applications/schema/api.json +28 -0
  308. dmart/sample/spaces/applications/schema/configuration.json +1 -0
  309. dmart/sample/spaces/applications/schema/error.json +43 -0
  310. dmart/sample/spaces/applications/schema/log.json +1 -0
  311. dmart/sample/spaces/applications/schema/query.json +118 -0
  312. dmart/sample/spaces/applications/schema/translation.json +26 -0
  313. dmart/sample/spaces/applications/schema.json +1 -0
  314. dmart/sample/spaces/applications/translations/.dm/meta.folder.json +1 -0
  315. dmart/sample/spaces/applications/translations.json +1 -0
  316. dmart/sample/spaces/archive/.dm/meta.space.json +27 -0
  317. dmart/sample/spaces/custom_plugins/dummy/__pycache__/plugin.cpython-314.pyc +0 -0
  318. dmart/sample/spaces/custom_plugins/dummy/config.json +28 -0
  319. dmart/sample/spaces/custom_plugins/dummy/plugin.py +6 -0
  320. dmart/sample/spaces/custom_plugins/missed_entry/config.json +12 -0
  321. dmart/sample/spaces/custom_plugins/missed_entry/plugin.py +119 -0
  322. dmart/sample/spaces/custom_plugins/own_changed_notification/__pycache__/plugin.cpython-314.pyc +0 -0
  323. dmart/sample/spaces/custom_plugins/own_changed_notification/config.json +12 -0
  324. dmart/sample/spaces/custom_plugins/own_changed_notification/plugin.py +65 -0
  325. dmart/sample/spaces/custom_plugins/reports_stats/config.json +14 -0
  326. dmart/sample/spaces/custom_plugins/reports_stats/plugin.py +82 -0
  327. dmart/sample/spaces/custom_plugins/system_notification_sender/config.json +22 -0
  328. dmart/sample/spaces/custom_plugins/system_notification_sender/notification.py +268 -0
  329. dmart/sample/spaces/custom_plugins/system_notification_sender/plugin.py +98 -0
  330. dmart/sample/spaces/management/.dm/events.jsonl +32 -0
  331. dmart/sample/spaces/management/.dm/meta.space.json +48 -0
  332. dmart/sample/spaces/management/.dm/notifications/attachments.view.json/admin.json +36 -0
  333. dmart/sample/spaces/management/.dm/notifications/attachments.view.json/meta.admin.json +1 -0
  334. dmart/sample/spaces/management/.dm/notifications/attachments.view.json/meta.system.json +1 -0
  335. dmart/sample/spaces/management/.dm/notifications/attachments.view.json/system.json +32 -0
  336. dmart/sample/spaces/management/collections/.dm/meta.folder.json +1 -0
  337. dmart/sample/spaces/management/collections.json +1 -0
  338. dmart/sample/spaces/management/groups/.dm/meta.folder.json +1 -0
  339. dmart/sample/spaces/management/groups.json +1 -0
  340. dmart/sample/spaces/management/health_check/.dm/meta.folder.json +1 -0
  341. dmart/sample/spaces/management/health_check.json +1 -0
  342. dmart/sample/spaces/management/notifications/.dm/meta.folder.json +1 -0
  343. dmart/sample/spaces/management/notifications/admin/.dm/meta.folder.json +9 -0
  344. dmart/sample/spaces/management/notifications/system/.dm/meta.folder.json +9 -0
  345. dmart/sample/spaces/management/notifications.json +1 -0
  346. dmart/sample/spaces/management/permissions/.dm/access_applications/meta.permission.json +31 -0
  347. dmart/sample/spaces/management/permissions/.dm/access_applications_world/meta.permission.json +31 -0
  348. dmart/sample/spaces/management/permissions/.dm/access_messages/meta.permission.json +23 -0
  349. dmart/sample/spaces/management/permissions/.dm/access_personal/meta.permission.json +40 -0
  350. dmart/sample/spaces/management/permissions/.dm/access_protected/meta.permission.json +33 -0
  351. dmart/sample/spaces/management/permissions/.dm/access_public/meta.permission.json +24 -0
  352. dmart/sample/spaces/management/permissions/.dm/browse_all_folders/meta.permission.json +23 -0
  353. dmart/sample/spaces/management/permissions/.dm/create_log/meta.permission.json +24 -0
  354. dmart/sample/spaces/management/permissions/.dm/interviewer/meta.permission.json +1 -0
  355. dmart/sample/spaces/management/permissions/.dm/manage_applications/meta.permission.json +1 -0
  356. dmart/sample/spaces/management/permissions/.dm/manage_debug/meta.permission.json +25 -0
  357. dmart/sample/spaces/management/permissions/.dm/manage_spaces/meta.permission.json +24 -0
  358. dmart/sample/spaces/management/permissions/.dm/meta.folder.json +1 -0
  359. dmart/sample/spaces/management/permissions/.dm/rules_management_default/meta.permission.json +32 -0
  360. dmart/sample/spaces/management/permissions/.dm/super_manager/meta.permission.json +52 -0
  361. dmart/sample/spaces/management/permissions/.dm/view_activity_log/meta.permission.json +26 -0
  362. dmart/sample/spaces/management/permissions/.dm/view_collections/meta.permission.json +29 -0
  363. dmart/sample/spaces/management/permissions/.dm/view_logs/meta.permission.json +30 -0
  364. dmart/sample/spaces/management/permissions/.dm/view_roles/meta.permission.json +29 -0
  365. dmart/sample/spaces/management/permissions/.dm/view_users/meta.permission.json +25 -0
  366. dmart/sample/spaces/management/permissions/.dm/view_world/meta.permission.json +31 -0
  367. dmart/sample/spaces/management/permissions/.dm/world/meta.permission.json +35 -0
  368. dmart/sample/spaces/management/permissions.json +1 -0
  369. dmart/sample/spaces/management/requests.json +1 -0
  370. dmart/sample/spaces/management/roles/.dm/dummy/meta.role.json +12 -0
  371. dmart/sample/spaces/management/roles/.dm/logged_in/meta.role.json +18 -0
  372. dmart/sample/spaces/management/roles/.dm/manager/meta.role.json +13 -0
  373. dmart/sample/spaces/management/roles/.dm/meta.folder.json +1 -0
  374. dmart/sample/spaces/management/roles/.dm/moderator/meta.role.json +13 -0
  375. dmart/sample/spaces/management/roles/.dm/super_admin/meta.role.json +14 -0
  376. dmart/sample/spaces/management/roles/.dm/test_role/meta.role.json +13 -0
  377. dmart/sample/spaces/management/roles/.dm/world/meta.role.json +15 -0
  378. dmart/sample/spaces/management/roles.json +1 -0
  379. dmart/sample/spaces/management/schema/.dm/admin_notification_request/attachments.media/meta.ui_schema.json +10 -0
  380. dmart/sample/spaces/management/schema/.dm/admin_notification_request/attachments.media/ui_schema.json +32 -0
  381. dmart/sample/spaces/management/schema/.dm/admin_notification_request/meta.schema.json +1 -0
  382. dmart/sample/spaces/management/schema/.dm/api/meta.schema.json +1 -0
  383. dmart/sample/spaces/management/schema/.dm/folder_rendering/meta.schema.json +1 -0
  384. dmart/sample/spaces/management/schema/.dm/health_check/meta.schema.json +17 -0
  385. dmart/sample/spaces/management/schema/.dm/meta.folder.json +1 -0
  386. dmart/sample/spaces/management/schema/.dm/meta_schema/meta.schema.json +1 -0
  387. dmart/sample/spaces/management/schema/.dm/metafile/meta.schema.json +14 -0
  388. dmart/sample/spaces/management/schema/.dm/notification/meta.schema.json +1 -0
  389. dmart/sample/spaces/management/schema/.dm/system_notification_request/attachments.media/meta.ui_schema.json +10 -0
  390. dmart/sample/spaces/management/schema/.dm/system_notification_request/attachments.media/ui_schema.json +32 -0
  391. dmart/sample/spaces/management/schema/.dm/system_notification_request/meta.schema.json +1 -0
  392. dmart/sample/spaces/management/schema/.dm/view/meta.schema.json +1 -0
  393. dmart/sample/spaces/management/schema/.dm/workflow/meta.schema.json +1 -0
  394. dmart/sample/spaces/management/schema/admin_notification_request.json +89 -0
  395. dmart/sample/spaces/management/schema/api.json +1 -0
  396. dmart/sample/spaces/management/schema/folder_rendering.json +238 -0
  397. dmart/sample/spaces/management/schema/health_check.json +8 -0
  398. dmart/sample/spaces/management/schema/meta_schema.json +74 -0
  399. dmart/sample/spaces/management/schema/metafile.json +153 -0
  400. dmart/sample/spaces/management/schema/notification.json +28 -0
  401. dmart/sample/spaces/management/schema/system_notification_request.json +57 -0
  402. dmart/sample/spaces/management/schema/view.json +23 -0
  403. dmart/sample/spaces/management/schema/workflow.json +87 -0
  404. dmart/sample/spaces/management/schema.json +1 -0
  405. dmart/sample/spaces/management/users/.dm/alibaba/meta.user.json +23 -0
  406. dmart/sample/spaces/management/users/.dm/anonymous/meta.user.json +18 -0
  407. dmart/sample/spaces/management/users/.dm/dmart/meta.user.json +26 -0
  408. dmart/sample/spaces/management/users/.dm/meta.folder.json +14 -0
  409. dmart/sample/spaces/management/workflows/.dm/channel/meta.content.json +1 -0
  410. dmart/sample/spaces/management/workflows/.dm/meta.folder.json +1 -0
  411. dmart/sample/spaces/management/workflows/channel.json +148 -0
  412. dmart/sample/spaces/management/workflows.json +1 -0
  413. dmart/sample/spaces/maqola/.dm/meta.space.json +33 -0
  414. dmart/sample/spaces/personal/.dm/meta.space.json +24 -0
  415. dmart/sample/spaces/personal/people/.dm/meta.folder.json +1 -0
  416. dmart/sample/spaces/personal/people/dmart/.dm/meta.folder.json +1 -0
  417. dmart/sample/spaces/personal/people/dmart/messages/.dm/0b5f7e7f/meta.content.json +1 -0
  418. dmart/sample/spaces/personal/people/dmart/messages/.dm/meta.folder.json +1 -0
  419. dmart/sample/spaces/personal/people/dmart/messages/.dm/mytest/meta.content.json +1 -0
  420. dmart/sample/spaces/personal/people/dmart/messages/0b5f7e7f.json +1 -0
  421. dmart/sample/spaces/personal/people/dmart/messages/mytest.json +1 -0
  422. dmart/sample/spaces/personal/people/dmart/notifications/.dm/meta.folder.json +1 -0
  423. dmart/sample/spaces/personal/people/dmart/private/.dm/inner/meta.content.json +1 -0
  424. dmart/sample/spaces/personal/people/dmart/private/.dm/meta.folder.json +1 -0
  425. dmart/sample/spaces/personal/people/dmart/private/inner.json +1 -0
  426. dmart/sample/spaces/personal/people/dmart/protected/.dm/avatar/meta.content.json +1 -0
  427. dmart/sample/spaces/personal/people/dmart/protected/.dm/meta.folder.json +1 -0
  428. dmart/sample/spaces/personal/people/dmart/protected/avatar.png +0 -0
  429. dmart/sample/spaces/personal/people/dmart/public/.dm/meta.folder.json +1 -0
  430. dmart/sample/test/.gitignore +2 -0
  431. dmart/sample/test/createcontent.json +9 -0
  432. dmart/sample/test/createmedia.json +9 -0
  433. dmart/sample/test/createmedia_entry.json +6 -0
  434. dmart/sample/test/createschema.json +8 -0
  435. dmart/sample/test/createschemawork.json +11 -0
  436. dmart/sample/test/createticket.json +13 -0
  437. dmart/sample/test/data.json +4 -0
  438. dmart/sample/test/deletecontent.json +12 -0
  439. dmart/sample/test/logo.jpeg +0 -0
  440. dmart/sample/test/my.jpg +0 -0
  441. dmart/sample/test/myticket.json +23 -0
  442. dmart/sample/test/resources.csv +12 -0
  443. dmart/sample/test/schema.json +16 -0
  444. dmart/sample/test/temp.json +1 -0
  445. dmart/sample/test/test.dmart +45 -0
  446. dmart/sample/test/ticket_schema.json +23 -0
  447. dmart/sample/test/ticket_workflow.json +85 -0
  448. dmart/sample/test/ticketbody.json +4 -0
  449. dmart/sample/test/ticketcontent.json +14 -0
  450. dmart/sample/test/updatecontent.json +20 -0
  451. dmart/sample/test/workflow_schema.json +68 -0
  452. dmart/scheduled_notification_handler.py +121 -0
  453. dmart/schema_migration.py +208 -0
  454. dmart/schema_modulate.py +192 -0
  455. dmart/set_admin_passwd.py +75 -0
  456. dmart/sync.py +202 -0
  457. dmart/test_utils.py +34 -0
  458. dmart/utils/__init__.py +0 -0
  459. dmart/utils/access_control.py +306 -0
  460. dmart/utils/async_request.py +8 -0
  461. dmart/utils/exporter.py +309 -0
  462. dmart/utils/firebase_notifier.py +57 -0
  463. dmart/utils/generate_email.py +37 -0
  464. dmart/utils/helpers.py +352 -0
  465. dmart/utils/hypercorn_config.py +12 -0
  466. dmart/utils/internal_error_code.py +60 -0
  467. dmart/utils/jwt.py +124 -0
  468. dmart/utils/logger.py +167 -0
  469. dmart/utils/middleware.py +99 -0
  470. dmart/utils/notification.py +75 -0
  471. dmart/utils/password_hashing.py +16 -0
  472. dmart/utils/plugin_manager.py +202 -0
  473. dmart/utils/query_policies_helper.py +128 -0
  474. dmart/utils/regex.py +44 -0
  475. dmart/utils/repository.py +529 -0
  476. dmart/utils/router_helper.py +19 -0
  477. dmart/utils/settings.py +212 -0
  478. dmart/utils/sms_notifier.py +21 -0
  479. dmart/utils/social_sso.py +67 -0
  480. dmart/utils/templates/activation.html.j2 +26 -0
  481. dmart/utils/templates/reminder.html.j2 +17 -0
  482. dmart/utils/ticket_sys_utils.py +203 -0
  483. dmart/utils/web_notifier.py +29 -0
  484. dmart/websocket.py +231 -0
  485. dmart-1.4.40.post8.dist-info/METADATA +75 -0
  486. dmart-1.4.40.post8.dist-info/RECORD +489 -0
  487. dmart-1.4.40.post8.dist-info/WHEEL +5 -0
  488. dmart-1.4.40.post8.dist-info/entry_points.txt +2 -0
  489. dmart-1.4.40.post8.dist-info/top_level.txt +1 -0
dmart/get_settings.py ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env -S BACKEND_ENV=config.env python3
2
+
3
+ from utils.settings import settings
4
+
5
+
6
+ if __name__ == "__main__":
7
+ print(settings.model_dump_json())
@@ -0,0 +1,3 @@
1
+ backlog = 2000
2
+ workers = 1
3
+ bind = 'localhost:8282'
dmart/info.json ADDED
@@ -0,0 +1 @@
1
+ {"branch": "pypi", "version": "f9f97955", "tag": "v1.4.0-81-gf9f97955", "version_date": "'Thu Jan 22 08:46:11 2026 +0000'"}
File without changes
@@ -0,0 +1,15 @@
1
+ {
2
+ "invitation_message": "تهانينا، لقد تم الآن إنشاء حساب الخاص بك، يرجى اتباع هذا الرابط للتأكيد وتسجيل الدخول: {link} يمكن استخدام هذا الرابط مرة واحدة وخلال الـ 48 ساعة القادمة.",
3
+ "reset_message": "تم إرسال رابط إعادة تعيين كلمة المرور إلى بريدك الإلكتروني. {link} يمكن استخدام هذا الرابط مرة واحدة وخلال الـ 48 ساعة القادمة.",
4
+ "rejected": "مرفوض",
5
+ "pending": "قيد المراجعة",
6
+ "cancelled": "مُلغى",
7
+ "assigned": "مُعيّن",
8
+ "failed": "فشل التسليم",
9
+ "confirmed": "مؤكد",
10
+ "new": "جديد",
11
+ "completed": "مكتمل",
12
+ "approved": "مقبول",
13
+ "delivered": "تم التسليم",
14
+ "contract_submitted": "تم تصعيد العقد"
15
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "invitation_message": "Congratulations, your account is now created, please follow this link to confirm and login: {link} This link can be used once and within the next 48 hours.",
3
+ "reset_message": "A password reset link has been sent to your email. {link} This link can be used once and within the next 48 hours.",
4
+ "rejected": "rejected",
5
+ "accepted": "accepted",
6
+ "pending": "pending",
7
+ "cancelled": "cancelled",
8
+ "assigned": "assigned",
9
+ "failed": "failed",
10
+ "confirmed": "confirmed",
11
+ "new": "new",
12
+ "completed": "completed",
13
+ "approved": "approved",
14
+ "contract_submitted": "contract submitted"
15
+
16
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "invitation_message": "لە ڕێگەی ئەم بەستەرەوە ئەکاونتەکەت پشتڕاست بکەرەوە: {link} ئەم بەستەرە دەتوانرێت جارێک و لە ماوەی ٤٨ کاتژمێری داهاتوودا بەکاربهێنرێت.",
3
+ "reset_message": "بەستەری گۆڕانکاری وشەی نهێنی بۆ ئیمەیلەکەت نێردرا. {link} ئەم بەستەرە دەتوانرێت جارێک و لە ماوەی ٤٨ کاتژمێری داهاتوودا بەکاربهێنرێت.",
4
+ "rejected": "مرفوض",
5
+ "pending": "قيد الانتظار",
6
+ "cancelled": "ألغيت",
7
+ "assigned": "مُكَلَّف",
8
+ "failed": "فشل",
9
+ "confirmed": "مؤكد",
10
+ "new": "new",
11
+ "completed": "مكتمل",
12
+ "approved": "موافقة",
13
+ "contract_submitted": "تم تقديم العقد"
14
+ }
@@ -0,0 +1,12 @@
1
+ import json
2
+ from pathlib import Path
3
+
4
+
5
+ languages: dict[str, dict[str, str]] = {}
6
+ def load_langs() -> None:
7
+ languages_dir = Path(__file__).resolve().parent
8
+ language_list = list(languages_dir.glob("*.json"))
9
+ for lang in language_list:
10
+ lang_code = lang.stem
11
+ with open(lang, 'r', encoding='utf8') as file:
12
+ languages[lang_code] = json.load(file)
dmart/login_creds.sh ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+
3
+ # Super manager login creds. Do not leave spaces in the value
4
+ export SUPERMAN='{"shortname":"dmart","password":"Test1234"}'
5
+
6
+ # Exctra account used for testing
7
+ export ALIBABA='{"shortname":"alibaba","password":"Test1234"}'
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+
3
+ # Super manager login creds. Do not leave spaces in the value
4
+ export SUPERMAN='{"shortname":"dmart","password":"xxxx"}'
5
+
6
+ # Exctra account used for testing
7
+ export ALIBABA='{"shortname":"alibaba","password":"xxxx"}'
dmart/main.py ADDED
@@ -0,0 +1,563 @@
1
+ #!/usr/bin/env -S BACKEND_ENV=config.env python3
2
+ """ Main module """
3
+ import socket
4
+ from starlette.datastructures import UploadFile
5
+ from contextlib import asynccontextmanager
6
+ import asyncio
7
+ import json
8
+ import os
9
+ from os import getpid
10
+ import sys
11
+ import time
12
+ import traceback
13
+ from datetime import datetime
14
+ from typing import Any, cast
15
+ from urllib.parse import urlparse, quote
16
+ from jsonschema.exceptions import ValidationError as SchemaValidationError
17
+ from pydantic import ValidationError
18
+ from languages.loader import load_langs
19
+ from utils.middleware import CustomRequestMiddleware, ChannelMiddleware
20
+ from utils.jwt import JWTBearer
21
+ from utils.plugin_manager import plugin_manager
22
+ from fastapi import Depends, FastAPI, Request, Response, status
23
+ from utils.logger import logging_schema
24
+ from fastapi.logger import logger
25
+ from fastapi.encoders import jsonable_encoder
26
+ from fastapi.exceptions import RequestValidationError
27
+ from utils.access_control import access_control
28
+ from fastapi.responses import ORJSONResponse, FileResponse
29
+ from hypercorn.asyncio import serve
30
+ from hypercorn.config import Config
31
+ from starlette.concurrency import iterate_in_threadpool
32
+ from starlette.exceptions import HTTPException as StarletteHTTPException
33
+ from starlette.staticfiles import StaticFiles
34
+ import models.api as api
35
+ from utils.settings import settings
36
+ from asgi_correlation_id import CorrelationIdMiddleware
37
+ from data_adapters.adapter import data_adapter as db
38
+ from api.managed.router import router as managed
39
+ from api.qr.router import router as qr
40
+ from api.public.router import router as public
41
+ from api.user.router import router as user
42
+ from api.info.router import router as info, git_info
43
+ from utils.internal_error_code import InternalErrorCode
44
+
45
+
46
+ class SPAStaticFiles(StaticFiles):
47
+ async def get_response(self, path: str, scope) -> Response:
48
+ try:
49
+ return await super().get_response(path, scope)
50
+ except StarletteHTTPException as ex:
51
+ if ex.status_code == 404 and path != "index.html" and not os.path.splitext(path)[1]:
52
+ try:
53
+ return await super().get_response("index.html", scope)
54
+ except StarletteHTTPException:
55
+ pass
56
+ raise ex
57
+
58
+
59
+ @asynccontextmanager
60
+ async def lifespan(app: FastAPI):
61
+ logger.info("Starting up")
62
+ print('{"stage":"starting up"}')
63
+
64
+ openapi_schema = app.openapi()
65
+ paths = openapi_schema["paths"]
66
+ for path in paths:
67
+ for method in paths[path]:
68
+ responses = paths[path][method]["responses"]
69
+ if responses.get("422"):
70
+ responses.pop("422")
71
+ app.openapi_schema = openapi_schema
72
+
73
+ await db.initialize_spaces()
74
+ await access_control.load_permissions_and_roles()
75
+ # await plugin_manager.load_plugins(app, capture_body)
76
+ yield
77
+
78
+
79
+ logger.info("Application shutting down")
80
+ print('{"stage":"shutting down"}')
81
+
82
+
83
+ app = FastAPI(
84
+ lifespan=lifespan,
85
+ title="Datamart API",
86
+ description="Structured Content Management System",
87
+ version=str(git_info["tag"]),
88
+ redoc_url=None,
89
+ docs_url=f"{settings.base_path}/docs",
90
+ openapi_url=f"{settings.base_path}/openapi.json",
91
+ servers=[{"url": f"{settings.base_path}/"}],
92
+ contact={
93
+ "name": "Kefah T. Issa",
94
+ "url": "https://dmart.cc",
95
+ "email": "kefah.issa@gmail.com",
96
+ },
97
+ license_info={
98
+ "name": "GNU Affero General Public License v3+",
99
+ "url": "https://www.gnu.org/licenses/agpl-3.0.en.html",
100
+ },
101
+ swagger_ui_parameters={"defaultModelsExpandDepth": -1},
102
+ openapi_tags=[
103
+ {"name": "user", "description": "User registration, login, profile and delete"},
104
+ {
105
+ "name": "managed",
106
+ "description": "Login-only content management api and media upload",
107
+ },
108
+ {
109
+ "name": "public",
110
+ "description": "Public api for query and GET access to media",
111
+ },
112
+ ],
113
+ default_response_class=ORJSONResponse,
114
+ )
115
+
116
+
117
+ async def capture_body(request: Request):
118
+ request.state.request_body = {}
119
+
120
+ if (
121
+ request.method == "POST"
122
+ and "application/json" in request.headers.get("content-type", "")
123
+ ):
124
+ request.state.request_body = await request.json()
125
+
126
+ if (
127
+ request.method == "POST"
128
+ and request.headers.get("content-type")
129
+ and "multipart/form-data" in request.headers.get("content-type", [])
130
+ ):
131
+ form = await request.form()
132
+ for field in form:
133
+ one = form[field]
134
+ if isinstance(one, str):
135
+ request.state.request_body[field] = form.get(field)
136
+ elif isinstance(one, UploadFile):
137
+ # TODO try to find a way to capture .json file content without await exeption
138
+ # inner_json= form.get(field).file
139
+ # form_to_dict[field]["file_name"]=form.get(field).filename
140
+ # form_to_dict[field]["content_type"]=form.get(field).content_type
141
+ request.state.request_body[field] = {
142
+ "name": one.filename,
143
+ "content_type": one.content_type,
144
+ }
145
+
146
+
147
+ @app.exception_handler(StarletteHTTPException)
148
+ async def my_exception_handler(_, exception):
149
+ return ORJSONResponse(content=exception.detail, status_code=exception.status_code)
150
+
151
+
152
+ @app.exception_handler(RequestValidationError)
153
+ async def validation_exception_handler(_: Request, exc: RequestValidationError):
154
+ err = jsonable_encoder({"detail": exc.errors()})["detail"]
155
+ raise api.Exception(
156
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
157
+ error=api.Error(
158
+ code=InternalErrorCode.UNPROCESSABLE_ENTITY, type="validation", message="Validation error [1]", info=err
159
+ ),
160
+ )
161
+
162
+
163
+ app.add_middleware(CustomRequestMiddleware)
164
+ app.add_middleware(ChannelMiddleware)
165
+
166
+
167
+ def set_middleware_extra(request, response, start_time, user_shortname, exception_data, response_body):
168
+ extra = {
169
+ "props": {
170
+ "timestamp": start_time,
171
+ "duration": 1000 * (time.time() - start_time),
172
+ "server": socket.gethostname(),
173
+ "process_id": getpid(),
174
+ "user_shortname": user_shortname,
175
+ "request": {
176
+ "url": request.url._url,
177
+ "verb": request.method,
178
+ "path": quote(str(request.url.path)),
179
+ "query_params": dict(request.query_params.items()),
180
+ "headers": dict(request.headers.items()),
181
+ },
182
+ "response": {
183
+ "headers": dict(response.headers.items()),
184
+ "http_status": response.status_code,
185
+ },
186
+ }
187
+ }
188
+
189
+ if exception_data is not None:
190
+ extra["props"]["exception"] = exception_data
191
+
192
+ if hasattr(request.state, "request_body"):
193
+ extra["props"]["request"]["body"] = request.state.request_body
194
+
195
+ if response_body and isinstance(response_body, dict):
196
+ extra["props"]["response"]["body"] = response_body
197
+
198
+ return extra
199
+
200
+
201
+
202
+ def set_middleware_response_headers(request, response):
203
+ referer = request.headers.get(
204
+ "referer",
205
+ request.headers.get("origin",
206
+ request.headers.get("x-forwarded-proto", "http")
207
+ + "://"
208
+ + request.headers.get(
209
+ "x-forwarded-host", f"{settings.listening_host}:{settings.listening_port}"
210
+ )),
211
+ )
212
+ origin = urlparse(referer)
213
+ response.headers[
214
+ "Access-Control-Allow-Origin"
215
+ ] = f"{origin.scheme}://{origin.netloc}"
216
+
217
+
218
+ response.headers["Access-Control-Allow-Credentials"] = "true"
219
+ response.headers["Access-Control-Allow-Headers"] = "content-type, charset, authorization, accept-language, content-length"
220
+ response.headers["Access-Control-Max-Age"] = "600"
221
+ response.headers[
222
+ "Access-Control-Allow-Methods"
223
+ ] = "OPTIONS, DELETE, POST, GET, PATCH, PUT"
224
+
225
+ response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
226
+ response.headers["Pragma"] = "no-cache"
227
+ response.headers["Expires"] = "0"
228
+ response.headers["x-server-time"] = datetime.now().isoformat()
229
+ response.headers["Access-Control-Expose-Headers"] = "x-server-time"
230
+ return response
231
+
232
+
233
+ def mask_sensitive_data(data):
234
+ if isinstance(data, dict):
235
+ return {k: mask_sensitive_data(v) if k not in ['password', 'access_token', 'refresh_token', 'auth_token', 'jwt', 'otp', 'code', 'token'] else '******' for k, v in data.items()}
236
+ elif isinstance(data, list):
237
+ return [mask_sensitive_data(item) for item in data]
238
+ elif isinstance(data, str) and 'auth_token' in data:
239
+ return '******'
240
+ return data
241
+
242
+
243
+ def set_logging(response, extra, request, exception_data):
244
+ _extra = mask_sensitive_data(extra)
245
+ if isinstance(_extra, dict):
246
+ if 400 <= response.status_code < 500:
247
+ logger.warning("Served request", extra=_extra)
248
+ elif response.status_code >= 500 or exception_data is not None:
249
+ logger.error("Served request", extra=_extra)
250
+ elif request.method != "OPTIONS": # Do not log OPTIONS request, to reduce excessive logging
251
+ logger.info("Served request", extra=_extra)
252
+
253
+
254
+ def set_stack(e):
255
+ return [
256
+ {
257
+ "file": frame.f_code.co_filename,
258
+ "function": frame.f_code.co_name,
259
+ "line": lineno,
260
+ }
261
+ for frame, lineno in traceback.walk_tb(e.__traceback__)
262
+ if "site-packages" not in frame.f_code.co_filename
263
+ ]
264
+
265
+ @app.middleware("http")
266
+ async def middle(request: Request, call_next):
267
+ """Wrapper function to manage errors and logging"""
268
+ if (
269
+ request.url._url.endswith("/docs")
270
+ or request.url._url.endswith("openapi.json")
271
+ or request.url._url.endswith("/favicon.ico")
272
+ ):
273
+ return await call_next(request)
274
+
275
+ start_time = time.time()
276
+ response_body: str | dict = {}
277
+ exception_data: dict[str, Any] | None = None
278
+
279
+
280
+ try:
281
+ response = await asyncio.wait_for(call_next(request), timeout=settings.request_timeout)
282
+ raw_response = [section async for section in response.body_iterator]
283
+ response.body_iterator = iterate_in_threadpool(iter(raw_response))
284
+ raw_data = b"".join(raw_response)
285
+ if raw_data:
286
+ try:
287
+ response_body = json.loads(raw_data)
288
+ except Exception:
289
+ response_body = {}
290
+ except asyncio.TimeoutError:
291
+ response = ORJSONResponse(content={'status':'failed',
292
+ 'error': {"code":504, "message": 'Request processing time excedeed limit'}},
293
+ status_code=status.HTTP_504_GATEWAY_TIMEOUT)
294
+ response_body = json.loads(str(response.body, 'utf8'))
295
+ except api.Exception as e:
296
+ if settings.active_data_db == 'sql':
297
+ if e.error.message.startswith('(sqlalchemy.dialects.postgresql'):
298
+ response = ORJSONResponse(
299
+ status_code=500,
300
+ content={
301
+ "status": "failed",
302
+ "error": 'Something went wrong',
303
+ },
304
+ )
305
+ else:
306
+ response = ORJSONResponse(
307
+ status_code=e.status_code,
308
+ content=jsonable_encoder(
309
+ api.Response(status=api.Status.failed, error=e.error)
310
+ ),
311
+ )
312
+ else:
313
+ response = ORJSONResponse(
314
+ status_code=e.status_code,
315
+ content=jsonable_encoder(
316
+ api.Response(status=api.Status.failed, error=e.error)
317
+ ),
318
+ )
319
+ stack = set_stack(e)
320
+ exception_data = {"props": {"exception": str(e), "stack": stack}}
321
+ response_body = json.loads(str(response.body, 'utf8'))
322
+ except ValidationError as e:
323
+ stack = set_stack(e)
324
+ exception_data = {"props": {"exception": str(e), "stack": stack}}
325
+ response = ORJSONResponse(
326
+ status_code=422,
327
+ content={
328
+ "status": "failed",
329
+ "error": {
330
+ "type": "validation",
331
+ "code": 422,
332
+ "message": "Validation error [2]",
333
+ "info": jsonable_encoder(e.errors()),
334
+ },
335
+ },
336
+ )
337
+ response_body = json.loads(str(response.body, 'utf8'))
338
+ except SchemaValidationError as e:
339
+ stack = set_stack(e)
340
+ exception_data = {"props": {"exception": str(e), "stack": stack}}
341
+ response = ORJSONResponse(
342
+ status_code=400,
343
+ content={
344
+ "status": "failed",
345
+ "error": {
346
+ "type": "validation",
347
+ "code": 422,
348
+ "message": "Validation error [3]",
349
+ "info": [{
350
+ "loc": list(e.path),
351
+ "msg": e.message
352
+ }],
353
+ },
354
+ },
355
+ )
356
+ response_body = json.loads(str(response.body, 'utf8'))
357
+ except Exception:
358
+ exception_message = ""
359
+ stack = None
360
+ if ee := sys.exc_info()[1]:
361
+ stack = set_stack(ee)
362
+ exception_message = str(ee)
363
+ exception_data = {"props": {"exception": str(ee), "stack": stack}}
364
+
365
+ error_log = {"type": "general", "code": 99, "message": exception_message}
366
+ if settings.debug_enabled:
367
+ error_log["stack"] = stack
368
+ response = ORJSONResponse(
369
+ status_code=500,
370
+ content={
371
+ "status": "failed",
372
+ "error": error_log,
373
+ },
374
+ )
375
+ response_body = json.loads(str(response.body, 'utf8'))
376
+
377
+ response = set_middleware_response_headers(request, response)
378
+
379
+ user_shortname = "guest"
380
+ if request.url.path == "/user/login":
381
+ try:
382
+ body = getattr(request.state, "request_body", {}) or {}
383
+ if isinstance(body, dict):
384
+ shortname_value = body.get("shortname")
385
+ if isinstance(shortname_value, str) and shortname_value.strip():
386
+ user_shortname = shortname_value
387
+ except Exception:
388
+ pass
389
+ else:
390
+ try:
391
+ user_shortname = str(await JWTBearer().__call__(request))
392
+ except Exception:
393
+ user_shortname = "guest"
394
+
395
+
396
+ extra = set_middleware_extra(request, response, start_time, user_shortname, exception_data, response_body)
397
+
398
+ set_logging(response, extra, request, exception_data)
399
+
400
+ #TODO: CHECK THIS
401
+ # if settings.hide_stack_trace:
402
+ # if (
403
+ # response_body and isinstance(response_body, dict)
404
+ # and "error" in response_body
405
+ # and "stack" in response_body["error"]
406
+ # ):
407
+ # response_body["error"].pop("stack", None)
408
+ #
409
+ # response.body_iterator = iterate_in_threadpool(iter([json.dumps(response_body).encode("utf-8")]))
410
+
411
+ return response
412
+
413
+
414
+ app.add_middleware(
415
+ CorrelationIdMiddleware,
416
+ header_name='X-Correlation-ID',
417
+ update_request_header=False,
418
+ validator=None,
419
+ )
420
+
421
+
422
+ @app.get("/", include_in_schema=False)
423
+ async def root():
424
+ """Dummy api end point"""
425
+ return {"status": "success", "message": "DMART API"}
426
+
427
+
428
+ # @app.get("/s", include_in_schema=False)
429
+ # async def secrets(key):
430
+ # if key == "alpha":
431
+ # return settings.dict()
432
+
433
+ """
434
+ @app.get("/spaces-backup", include_in_schema=False)
435
+ async def space_backup(key: str):
436
+ if not key or key != "ABC":
437
+ return api.Response(
438
+ status=api.Status.failed,
439
+ error=api.Error(type="git", code=InternalErrorCode.INVALID_APP_KEY, message="Api key is invalid"),
440
+ )
441
+
442
+ import subprocess
443
+
444
+ cmd = "/usr/bin/bash -c 'cd .. && ./spaces-backup.sh'"
445
+ # cmd = "../git-update.sh"
446
+
447
+ result_stdout, result_stderr = subprocess.Popen(
448
+ cmd.split(" "), stdout=subprocess.PIPE, stderr=subprocess.PIPE
449
+ ).communicate()
450
+ attributes = {
451
+ "stdout": result_stdout.decode().split("\n"),
452
+ "stderr": result_stderr.decode().split("\n"),
453
+ }
454
+ return api.Response(status=api.Status.success, attributes=attributes)
455
+ """
456
+
457
+ app.include_router(
458
+ user, prefix="/user", tags=["user"], dependencies=[Depends(capture_body)]
459
+ )
460
+ app.include_router(
461
+ managed, prefix="/managed", tags=["managed"], dependencies=[Depends(capture_body)]
462
+ )
463
+ app.include_router(
464
+ qr,
465
+ prefix="/qr",
466
+ tags=["QR"],
467
+ dependencies=[Depends(capture_body)],
468
+ )
469
+
470
+ app.include_router(
471
+ public, prefix="/public", tags=["public"], dependencies=[Depends(capture_body)]
472
+ )
473
+
474
+ app.include_router(
475
+ info, prefix="/info", tags=["info"], dependencies=[Depends(capture_body)]
476
+ )
477
+
478
+ # load plugins
479
+ asyncio.run(plugin_manager.load_plugins(app, capture_body))
480
+
481
+
482
+ cxb_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "cxb")
483
+ if os.path.isdir(os.path.join(cxb_path, "client")):
484
+ cxb_path = os.path.join(cxb_path, "client")
485
+
486
+ if not os.path.exists(os.path.join(cxb_path, "index.html")):
487
+ project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
488
+ cxb_dist_path = os.path.join(project_root, "cxb", "dist", "client")
489
+ if os.path.isdir(cxb_dist_path):
490
+ cxb_path = cxb_dist_path
491
+
492
+ if os.path.isdir(cxb_path):
493
+ @app.get(f"{settings.cxb_url}/config.json", include_in_schema=False)
494
+ async def get_cxb_config():
495
+ cxb_config = os.getenv("DMART_CXB_CONFIG")
496
+ if cxb_config and os.path.exists(cxb_config):
497
+ return FileResponse(cxb_config)
498
+
499
+ if os.path.exists("config.json"):
500
+ return FileResponse("config.json")
501
+
502
+ user_config = settings.spaces_folder / "config.json"
503
+ if user_config.exists():
504
+ return FileResponse(user_config)
505
+
506
+ bundled_config = os.path.join(cxb_path, "config.json")
507
+ if os.path.exists(bundled_config):
508
+ return FileResponse(bundled_config)
509
+
510
+ return {
511
+ "title": "DMART Unified Data Platform",
512
+ "footer": "dmart.cc unified data platform",
513
+ "short_name": "dmart",
514
+ "display_name": "dmart",
515
+ "description": "dmart unified data platform",
516
+ "default_language": "en",
517
+ "languages": { "ar": "العربية", "en": "English" },
518
+ "backend": f"{settings.app_url}" if settings.app_url else f"http://{settings.listening_host}:{settings.listening_port}",
519
+ "websocket": settings.websocket_url if settings.websocket_url else f"ws://{settings.listening_host}:{settings.websocket_port}/ws"
520
+ }
521
+
522
+ app.mount(settings.cxb_url, SPAStaticFiles(directory=cxb_path, html=True), name="cxb")
523
+
524
+ @app.options("/{x:path}", include_in_schema=False)
525
+ async def myoptions():
526
+ return Response(status_code=status.HTTP_200_OK)
527
+
528
+
529
+ @app.get("/{x:path}", include_in_schema=False)
530
+ @app.post("/{x:path}", include_in_schema=False)
531
+ @app.put("/{x:path}", include_in_schema=False)
532
+ @app.patch("/{x:path}", include_in_schema=False)
533
+ @app.delete("/{x:path}", include_in_schema=False)
534
+ async def catchall() -> None:
535
+ raise api.Exception(
536
+ status_code=status.HTTP_404_NOT_FOUND,
537
+ error=api.Error(
538
+ type="catchall", code=InternalErrorCode.INVALID_ROUTE, message="Requested method or path is invalid"
539
+ ),
540
+ )
541
+
542
+ load_langs()
543
+
544
+
545
+ async def main():
546
+ config = Config()
547
+ config.bind = [f"{settings.listening_host}:{settings.listening_port}"]
548
+ config.backlog = 200
549
+
550
+ config.logconfig_dict = logging_schema
551
+ config.errorlog = logger
552
+
553
+ try:
554
+ await serve(cast(Any, app), config)
555
+ except OSError as e:
556
+ print("[!1server]", e)
557
+
558
+
559
+ if __name__ == "__main__":
560
+ try:
561
+ asyncio.run(main())
562
+ except Exception as e:
563
+ print("[!1server]", e)
dmart/manifest.sh ADDED
@@ -0,0 +1,12 @@
1
+ #!/bin/bash
2
+
3
+ source ./login_creds.sh
4
+
5
+ APP_URL="http://localhost:8282"
6
+
7
+
8
+ TOKEN=$(curl -s "${APP_URL}/user/login" -H 'Content-Type: application/json' -d "${SUPERMAN}" | jq -r '.records[0].attributes.access_token')
9
+ # curl -s -H "Authorization: Bearer ${TOKEN}" "${APP_URL}/managed/reload-security-data" | jq .
10
+ # curl -s -H "Authorization: Bearer ${TOKEN}" "${APP_URL}/info/me" | jq .
11
+ curl -s -H "Authorization: Bearer ${TOKEN}" "${APP_URL}/info/manifest" | jq .
12
+ # curl -s -H "Authorization: Bearer ${TOKEN}" "${APP_URL}/info/settings" | jq .