dmart 1.4.17__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 (289) hide show
  1. alembic.ini +117 -0
  2. api/__init__.py +0 -0
  3. api/info/__init__.py +0 -0
  4. api/info/router.py +109 -0
  5. api/managed/__init__.py +0 -0
  6. api/managed/router.py +1541 -0
  7. api/managed/utils.py +1879 -0
  8. api/public/__init__.py +0 -0
  9. api/public/router.py +758 -0
  10. api/qr/__init__.py +0 -0
  11. api/qr/router.py +108 -0
  12. api/user/__init__.py +0 -0
  13. api/user/model/__init__.py +0 -0
  14. api/user/model/errors.py +14 -0
  15. api/user/model/requests.py +165 -0
  16. api/user/model/responses.py +11 -0
  17. api/user/router.py +1413 -0
  18. api/user/service.py +270 -0
  19. bundler.py +55 -0
  20. config/__init__.py +0 -0
  21. config/channels.json +11 -0
  22. config/notification.json +17 -0
  23. cxb/__init__.py +0 -0
  24. cxb/client/__init__.py +0 -0
  25. cxb/client/assets/@codemirror-Rn7_6DkE.js +10 -0
  26. cxb/client/assets/@edraj-CS4NwVbD.js +1 -0
  27. cxb/client/assets/@floating-ui-BwwcF-xh.js +1 -0
  28. cxb/client/assets/@formatjs-yKEsAtjs.js +1 -0
  29. cxb/client/assets/@fortawesome-DRW1UCdr.js +9 -0
  30. cxb/client/assets/@jsonquerylang-laKNoFFq.js +12 -0
  31. cxb/client/assets/@lezer-za4Q-8Ew.js +1 -0
  32. cxb/client/assets/@marijn-DXwl3gUT.js +1 -0
  33. cxb/client/assets/@popperjs-l0sNRNKZ.js +1 -0
  34. cxb/client/assets/@replit--ERk53eB.js +1 -0
  35. cxb/client/assets/@roxi-CGMFK4i8.js +6 -0
  36. cxb/client/assets/@typewriter-cCzskkIv.js +17 -0
  37. cxb/client/assets/@zerodevx-BlBZjKxu.js +1 -0
  38. cxb/client/assets/@zerodevx-CVEpe6WZ.css +1 -0
  39. cxb/client/assets/BreadCrumbLite-DAhOx38v.js +1 -0
  40. cxb/client/assets/EntryRenderer-25YDhRen.js +32 -0
  41. cxb/client/assets/EntryRenderer-DXytdFp9.css +1 -0
  42. cxb/client/assets/ListView-BpAycA2h.js +16 -0
  43. cxb/client/assets/ListView-U8of-_c-.css +1 -0
  44. cxb/client/assets/Prism--hMplq-p.js +3 -0
  45. cxb/client/assets/Prism-Uh6uStUw.css +1 -0
  46. cxb/client/assets/Table2Cols-BsbwicQm.js +1 -0
  47. cxb/client/assets/_..-BvT6vdHa.css +1 -0
  48. cxb/client/assets/_...404_-fuLH_rX9.js +2 -0
  49. cxb/client/assets/_...fallback_-Ba_NLmAE.js +1 -0
  50. cxb/client/assets/_module-Bfk8MiCs.js +3 -0
  51. cxb/client/assets/_module-CEW0D5oI.js +4 -0
  52. cxb/client/assets/_module-Dgq0ZVtz.js +1 -0
  53. cxb/client/assets/ajv-Cpj98o6Y.js +1 -0
  54. cxb/client/assets/axios-CG2WSiiR.js +6 -0
  55. cxb/client/assets/clsx-B-dksMZM.js +1 -0
  56. cxb/client/assets/codemirror-wrapped-line-indent-DPhKvljI.js +1 -0
  57. cxb/client/assets/compare-C3AjiGFR.js +1 -0
  58. cxb/client/assets/compute-scroll-into-view-Bl8rNFhg.js +1 -0
  59. cxb/client/assets/consolite-DlCuI0F9.js +1 -0
  60. cxb/client/assets/crelt-C8TCjufn.js +1 -0
  61. cxb/client/assets/date-fns-l0sNRNKZ.js +1 -0
  62. cxb/client/assets/deepmerge-rn4rBaHU.js +1 -0
  63. cxb/client/assets/dmart_services-AL6-IdDE.js +1 -0
  64. cxb/client/assets/downloadFile-D08i0YDh.js +1 -0
  65. cxb/client/assets/easy-signal-BiPFIK3O.js +1 -0
  66. cxb/client/assets/esm-env-rsSWfq8L.js +1 -0
  67. cxb/client/assets/export-OF_rTiXu.js +1 -0
  68. cxb/client/assets/fast-deep-equal-l0sNRNKZ.js +1 -0
  69. cxb/client/assets/fast-diff-C-IidNf4.js +1 -0
  70. cxb/client/assets/fast-uri-l0sNRNKZ.js +1 -0
  71. cxb/client/assets/flowbite-svelte-BLvjb-sa.js +1 -0
  72. cxb/client/assets/flowbite-svelte-CD54FDqW.css +1 -0
  73. cxb/client/assets/flowbite-svelte-icons-BI8GVhw_.js +1 -0
  74. cxb/client/assets/github-slugger-CQ4oX9Ud.js +1 -0
  75. cxb/client/assets/global-igKv-1g9.js +1 -0
  76. cxb/client/assets/hookar-BMRD9G9H.js +1 -0
  77. cxb/client/assets/immutable-json-patch-DtRO2E_S.js +1 -0
  78. cxb/client/assets/import-1vE3gBat.js +1 -0
  79. cxb/client/assets/index-B-eTh-ZX.js +1 -0
  80. cxb/client/assets/index-BVyxzKtH.js +1 -0
  81. cxb/client/assets/index-BdeNM69f.js +1 -0
  82. cxb/client/assets/index-C6cPO4op.js +1 -0
  83. cxb/client/assets/index-CC-A1ipE.js +1 -0
  84. cxb/client/assets/index-CTxJ-lDp.js +1 -0
  85. cxb/client/assets/index-Cd-F5j_k.js +1 -0
  86. cxb/client/assets/index-D742rwaM.js +1 -0
  87. cxb/client/assets/index-DTfhnhwd.js +1 -0
  88. cxb/client/assets/index-DdXRK7n9.js +2 -0
  89. cxb/client/assets/index-DtiCmB4o.js +1 -0
  90. cxb/client/assets/index-NBrXBlLA.css +2 -0
  91. cxb/client/assets/index-ac-Buu_H.js +4 -0
  92. cxb/client/assets/index-iYkH7C67.js +1 -0
  93. cxb/client/assets/info-B986lRiM.js +1 -0
  94. cxb/client/assets/intl-messageformat-Dc5UU-HB.js +3 -0
  95. cxb/client/assets/jmespath-l0sNRNKZ.js +1 -0
  96. cxb/client/assets/json-schema-traverse-l0sNRNKZ.js +1 -0
  97. cxb/client/assets/json-source-map-DRgZidqy.js +5 -0
  98. cxb/client/assets/jsonpath-plus-l0sNRNKZ.js +1 -0
  99. cxb/client/assets/jsonrepair-B30Dx381.js +8 -0
  100. cxb/client/assets/lodash-es-DZVAA2ox.js +1 -0
  101. cxb/client/assets/marked-DKjyhwJX.js +56 -0
  102. cxb/client/assets/marked-gfm-heading-id-U5zO829x.js +2 -0
  103. cxb/client/assets/marked-mangle-CDMeiHC6.js +1 -0
  104. cxb/client/assets/memoize-one-BdPwpGay.js +1 -0
  105. cxb/client/assets/natural-compare-lite-Bg2Xcf-o.js +7 -0
  106. cxb/client/assets/pagination-svelte-D5CyoiE_.js +13 -0
  107. cxb/client/assets/pagination-svelte-v10nAbbM.css +1 -0
  108. cxb/client/assets/plantuml-encoder-C47mzt9T.js +1 -0
  109. cxb/client/assets/prismjs-DTUiLGJu.js +9 -0
  110. cxb/client/assets/profile-BUf-tKMe.js +1 -0
  111. cxb/client/assets/query-CNmXTsgf.js +1 -0
  112. cxb/client/assets/queryHelpers-C9iBWwqe.js +1 -0
  113. cxb/client/assets/scroll-into-view-if-needed-KR58zyjF.js +1 -0
  114. cxb/client/assets/spaces-0oyGvpii.js +1 -0
  115. cxb/client/assets/style-mod-Bs6eFhZE.js +3 -0
  116. cxb/client/assets/svelte-B2XmcTi_.js +4 -0
  117. cxb/client/assets/svelte-awesome-COLlx0DN.css +1 -0
  118. cxb/client/assets/svelte-awesome-DhnMA6Q_.js +1 -0
  119. cxb/client/assets/svelte-datatables-net-CY7LBj6I.js +1 -0
  120. cxb/client/assets/svelte-floating-ui-BlS3sOAQ.js +1 -0
  121. cxb/client/assets/svelte-i18n-CT2KkQaN.js +3 -0
  122. cxb/client/assets/svelte-jsoneditor-BzfX6Usi.css +1 -0
  123. cxb/client/assets/svelte-jsoneditor-CUGSvWId.js +25 -0
  124. cxb/client/assets/svelte-select-CegQKzqH.css +1 -0
  125. cxb/client/assets/svelte-select-CjHAt_85.js +6 -0
  126. cxb/client/assets/tailwind-merge-CJvxXMcu.js +1 -0
  127. cxb/client/assets/tailwind-variants-Cj20BoQ3.js +1 -0
  128. cxb/client/assets/toast-B9WDyfyI.js +1 -0
  129. cxb/client/assets/tslib-pJfR_DrR.js +1 -0
  130. cxb/client/assets/typewriter-editor-DkTVIJdm.js +25 -0
  131. cxb/client/assets/user-DeK_NB5v.js +1 -0
  132. cxb/client/assets/vanilla-picker-l5rcX3cq.js +8 -0
  133. cxb/client/assets/w3c-keyname-Vcq4gwWv.js +1 -0
  134. cxb/client/config.json +11 -0
  135. cxb/client/config.sample.json +11 -0
  136. cxb/client/favicon.ico +0 -0
  137. cxb/client/favicon.png +0 -0
  138. cxb/client/index.html +28 -0
  139. data_adapters/__init__.py +0 -0
  140. data_adapters/adapter.py +16 -0
  141. data_adapters/base_data_adapter.py +467 -0
  142. data_adapters/file/__init__.py +0 -0
  143. data_adapters/file/adapter.py +2043 -0
  144. data_adapters/file/adapter_helpers.py +1013 -0
  145. data_adapters/file/archive.py +150 -0
  146. data_adapters/file/create_index.py +331 -0
  147. data_adapters/file/create_users_folders.py +52 -0
  148. data_adapters/file/custom_validations.py +68 -0
  149. data_adapters/file/drop_index.py +40 -0
  150. data_adapters/file/health_check.py +560 -0
  151. data_adapters/file/redis_services.py +1110 -0
  152. data_adapters/helpers.py +27 -0
  153. data_adapters/sql/__init__.py +0 -0
  154. data_adapters/sql/adapter.py +3218 -0
  155. data_adapters/sql/adapter_helpers.py +491 -0
  156. data_adapters/sql/create_tables.py +451 -0
  157. data_adapters/sql/create_users_folders.py +53 -0
  158. data_adapters/sql/db_to_json_migration.py +485 -0
  159. data_adapters/sql/health_check_sql.py +232 -0
  160. data_adapters/sql/json_to_db_migration.py +454 -0
  161. data_adapters/sql/update_query_policies.py +101 -0
  162. data_generator.py +81 -0
  163. dmart-1.4.17.dist-info/METADATA +65 -0
  164. dmart-1.4.17.dist-info/RECORD +289 -0
  165. dmart-1.4.17.dist-info/WHEEL +5 -0
  166. dmart-1.4.17.dist-info/entry_points.txt +2 -0
  167. dmart-1.4.17.dist-info/top_level.txt +24 -0
  168. dmart.py +623 -0
  169. dmart_migrations/README +1 -0
  170. dmart_migrations/__init__.py +0 -0
  171. dmart_migrations/__pycache__/__init__.cpython-314.pyc +0 -0
  172. dmart_migrations/__pycache__/env.cpython-314.pyc +0 -0
  173. dmart_migrations/env.py +100 -0
  174. dmart_migrations/notes.txt +11 -0
  175. dmart_migrations/script.py.mako +28 -0
  176. dmart_migrations/scripts/__init__.py +0 -0
  177. dmart_migrations/scripts/calculate_checksums.py +77 -0
  178. dmart_migrations/scripts/migration_f7a4949eed19.py +28 -0
  179. dmart_migrations/versions/0f3d2b1a7c21_add_authz_materialized_views.py +87 -0
  180. dmart_migrations/versions/10d2041b94d4_last_checksum_history.py +62 -0
  181. dmart_migrations/versions/1cf4e1ee3cb8_ext_permission_with_filter_fields_values.py +33 -0
  182. dmart_migrations/versions/26bfe19b49d4_rm_failedloginattempts.py +42 -0
  183. dmart_migrations/versions/3c8bca2219cc_add_otp_table.py +38 -0
  184. dmart_migrations/versions/6675fd9dfe42_remove_unique_from_sessions_table.py +36 -0
  185. dmart_migrations/versions/71bc1df82e6a_adding_user_last_login_at.py +43 -0
  186. dmart_migrations/versions/74288ccbd3b5_initial.py +264 -0
  187. dmart_migrations/versions/7520a89a8467_rm_activesession_table.py +39 -0
  188. dmart_migrations/versions/848b623755a4_make_created_nd_updated_at_required.py +138 -0
  189. dmart_migrations/versions/8640dcbebf85_add_notes_to_users.py +32 -0
  190. dmart_migrations/versions/91c94250232a_adding_fk_on_owner_shortname.py +104 -0
  191. dmart_migrations/versions/98ecd6f56f9a_ext_meta_with_owner_group_shortname.py +66 -0
  192. dmart_migrations/versions/9aae9138c4ef_indexing_created_at_updated_at.py +80 -0
  193. dmart_migrations/versions/__init__.py +0 -0
  194. dmart_migrations/versions/__pycache__/0f3d2b1a7c21_add_authz_materialized_views.cpython-314.pyc +0 -0
  195. dmart_migrations/versions/__pycache__/10d2041b94d4_last_checksum_history.cpython-314.pyc +0 -0
  196. dmart_migrations/versions/__pycache__/1cf4e1ee3cb8_ext_permission_with_filter_fields_values.cpython-314.pyc +0 -0
  197. dmart_migrations/versions/__pycache__/26bfe19b49d4_rm_failedloginattempts.cpython-314.pyc +0 -0
  198. dmart_migrations/versions/__pycache__/3c8bca2219cc_add_otp_table.cpython-314.pyc +0 -0
  199. dmart_migrations/versions/__pycache__/6675fd9dfe42_remove_unique_from_sessions_table.cpython-314.pyc +0 -0
  200. dmart_migrations/versions/__pycache__/71bc1df82e6a_adding_user_last_login_at.cpython-314.pyc +0 -0
  201. dmart_migrations/versions/__pycache__/74288ccbd3b5_initial.cpython-314.pyc +0 -0
  202. dmart_migrations/versions/__pycache__/7520a89a8467_rm_activesession_table.cpython-314.pyc +0 -0
  203. dmart_migrations/versions/__pycache__/848b623755a4_make_created_nd_updated_at_required.cpython-314.pyc +0 -0
  204. dmart_migrations/versions/__pycache__/8640dcbebf85_add_notes_to_users.cpython-314.pyc +0 -0
  205. dmart_migrations/versions/__pycache__/91c94250232a_adding_fk_on_owner_shortname.cpython-314.pyc +0 -0
  206. dmart_migrations/versions/__pycache__/98ecd6f56f9a_ext_meta_with_owner_group_shortname.cpython-314.pyc +0 -0
  207. dmart_migrations/versions/__pycache__/9aae9138c4ef_indexing_created_at_updated_at.cpython-314.pyc +0 -0
  208. dmart_migrations/versions/__pycache__/b53f916b3f6d_json_to_jsonb.cpython-314.pyc +0 -0
  209. dmart_migrations/versions/__pycache__/eb5f1ec65156_adding_user_locked_to_device.cpython-314.pyc +0 -0
  210. dmart_migrations/versions/__pycache__/f7a4949eed19_adding_query_policies_to_meta.cpython-314.pyc +0 -0
  211. dmart_migrations/versions/b53f916b3f6d_json_to_jsonb.py +492 -0
  212. dmart_migrations/versions/eb5f1ec65156_adding_user_locked_to_device.py +36 -0
  213. dmart_migrations/versions/f7a4949eed19_adding_query_policies_to_meta.py +60 -0
  214. get_settings.py +7 -0
  215. info.json +1 -0
  216. languages/__init__.py +0 -0
  217. languages/arabic.json +15 -0
  218. languages/english.json +16 -0
  219. languages/kurdish.json +14 -0
  220. languages/loader.py +12 -0
  221. main.py +560 -0
  222. migrate.py +24 -0
  223. models/__init__.py +0 -0
  224. models/api.py +203 -0
  225. models/core.py +597 -0
  226. models/enums.py +255 -0
  227. password_gen.py +8 -0
  228. plugins/__init__.py +0 -0
  229. plugins/action_log/__init__.py +0 -0
  230. plugins/action_log/plugin.py +121 -0
  231. plugins/admin_notification_sender/__init__.py +0 -0
  232. plugins/admin_notification_sender/plugin.py +124 -0
  233. plugins/ldap_manager/__init__.py +0 -0
  234. plugins/ldap_manager/plugin.py +100 -0
  235. plugins/local_notification/__init__.py +0 -0
  236. plugins/local_notification/plugin.py +123 -0
  237. plugins/realtime_updates_notifier/__init__.py +0 -0
  238. plugins/realtime_updates_notifier/plugin.py +58 -0
  239. plugins/redis_db_update/__init__.py +0 -0
  240. plugins/redis_db_update/plugin.py +188 -0
  241. plugins/resource_folders_creation/__init__.py +0 -0
  242. plugins/resource_folders_creation/plugin.py +81 -0
  243. plugins/system_notification_sender/__init__.py +0 -0
  244. plugins/system_notification_sender/plugin.py +188 -0
  245. plugins/update_access_controls/__init__.py +0 -0
  246. plugins/update_access_controls/plugin.py +9 -0
  247. pytests/__init__.py +0 -0
  248. pytests/api_user_models_erros_test.py +16 -0
  249. pytests/api_user_models_requests_test.py +98 -0
  250. pytests/archive_test.py +72 -0
  251. pytests/base_test.py +300 -0
  252. pytests/get_settings_test.py +14 -0
  253. pytests/json_to_db_migration_test.py +237 -0
  254. pytests/service_test.py +26 -0
  255. pytests/test_info.py +55 -0
  256. pytests/test_status.py +15 -0
  257. run_notification_campaign.py +85 -0
  258. scheduled_notification_handler.py +121 -0
  259. schema_migration.py +208 -0
  260. schema_modulate.py +192 -0
  261. set_admin_passwd.py +55 -0
  262. sync.py +202 -0
  263. utils/__init__.py +0 -0
  264. utils/access_control.py +306 -0
  265. utils/async_request.py +8 -0
  266. utils/exporter.py +309 -0
  267. utils/firebase_notifier.py +57 -0
  268. utils/generate_email.py +37 -0
  269. utils/helpers.py +352 -0
  270. utils/hypercorn_config.py +12 -0
  271. utils/internal_error_code.py +60 -0
  272. utils/jwt.py +124 -0
  273. utils/logger.py +167 -0
  274. utils/middleware.py +99 -0
  275. utils/notification.py +75 -0
  276. utils/password_hashing.py +16 -0
  277. utils/plugin_manager.py +202 -0
  278. utils/query_policies_helper.py +128 -0
  279. utils/regex.py +44 -0
  280. utils/repository.py +529 -0
  281. utils/router_helper.py +19 -0
  282. utils/settings.py +166 -0
  283. utils/sms_notifier.py +21 -0
  284. utils/social_sso.py +67 -0
  285. utils/templates/activation.html.j2 +26 -0
  286. utils/templates/reminder.html.j2 +17 -0
  287. utils/ticket_sys_utils.py +203 -0
  288. utils/web_notifier.py +29 -0
  289. websocket.py +231 -0
dmart.py ADDED
@@ -0,0 +1,623 @@
1
+ #!/usr/bin/env -S BACKEND_ENV=config.env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import asyncio
6
+ import json
7
+ import os
8
+ import shutil
9
+ import ssl
10
+ import subprocess
11
+ import sys
12
+ import time
13
+ import warnings
14
+ import webbrowser
15
+ from multiprocessing import freeze_support
16
+ from pathlib import Path
17
+
18
+ from hypercorn.config import Config
19
+ from hypercorn.run import run
20
+
21
+ try:
22
+ from alembic import command as alembic_command
23
+ from alembic.config import Config as AlembicConfig
24
+ HAS_ALEMBIC = True
25
+ except ImportError:
26
+ HAS_ALEMBIC = False
27
+
28
+ from data_adapters.file.archive import archive
29
+ from data_adapters.file.create_index import main as create_index
30
+ from data_adapters.file.health_check import main as health_check
31
+ from data_adapters.sql.json_to_db_migration import main as json_to_db_migration
32
+ from data_adapters.sql.db_to_json_migration import main as db_to_json_migration
33
+ from main import main as server
34
+ from utils.exporter import main as exporter, exit_with_error, OUTPUT_FOLDER_NAME, validate_config, extract
35
+ from utils.settings import settings
36
+
37
+ freeze_support()
38
+
39
+ commands = """ server
40
+ serve
41
+ hyper
42
+ health-check
43
+ create-index
44
+ export
45
+ settings
46
+ set_password
47
+ archive
48
+ json_to_db
49
+ db_to_json
50
+ migrate
51
+ help
52
+ version
53
+ info
54
+ """
55
+
56
+ sentinel = object()
57
+
58
+
59
+ def hypercorn_main() -> int:
60
+ parser = argparse.ArgumentParser()
61
+ parser.add_argument(
62
+ "application",
63
+ help="The application to dispatch to as path.to.module:instance.path",
64
+ nargs="?",
65
+ default="main:app"
66
+ )
67
+ parser.add_argument("--access-log", help="Deprecated, see access-logfile", default=sentinel)
68
+ parser.add_argument(
69
+ "--access-logfile",
70
+ help="The target location for the access log, use `-` for stdout",
71
+ default=sentinel,
72
+ )
73
+ parser.add_argument(
74
+ "--access-logformat",
75
+ help="The log format for the access log, see help docs",
76
+ default=sentinel,
77
+ )
78
+ parser.add_argument(
79
+ "--backlog", help="The maximum number of pending connections", type=int, default=sentinel
80
+ )
81
+ parser.add_argument(
82
+ "-b",
83
+ "--bind",
84
+ dest="binds",
85
+ help=""" The TCP host/address to bind to. Should be either host:port, host,
86
+ unix:path or fd://num, e.g. 127.0.0.1:5000, 127.0.0.1,
87
+ unix:/tmp/socket or fd://33 respectively. """,
88
+ default=[],
89
+ action="append",
90
+ )
91
+ parser.add_argument("--ca-certs", help="Path to the SSL CA certificate file", default=sentinel)
92
+ parser.add_argument("--certfile", help="Path to the SSL certificate file", default=sentinel)
93
+ parser.add_argument("--cert-reqs", help="See verify mode argument", type=int, default=sentinel)
94
+ parser.add_argument("--ciphers", help="Ciphers to use for the SSL setup", default=sentinel)
95
+ parser.add_argument(
96
+ "-c",
97
+ "--config",
98
+ help="Location of a TOML config file, or when prefixed with `file:` a Python file, or when prefixed with `python:` a Python module.", # noqa: E501
99
+ default="hypercorn_config.toml",
100
+ )
101
+ parser.add_argument(
102
+ "--debug",
103
+ help="Enable debug mode, i.e. extra logging and checks",
104
+ action="store_true",
105
+ default=sentinel,
106
+ )
107
+ parser.add_argument("--error-log", help="Deprecated, see error-logfile", default=sentinel)
108
+ parser.add_argument(
109
+ "--error-logfile",
110
+ "--log-file",
111
+ dest="error_logfile",
112
+ help="The target location for the error log, use `-` for stderr",
113
+ default=sentinel,
114
+ )
115
+ parser.add_argument(
116
+ "--graceful-timeout",
117
+ help="""Time to wait after SIGTERM or Ctrl-C for any remaining requests (tasks)
118
+ to complete.""",
119
+ default=sentinel,
120
+ type=int,
121
+ )
122
+ parser.add_argument(
123
+ "--read-timeout",
124
+ help="""Seconds to wait before timing out reads on TCP sockets""",
125
+ default=sentinel,
126
+ type=int,
127
+ )
128
+ parser.add_argument(
129
+ "--max-requests",
130
+ help="""Maximum number of requests a worker will process before restarting""",
131
+ default=sentinel,
132
+ type=int,
133
+ )
134
+ parser.add_argument(
135
+ "--max-requests-jitter",
136
+ help="This jitter causes the max-requests per worker to be "
137
+ "randomized by randint(0, max_requests_jitter)",
138
+ default=sentinel,
139
+ type=int,
140
+ )
141
+ parser.add_argument(
142
+ "-g", "--group", help="Group to own any unix sockets.", default=sentinel, type=int
143
+ )
144
+ parser.add_argument(
145
+ "-k",
146
+ "--worker-class",
147
+ dest="worker_class",
148
+ help="The type of worker to use. "
149
+ "Options include asyncio, uvloop (pip install hypercorn[uvloop]), "
150
+ "and trio (pip install hypercorn[trio]).",
151
+ default=sentinel,
152
+ )
153
+ parser.add_argument(
154
+ "--keep-alive",
155
+ help="Seconds to keep inactive connections alive for",
156
+ default=sentinel,
157
+ type=int,
158
+ )
159
+ parser.add_argument("--keyfile", help="Path to the SSL key file", default=sentinel)
160
+ parser.add_argument(
161
+ "--keyfile-password", help="Password to decrypt the SSL key file", default=sentinel
162
+ )
163
+ parser.add_argument(
164
+ "--insecure-bind",
165
+ dest="insecure_binds",
166
+ help="""The TCP host/address to bind to. SSL options will not apply to these binds.
167
+ See *bind* for formatting options. Care must be taken! See HTTP -> HTTPS redirection docs.
168
+ """,
169
+ default=[],
170
+ action="append",
171
+ )
172
+ parser.add_argument(
173
+ "--log-config",
174
+ help=""""A Python logging configuration file. This can be prefixed with
175
+ 'json:' or 'toml:' to load the configuration from a file in
176
+ that format. Default is the logging ini format.""",
177
+ default=sentinel,
178
+ )
179
+ parser.add_argument(
180
+ "--log-level", help="The (error) log level, defaults to info", default=sentinel
181
+ )
182
+ parser.add_argument(
183
+ "-p", "--pid", help="Location to write the PID (Program ID) to.", default=sentinel
184
+ )
185
+ parser.add_argument(
186
+ "--quic-bind",
187
+ dest="quic_binds",
188
+ help="""The UDP/QUIC host/address to bind to. See *bind* for formatting
189
+ options.
190
+ """,
191
+ default=[],
192
+ action="append",
193
+ )
194
+ parser.add_argument(
195
+ "--reload",
196
+ help="Enable automatic reloads on code changes",
197
+ action="store_true",
198
+ default=sentinel,
199
+ )
200
+ parser.add_argument(
201
+ "--root-path", help="The setting for the ASGI root_path variable", default=sentinel
202
+ )
203
+ parser.add_argument(
204
+ "--server-name",
205
+ dest="server_names",
206
+ help="""The hostnames that can be served, requests to different hosts
207
+ will be responded to with 404s.
208
+ """,
209
+ default=[],
210
+ action="append",
211
+ )
212
+ parser.add_argument(
213
+ "--statsd-host", help="The host:port of the statsd server", default=sentinel
214
+ )
215
+ parser.add_argument("--statsd-prefix", help="Prefix for all statsd messages", default="")
216
+ parser.add_argument(
217
+ "-m",
218
+ "--umask",
219
+ help="The permissions bit mask to use on any unix sockets.",
220
+ default=sentinel,
221
+ type=int,
222
+ )
223
+ parser.add_argument(
224
+ "-u", "--user", help="User to own any unix sockets.", default=sentinel, type=int
225
+ )
226
+ parser.add_argument(
227
+ "--open-cxb",
228
+ help="Open CXB page in browser after server starts",
229
+ action="store_true",
230
+ default=False,
231
+ )
232
+ parser.add_argument(
233
+ "--cxb-config",
234
+ help="Path to CXB config.json",
235
+ default=sentinel,
236
+ )
237
+
238
+ def _convert_verify_mode(value: str) -> ssl.VerifyMode:
239
+ try:
240
+ return ssl.VerifyMode[value]
241
+ except KeyError:
242
+ raise argparse.ArgumentTypeError(f"'{value}' is not a valid verify mode")
243
+
244
+ parser.add_argument(
245
+ "--verify-mode",
246
+ help="SSL verify mode for peer's certificate, see ssl.VerifyMode enum for possible values.",
247
+ type=_convert_verify_mode,
248
+ default=sentinel,
249
+ )
250
+ parser.add_argument(
251
+ "--websocket-ping-interval",
252
+ help="""If set this is the time in seconds between pings sent to the client.
253
+ This can be used to keep the websocket connection alive.""",
254
+ default=sentinel,
255
+ type=int,
256
+ )
257
+ parser.add_argument(
258
+ "-w",
259
+ "--workers",
260
+ dest="workers",
261
+ help="The number of workers to spawn and use",
262
+ default=sentinel,
263
+ type=int,
264
+ )
265
+ args = parser.parse_args(sys.argv[1:])
266
+
267
+ # Set defaults if config file is not found
268
+ config = Config()
269
+ if args.config and os.path.exists(args.config):
270
+ config = Config.from_toml(args.config)
271
+ else:
272
+ # Default Hypercorn configuration
273
+ config.backlog = 2000
274
+ config.workers = 1
275
+ config.bind = ["localhost:8282"]
276
+
277
+ config.application_path = args.application
278
+
279
+ if args.log_level is not sentinel:
280
+ config.loglevel = args.log_level
281
+ if args.access_logformat is not sentinel:
282
+ config.access_log_format = args.access_logformat
283
+ if args.access_log is not sentinel:
284
+ warnings.warn(
285
+ "The --access-log argument is deprecated, use `--access-logfile` instead",
286
+ DeprecationWarning,
287
+ )
288
+ config.accesslog = args.access_log
289
+ if args.access_logfile is not sentinel:
290
+ config.accesslog = args.access_logfile
291
+ if args.backlog is not sentinel:
292
+ config.backlog = args.backlog
293
+ if args.ca_certs is not sentinel:
294
+ config.ca_certs = args.ca_certs
295
+ if args.certfile is not sentinel:
296
+ config.certfile = args.certfile
297
+ if args.cert_reqs is not sentinel:
298
+ config.cert_reqs = args.cert_reqs
299
+ if args.ciphers is not sentinel:
300
+ config.ciphers = args.ciphers
301
+ if args.debug is not sentinel:
302
+ config.debug = args.debug
303
+ if args.error_log is not sentinel:
304
+ warnings.warn(
305
+ "The --error-log argument is deprecated, use `--error-logfile` instead",
306
+ DeprecationWarning,
307
+ )
308
+ config.errorlog = args.error_log
309
+ if args.error_logfile is not sentinel:
310
+ config.errorlog = args.error_logfile
311
+ if args.graceful_timeout is not sentinel:
312
+ config.graceful_timeout = args.graceful_timeout
313
+ if args.read_timeout is not sentinel:
314
+ config.read_timeout = args.read_timeout
315
+ if args.group is not sentinel:
316
+ config.group = args.group
317
+ if args.keep_alive is not sentinel:
318
+ config.keep_alive_timeout = args.keep_alive
319
+ if args.keyfile is not sentinel:
320
+ config.keyfile = args.keyfile
321
+ if args.keyfile_password is not sentinel:
322
+ config.keyfile_password = args.keyfile_password
323
+ if args.log_config is not sentinel:
324
+ config.logconfig = args.log_config
325
+ if args.max_requests is not sentinel:
326
+ config.max_requests = args.max_requests
327
+ if args.max_requests_jitter is not sentinel:
328
+ config.max_requests_jitter = args.max_requests
329
+ if args.pid is not sentinel:
330
+ config.pid_path = args.pid
331
+ if args.root_path is not sentinel:
332
+ config.root_path = args.root_path
333
+ if args.reload is not sentinel:
334
+ config.use_reloader = args.reload
335
+ if args.statsd_host is not sentinel:
336
+ config.statsd_host = args.statsd_host
337
+ if args.statsd_prefix is not sentinel:
338
+ config.statsd_prefix = args.statsd_prefix
339
+ if args.umask is not sentinel:
340
+ config.umask = args.umask
341
+ if args.user is not sentinel:
342
+ config.user = args.user
343
+ if args.worker_class is not sentinel:
344
+ config.worker_class = args.worker_class
345
+ if args.verify_mode is not sentinel:
346
+ config.verify_mode = args.verify_mode
347
+ if args.websocket_ping_interval is not sentinel:
348
+ config.websocket_ping_interval = args.websocket_ping_interval
349
+ if args.workers is not sentinel:
350
+ config.workers = args.workers
351
+
352
+ if args.cxb_config is not sentinel:
353
+ os.environ["DMART_CXB_CONFIG"] = args.cxb_config
354
+
355
+ if len(args.binds) > 0:
356
+ config.bind = args.binds
357
+ if len(args.insecure_binds) > 0:
358
+ config.insecure_bind = args.insecure_binds
359
+ if len(args.quic_binds) > 0:
360
+ config.quic_bind = args.quic_binds
361
+ if len(args.server_names) > 0:
362
+ config.server_names = args.server_names
363
+
364
+ if args.open_cxb:
365
+ port = 8282
366
+ host = "127.0.0.1"
367
+
368
+ if len(config.bind) > 0:
369
+ try:
370
+ bind_parts = config.bind[0].split(":")
371
+ if len(bind_parts) == 2:
372
+ host = bind_parts[0]
373
+ port = int(bind_parts[1])
374
+ elif len(bind_parts) == 1:
375
+ host = bind_parts[0]
376
+ except Exception as e:
377
+ print(e)
378
+ pass
379
+
380
+ url = f"http://{host}:{port}/cxb/"
381
+
382
+ def open_browser():
383
+ time.sleep(2)
384
+ webbrowser.open(url)
385
+
386
+ import threading
387
+ threading.Thread(target=open_browser, daemon=True).start()
388
+
389
+ return run(config)
390
+
391
+
392
+ def main():
393
+ sys.argv = sys.argv[1:]
394
+ if len(sys.argv) == 0:
395
+ print("You must provide a command to run:")
396
+ print(commands)
397
+ sys.exit(1)
398
+
399
+ match sys.argv[0]:
400
+ case "hyper":
401
+ hypercorn_main()
402
+ case "server" | "serve":
403
+ open_cxb = False
404
+ if "--open-cxb" in sys.argv:
405
+ open_cxb = True
406
+ sys.argv.remove("--open-cxb")
407
+
408
+ if "--cxb-config" in sys.argv:
409
+ idx = sys.argv.index("--cxb-config")
410
+ if idx + 1 < len(sys.argv):
411
+ os.environ["DMART_CXB_CONFIG"] = sys.argv[idx + 1]
412
+ sys.argv.pop(idx + 1)
413
+ sys.argv.pop(idx)
414
+
415
+ if open_cxb:
416
+ url = f"http://{settings.listening_host}:{settings.listening_port}/cxb/"
417
+ def open_browser():
418
+ time.sleep(1)
419
+ webbrowser.open(url)
420
+
421
+ import threading
422
+ threading.Thread(target=open_browser, daemon=True).start()
423
+
424
+ asyncio.run(server())
425
+ case "health-check":
426
+ parser = argparse.ArgumentParser(
427
+ description="This created for doing health check functionality",
428
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
429
+ )
430
+ parser.add_argument("-t", "--type", help="type of health check (soft or hard)")
431
+ parser.add_argument("-s", "--space", help="hit the target space or pass (all) to make the full health check")
432
+ parser.add_argument("-m", "--schemas", nargs="*", help="hit the target schema inside the space")
433
+
434
+ args = parser.parse_args()
435
+ before_time = time.time()
436
+ asyncio.run(health_check(args.type, args.space, args.schemas))
437
+ print(f'total time: {"{:.2f}".format(time.time() - before_time)} sec')
438
+ case "create-index":
439
+ parser = argparse.ArgumentParser(
440
+ description="Recreate Redis indices based on the available schema definitions",
441
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
442
+ )
443
+ parser.add_argument("-p", "--space", help="recreate indices for this space only")
444
+ parser.add_argument(
445
+ "-c", "--schemas", nargs="*", help="recreate indices for this schemas only"
446
+ )
447
+ parser.add_argument(
448
+ "-s", "--subpaths", nargs="*", help="upload documents for this subpaths only"
449
+ )
450
+ parser.add_argument(
451
+ "--flushall", action='store_true', help="FLUSHALL data on Redis"
452
+ )
453
+
454
+ args = parser.parse_args()
455
+
456
+ asyncio.run(create_index(args.space, args.schemas, args.subpaths, args.flushall))
457
+ case "export":
458
+ parser = argparse.ArgumentParser()
459
+ parser.add_argument(
460
+ "--config", required=True, help="Json config relative path from the script"
461
+ )
462
+ parser.add_argument(
463
+ "--spaces", required=True, help="Spaces relative path from the script"
464
+ )
465
+ parser.add_argument(
466
+ "--output",
467
+ help="Output relative path from the script (the default path is the current script path",
468
+ )
469
+ parser.add_argument(
470
+ "--since",
471
+ help="Export entries created/updated since the provided timestamp",
472
+ )
473
+ args = parser.parse_args()
474
+ since = None
475
+ output_path = ""
476
+ if args.output:
477
+ output_path = args.output
478
+
479
+ if args.since:
480
+ since = int(round(float(args.since) * 1000))
481
+
482
+ if not os.path.isdir(args.spaces):
483
+ exit_with_error(f"The spaces folder {args.spaces} is not found.")
484
+
485
+ out_path = os.path.join(output_path, OUTPUT_FOLDER_NAME)
486
+ if os.path.isdir(out_path):
487
+ shutil.rmtree(out_path)
488
+
489
+ tasks = []
490
+ with open(args.config, "r") as f:
491
+ config_objs = json.load(f)
492
+
493
+ for config_obj in config_objs:
494
+ if not validate_config(config_obj):
495
+ continue
496
+ tasks.append(extract(config_obj.get("space", ""),
497
+ config_obj.get("subpath", ""),
498
+ config_obj.get("resource_type", ""),
499
+ config_obj.get("schema_shortname", ""),
500
+ config_obj.get("included_meta_fields", {}),
501
+ config_obj.get("excluded_payload_fields", {}),
502
+ args.spaces, output_path, since))
503
+
504
+ asyncio.run(exporter(tasks))
505
+
506
+ print(
507
+ f"Output path: {os.path.abspath(os.path.join(output_path, OUTPUT_FOLDER_NAME))}"
508
+ )
509
+ case "settings":
510
+ print(settings.model_dump_json())
511
+ case "set_password":
512
+ import set_admin_passwd # noqa: F401
513
+ case "archive":
514
+ parser = argparse.ArgumentParser(
515
+ description="Script for archiving records from different spaces and subpaths."
516
+ )
517
+ parser.add_argument("space", type=str, help="The name of the space")
518
+ parser.add_argument("subpath", type=str, help="The subpath within the space")
519
+ parser.add_argument(
520
+ "schema",
521
+ type=str,
522
+ help="The subpath within the space. Optional, if not provided move everything",
523
+ nargs="?",
524
+ )
525
+ parser.add_argument(
526
+ "olderthan",
527
+ type=int,
528
+ help="The number of day, older than which, the entries will be archived (based on updated_at)",
529
+ )
530
+
531
+ args = parser.parse_args()
532
+ space = args.space
533
+ subpath = args.subpath
534
+ olderthan = args.olderthan
535
+ schema = args.schema or "meta"
536
+
537
+ asyncio.run(archive(space, subpath, schema, olderthan))
538
+ print("Done.")
539
+ case "json_to_db":
540
+ asyncio.run(json_to_db_migration())
541
+ case "db_to_json":
542
+ db_to_json_migration()
543
+ case "migrate":
544
+ pkg_dir = Path(__file__).resolve().parent
545
+
546
+ # Try to find migrations directory
547
+ alembic_dir = pkg_dir / "dmart_migrations"
548
+ if not alembic_dir.exists():
549
+ # Fallback to alembic for backward compatibility
550
+ alembic_dir = pkg_dir / "alembic"
551
+
552
+ original_ini_path = pkg_dir / "alembic.ini"
553
+ if not original_ini_path.exists():
554
+ original_ini_path = alembic_dir / "alembic.ini"
555
+
556
+ if not original_ini_path.exists():
557
+ print(f"Error: 'alembic.ini' not found in {pkg_dir} or {alembic_dir}")
558
+ sys.exit(1)
559
+
560
+ try:
561
+ # Check if alembic library is installed
562
+ if not HAS_ALEMBIC:
563
+ print("Error: 'alembic' library not found. Please install it with 'pip install alembic'.")
564
+ sys.exit(1)
565
+
566
+ alembic_cfg = AlembicConfig(str(original_ini_path))
567
+ alembic_cfg.set_main_option("script_location", alembic_dir.as_posix())
568
+
569
+ # Run alembic upgrade head
570
+ alembic_command.upgrade(alembic_cfg, "head")
571
+
572
+ except Exception as e:
573
+ print(f"Error running migration: {e}")
574
+ sys.exit(1)
575
+ case "help":
576
+ print("Available commands:")
577
+ print(commands)
578
+ case "version":
579
+ info_json_path = Path(__file__).resolve().parent / "info.json"
580
+ if info_json_path.exists():
581
+ with open(info_json_path) as info:
582
+ print(json.load(info).get("tag"))
583
+ else:
584
+ tag_cmd = "git describe --tags"
585
+ result, _ = subprocess.Popen(tag_cmd.split(" "), stdout=subprocess.PIPE,
586
+ stderr=subprocess.PIPE).communicate()
587
+ tag = None if result is None or len(result) == 0 else result.decode().strip()
588
+ print(tag)
589
+ case "info":
590
+ info_json_path = Path(__file__).resolve().parent / "info.json"
591
+ if info_json_path.exists():
592
+ with open(info_json_path) as info:
593
+ print(json.load(info))
594
+ else:
595
+ branch_cmd = "git rev-parse --abbrev-ref HEAD"
596
+ result, _ = subprocess.Popen(branch_cmd.split(" "), stdout=subprocess.PIPE,
597
+ stderr=subprocess.PIPE).communicate()
598
+ branch = None if result is None or len(result) == 0 else result.decode().strip()
599
+
600
+ version_cmd = "git rev-parse --short HEAD"
601
+ result, _ = subprocess.Popen(version_cmd.split(" "), stdout=subprocess.PIPE,
602
+ stderr=subprocess.PIPE).communicate()
603
+ version = None if result is None or len(result) == 0 else result.decode().strip()
604
+
605
+ tag_cmd = "git describe --tags"
606
+ result, _ = subprocess.Popen(tag_cmd.split(" "), stdout=subprocess.PIPE,
607
+ stderr=subprocess.PIPE).communicate()
608
+ tag = None if result is None or len(result) == 0 else result.decode().strip()
609
+
610
+ version_date_cmd = "git show --pretty=format:'%ad'"
611
+ result, _ = subprocess.Popen(version_date_cmd.split(" "), stdout=subprocess.PIPE,
612
+ stderr=subprocess.PIPE).communicate()
613
+ version_date = None if result is None or len(result) == 0 else result.decode().split("\n")[0]
614
+
615
+ print({
616
+ "commit_hash": version,
617
+ "date": version_date,
618
+ "branch": branch,
619
+ "tag": tag
620
+ })
621
+
622
+ if __name__ == "__main__":
623
+ main()
@@ -0,0 +1 @@
1
+ Generic single-database configuration.
File without changes