dmart 0.1.10__py3-none-any.whl → 1.4.1__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.
api/managed/utils.py CHANGED
@@ -469,22 +469,23 @@ async def serve_request_update(request, owner_shortname: str):
469
469
  schema_shortname=record_schema_shortname,
470
470
  )
471
471
 
472
- requested_checksum = record.attributes.get("last_checksum_history")
473
- if requested_checksum:
474
- latest_history = await db.get_latest_history(
475
- space_name=request.space_name,
476
- subpath=record.subpath,
477
- shortname=record.shortname,
478
- )
479
- if latest_history and latest_history.last_checksum_history != requested_checksum:
480
- raise api.Exception(
481
- status.HTTP_409_CONFLICT,
482
- api.Error(
483
- type="request",
484
- code=InternalErrorCode.CONFLICT,
485
- message="Resource has been updated by another request!",
486
- ),
472
+ if settings.is_sha_required:
473
+ requested_checksum = record.attributes.get("last_checksum_history")
474
+ if requested_checksum:
475
+ latest_history = await db.get_latest_history(
476
+ space_name=request.space_name,
477
+ subpath=record.subpath,
478
+ shortname=record.shortname,
487
479
  )
480
+ if latest_history and latest_history.last_checksum_history != requested_checksum:
481
+ raise api.Exception(
482
+ status.HTTP_409_CONFLICT,
483
+ api.Error(
484
+ type="request",
485
+ code=InternalErrorCode.CONFLICT,
486
+ message="Resource has been updated by another request!",
487
+ ),
488
+ )
488
489
 
489
490
  # CHECK PERMISSION
490
491
  if not await access_control.check_access(
@@ -1534,22 +1535,23 @@ async def serve_space_update(request, record, owner_shortname: str, is_replace:
1534
1535
  user_shortname=owner_shortname,
1535
1536
  )
1536
1537
 
1537
- requested_checksum = record.attributes.get("last_checksum_history")
1538
- if requested_checksum:
1539
- latest_history = await db.get_latest_history(
1540
- space_name=space.shortname,
1541
- subpath=record.subpath,
1542
- shortname=space.shortname,
1543
- )
1544
- if latest_history and latest_history.last_checksum_history != requested_checksum:
1545
- raise api.Exception(
1546
- status.HTTP_409_CONFLICT,
1547
- api.Error(
1548
- type="request",
1549
- code=InternalErrorCode.CONFLICT,
1550
- message="Resource has been updated by another request. Please refresh and try again.",
1551
- ),
1538
+ if settings.is_sha_required:
1539
+ requested_checksum = record.attributes.get("last_checksum_history")
1540
+ if requested_checksum:
1541
+ latest_history = await db.get_latest_history(
1542
+ space_name=space.shortname,
1543
+ subpath=record.subpath,
1544
+ shortname=space.shortname,
1552
1545
  )
1546
+ if latest_history and latest_history.last_checksum_history != requested_checksum:
1547
+ raise api.Exception(
1548
+ status.HTTP_409_CONFLICT,
1549
+ api.Error(
1550
+ type="request",
1551
+ code=InternalErrorCode.CONFLICT,
1552
+ message="Resource has been updated by another request. Please refresh and try again.",
1553
+ ),
1554
+ )
1553
1555
 
1554
1556
  old_flat = flatten_dict(old_space.model_dump())
1555
1557
  new_flat = flatten_dict(space.model_dump())
bundler.py CHANGED
@@ -2,7 +2,7 @@
2
2
  import json
3
3
  import subprocess
4
4
  import PyInstaller.__main__
5
-
5
+ import os
6
6
 
7
7
  branch_cmd = "git rev-parse --abbrev-ref HEAD"
8
8
  result, _ = subprocess.Popen(branch_cmd.split(" "), stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
@@ -29,8 +29,7 @@ info = {
29
29
 
30
30
  json.dump(info, open('info.json', 'w'))
31
31
 
32
-
33
- PyInstaller.__main__.run([
32
+ args = [
34
33
  'dmart.py',
35
34
  '--name=dmart',
36
35
  '--onefile',
@@ -41,4 +40,13 @@ PyInstaller.__main__.run([
41
40
  '--collect-submodules=concurrent_log_handler',
42
41
  '--collect-submodules=pythonjsonlogger',
43
42
  '--clean',
44
- ])
43
+ ]
44
+
45
+ cxb_path = 'cxb'
46
+ if not os.path.isdir(cxb_path):
47
+ cxb_path = '../cxb/dist/client'
48
+
49
+ if os.path.isdir(cxb_path):
50
+ args.append(f'--add-data={cxb_path}:cxb')
51
+
52
+ PyInstaller.__main__.run(args)
@@ -1379,7 +1379,7 @@ class SQLAdapter(BaseDataAdapter):
1379
1379
  ffv_resource_type.append(perm_key_splited[2])
1380
1380
 
1381
1381
  if len(ffv_spaces):
1382
- perm_key_splited_query = f'@space_name:{'|'.join(ffv_spaces)} @subpath:{f"/{'|/'.join(ffv_subpath)}"} @resource_type:{'|'.join(ffv_resource_type)} {' '.join(ffv_query)}'
1382
+ perm_key_splited_query = f'@space_name:{"|".join(ffv_spaces)} @subpath:/{"|/".join(ffv_subpath)} @resource_type:{"|".join(ffv_resource_type)} {" ".join(ffv_query)}'
1383
1383
  if query.search:
1384
1384
  query.search += f' {perm_key_splited_query}'
1385
1385
  else:
@@ -2146,7 +2146,7 @@ class SQLAdapter(BaseDataAdapter):
2146
2146
 
2147
2147
  return history_diff
2148
2148
  except Exception as e:
2149
- print("[!store_entry_diff]", e)
2149
+ print("[!store_entry_diff]", e, old_version_flattend, new_version_flattend)
2150
2150
  logger.error(f"Failed parsing an entry. Error: {e}")
2151
2151
  return {}
2152
2152
 
@@ -2466,7 +2466,7 @@ class SQLAdapter(BaseDataAdapter):
2466
2466
  try:
2467
2467
  return await self.load(space_name, "/", space_name, core.Space)
2468
2468
  except Exception as e:
2469
- print("[!fetch_space]", e)
2469
+ print("[!fetch_space]", e, space_name)
2470
2470
  return None
2471
2471
 
2472
2472
  async def set_user_session(self, user_shortname: str, token: str) -> bool:
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dmart
3
- Version: 0.1.10
4
- Requires-Python: >=3.10
3
+ Version: 1.4.1
4
+ Requires-Python: >=3.11
5
5
  Requires-Dist: fastapi
6
6
  Requires-Dist: pydantic
7
7
  Requires-Dist: pydantic[email]
@@ -19,7 +19,6 @@ Requires-Dist: pydantic-settings
19
19
  Requires-Dist: fastapi-sso
20
20
  Requires-Dist: sqlmodel
21
21
  Requires-Dist: psycopg[binary]
22
- Requires-Dist: asyncpg
23
22
  Requires-Dist: greenlet
24
23
  Requires-Dist: alembic
25
24
  Requires-Dist: jinja2
@@ -1,8 +1,8 @@
1
- bundler.py,sha256=8gEQputdVfI8vda5Lkzw12blv7kYkmPLVbEWr_itLe0,1522
1
+ bundler.py,sha256=so8ZJResb1PcOH5vboa_mpFAdYr_T8u8DbbFXd570Lg,1704
2
2
  data_generator.py,sha256=CnE-VHEeX7-lAXtqCgbRqR9WHjTuOgeiZcviYrHAmho,2287
3
- dmart.py,sha256=VgNH7vpzHBO2FcMztdwZBLq1TovzbkBFkXrZwn6dKjI,19712
3
+ dmart.py,sha256=QZQvR0q3CGOJy8KzXh7m8wW51JqILw--xvC0N4akjnY,21683
4
4
  get_settings.py,sha256=Sbe2WCoiK398E7HY4SNLfDN_GmE8knR4M-YJWF31jcg,153
5
- main.py,sha256=XqKHlgFy_S_EtvbGsSxiCMSApSU33BlTpv8T4UWX0yc,17351
5
+ main.py,sha256=0jguG9WZMYJgYAR_JO95GrSFM8Hu5qbInABMxWvoY3I,19526
6
6
  migrate.py,sha256=hn1MZoVby_Jjqhc7y3CrLcGD619QmVZv3PONNvO7VKQ,665
7
7
  password_gen.py,sha256=xjx8wi105ZYvhLBBQj7_rugACpxifGXHse6f7YlGXWQ,196
8
8
  run_notification_campaign.py,sha256=ZCvHfaimK4W6q4XuHs2r6wavMGipYYw4aUkNlxmGUd8,2652
@@ -40,7 +40,7 @@ api/info/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
40
  api/info/router.py,sha256=sQZZor7A-uDzsJX39aqEA7bMZOJ-WTitYeFvVNWfaHw,3938
41
41
  api/managed/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
42
  api/managed/router.py,sha256=0xfJ3NXV3XHyG8yWLEECBJt-XppymOxYuMDvQJdO1MI,50865
43
- api/managed/utils.py,sha256=OZgqqWAKMXqhFOloBcm85KiE4BpZ8h0CNffK8O7Pc7c,72639
43
+ api/managed/utils.py,sha256=KyDqnGK_ndX2Uy47FmrPHmu6PpZ35IhgjGjA816x-uY,72841
44
44
  api/public/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
45
  api/public/router.py,sha256=TrraWs2LGL_c_JRDbvS8OHm3fJ-ZKF7o4El7wvjV4Mk,24753
46
46
  api/qr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -70,7 +70,7 @@ data_adapters/file/drop_index.py,sha256=OK3wXwaO9tUcHcJjqyLeBnkElzK35MZMi8YLGWdr
70
70
  data_adapters/file/health_check.py,sha256=cMvwsXhjEykjrTyB3HtUn8QqKdtB_h5w8mGOEYPepzU,24221
71
71
  data_adapters/file/redis_services.py,sha256=83STcca5fYFaEVLRYAxfUQXeUQZqJOT8XH-GBSbkR-E,39914
72
72
  data_adapters/sql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
- data_adapters/sql/adapter.py,sha256=xdd5EcZU1fF4GTBxSg4BUyKvd8o95kcf1FqtPHUN2u8,154514
73
+ data_adapters/sql/adapter.py,sha256=MlNRWjUigjjS2SRzDYNaUOsGwTFPHZQn-o0DXxKKHl8,154565
74
74
  data_adapters/sql/adapter_helpers.py,sha256=Eu22NElz2fMu6zyOsGsGrnAZcyWhHz9I__RJ9z6cwK0,15076
75
75
  data_adapters/sql/create_tables.py,sha256=KqaXHTDOD8YaqGNc_e0iHHotd0WE3Kad_tBevtoGA20,17427
76
76
  data_adapters/sql/create_users_folders.py,sha256=fm3P-CMcPX4b4DqXHKWMOtfX4RHdaev2nCDhYrS5cIs,1911
@@ -82,7 +82,7 @@ languages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
82
82
  languages/arabic.json,sha256=UL61rP9_M42CGfU94G5-1bXVUVnsJWXzoqhaXTXTJuM,910
83
83
  languages/english.json,sha256=Y7eZ2X8c427_97qYrHOeGb2d725T-YlNNFVSi8FB7Kw,649
84
84
  languages/kurdish.json,sha256=GgPLkVKyhIQjT7h3cPfDh0oyzg26znvBUe5X_Zz2TWI,864
85
- languages/loader.py,sha256=yaBJHGVRxi8Z-H8x4MOQcwyhiY0zD9UY-m0AjLi4eq8,393
85
+ languages/loader.py,sha256=3gf2WmfSQ-AVIpyLAm_5J5NcQlWqBxQHMjcUd04Pijs,381
86
86
  models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
87
87
  models/api.py,sha256=f5X56dudyEysPmDuI5grM2RRCXuIQoehaAB6wMAGG28,6473
88
88
  models/core.py,sha256=tEb7cbnC71yE9SDluynj7dE3U8Ed-EbF3uRJizy-uuU,16880
@@ -103,7 +103,7 @@ plugins/redis_db_update/plugin.py,sha256=z05k1zNJgBnKPj-jrtMUeI9br75ZPlifbzL0Hxp
103
103
  plugins/resource_folders_creation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
104
104
  plugins/resource_folders_creation/plugin.py,sha256=OwYPtRjMt2esAAEdv1FjdZgjEz01yt2xOZQi3nB0kEQ,3327
105
105
  plugins/system_notification_sender/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
- plugins/system_notification_sender/plugin.py,sha256=96xzmuUGvO2WOJ5y90akDoRB7Bqy-VpOo6ht8-rLLtQ,8253
106
+ plugins/system_notification_sender/plugin.py,sha256=MUOujwyRJ3yQrXwZ-X4qSdx0ZNU2c-sYy0d0-U8twoA,8253
107
107
  plugins/update_access_controls/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
108
108
  plugins/update_access_controls/plugin.py,sha256=43UV4vg-zxBF_7Bv0AZH6gU0Bgy2ybapNK21wJTF05k,301
109
109
  pytests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -121,7 +121,7 @@ utils/access_control.py,sha256=8cCKr-6bL-Shl8j8xtfjEykMPGy6wkbNz-NRwLCdx-Y,11757
121
121
  utils/async_request.py,sha256=Lm2xGXLeph7P1_fLhhNJDhPubKT2ncFn_Ueft4JVoeI,255
122
122
  utils/exporter.py,sha256=HjcZCzcuH6N6f7Gn2hkTEeEFyo-MfrsiZUYAE-9kkVQ,9718
123
123
  utils/firebase_notifier.py,sha256=nAeCUo5Mtwxygwj8ONlw8ZAtL_ekdJBabvU0z2dZ3NY,2391
124
- utils/generate_email.py,sha256=_yHJiYp9dd1IT2lL2N2VmPrsCuKN30YTmE0votM9It8,1132
124
+ utils/generate_email.py,sha256=25i1iNVDfyRDq7vvZVFkaTYfMZFkAg1ZVQO2GdK7yRQ,1122
125
125
  utils/helpers.py,sha256=gNxLg09cclRWrKNBy2pwGZsxGA0iFS5iZ_nyra3SmnI,9928
126
126
  utils/hypercorn_config.py,sha256=q28HGRLWo9wjOVF183WwFPs3HQo4Nexc7q_7dmSVHRI,311
127
127
  utils/internal_error_code.py,sha256=KGlXPC5YruPmb0ORVY7U3EEVpBgSLuU4lHdXgwUVN2M,1637
@@ -135,15 +135,15 @@ utils/query_policies_helper.py,sha256=jBcNI_15P6LqVeWz6w9UMreLuNIc50GxqAf17KzxE8
135
135
  utils/regex.py,sha256=cv9b_l_e8tz42mKckeeyDgypKqh2e71E28co2iuEVxA,2286
136
136
  utils/repository.py,sha256=9L-IvQ0Js0SQ5OR-Rh0i2Wdu4H9H06r8eE84hfBIu7Q,18313
137
137
  utils/router_helper.py,sha256=Tgoq3oakejdEeyeVieTNk38JsPZ8x5RuR0kw2THc1mI,604
138
- utils/settings.py,sha256=jKesIr67J9RP-QwrzsUQzXRAGjqpvYGUhmPuGY_GNpI,5639
138
+ utils/settings.py,sha256=SIunwTJF0Ac9fUWRt3sUsgWzMkWk0RUryEANYuOJQ9k,5660
139
139
  utils/sms_notifier.py,sha256=04D6D_ldk3S9SojI7_381pqLc8v9lligeNHAysohz7w,550
140
140
  utils/social_sso.py,sha256=Dm1W6U9OwKbAeUwM-kwJBHFEoreeoN-s-RHdOZ1-cNg,2216
141
141
  utils/ticket_sys_utils.py,sha256=9QAlW2iiy8KyxQRBDj_WmzS5kKb0aYJmGwd4qzmGVqo,7005
142
142
  utils/web_notifier.py,sha256=QM87VVid2grC5lK3NdS1yzz0z1wXljr4GChJOeK86W4,843
143
143
  utils/templates/activation.html.j2,sha256=XAMKCdoqONoc4ZQucD0yV-Pg5DlHHASZrTVItNS-iBE,640
144
144
  utils/templates/reminder.html.j2,sha256=aoS8bTs56q4hjAZKsb0jV9c-PIURBELuBOpT_qPZNVU,639
145
- dmart-0.1.10.dist-info/METADATA,sha256=RMl8zHzCOJJRamNEhE2DRvGLc7QhcplHF8iG9oMjGGo,2092
146
- dmart-0.1.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
147
- dmart-0.1.10.dist-info/entry_points.txt,sha256=GjfoGh1bpxuU9HHGJzbtCFPNptHv9TryxHMN3uBSKpg,37
148
- dmart-0.1.10.dist-info/top_level.txt,sha256=JTypu1r5v9v7ru-60JSSbnSMEESHFRMacpcz-rPJx_U,262
149
- dmart-0.1.10.dist-info/RECORD,,
145
+ dmart-1.4.1.dist-info/METADATA,sha256=Xx4kYj7woXX61a2PqjZnthP2Ud5fVUpbly5mCg6xWCk,2068
146
+ dmart-1.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
147
+ dmart-1.4.1.dist-info/entry_points.txt,sha256=GjfoGh1bpxuU9HHGJzbtCFPNptHv9TryxHMN3uBSKpg,37
148
+ dmart-1.4.1.dist-info/top_level.txt,sha256=JTypu1r5v9v7ru-60JSSbnSMEESHFRMacpcz-rPJx_U,262
149
+ dmart-1.4.1.dist-info/RECORD,,
dmart.py CHANGED
@@ -11,6 +11,7 @@ import subprocess
11
11
  import sys
12
12
  import time
13
13
  import warnings
14
+ import webbrowser
14
15
  from multiprocessing import freeze_support
15
16
  from pathlib import Path
16
17
 
@@ -29,6 +30,7 @@ from utils.settings import settings
29
30
  freeze_support()
30
31
 
31
32
  commands = """ server
33
+ serve
32
34
  hyper
33
35
  health-check
34
36
  create-index
@@ -49,7 +51,10 @@ sentinel = object()
49
51
  def hypercorn_main() -> int:
50
52
  parser = argparse.ArgumentParser()
51
53
  parser.add_argument(
52
- "application", help="The application to dispatch to as path.to.module:instance.path"
54
+ "application",
55
+ help="The application to dispatch to as path.to.module:instance.path",
56
+ nargs="?",
57
+ default="main:app"
53
58
  )
54
59
  parser.add_argument("--access-log", help="Deprecated, see access-logfile", default=sentinel)
55
60
  parser.add_argument(
@@ -83,7 +88,7 @@ def hypercorn_main() -> int:
83
88
  "-c",
84
89
  "--config",
85
90
  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
86
- default=None,
91
+ default="hypercorn_config.toml",
87
92
  )
88
93
  parser.add_argument(
89
94
  "--debug",
@@ -210,6 +215,17 @@ def hypercorn_main() -> int:
210
215
  parser.add_argument(
211
216
  "-u", "--user", help="User to own any unix sockets.", default=sentinel, type=int
212
217
  )
218
+ parser.add_argument(
219
+ "--open-cxb",
220
+ help="Open CXB page in browser after server starts",
221
+ action="store_true",
222
+ default=False,
223
+ )
224
+ parser.add_argument(
225
+ "--cxb-config",
226
+ help="Path to CXB config.json",
227
+ default=sentinel,
228
+ )
213
229
 
214
230
  def _convert_verify_mode(value: str) -> ssl.VerifyMode:
215
231
  try:
@@ -314,6 +330,9 @@ def hypercorn_main() -> int:
314
330
  config.websocket_ping_interval = args.websocket_ping_interval
315
331
  if args.workers is not sentinel:
316
332
  config.workers = args.workers
333
+
334
+ if args.cxb_config is not sentinel:
335
+ os.environ["DMART_CXB_CONFIG"] = args.cxb_config
317
336
 
318
337
  if len(args.binds) > 0:
319
338
  config.bind = args.binds
@@ -323,6 +342,32 @@ def hypercorn_main() -> int:
323
342
  config.quic_bind = args.quic_binds
324
343
  if len(args.server_names) > 0:
325
344
  config.server_names = args.server_names
345
+
346
+ if args.open_cxb:
347
+ # Assuming default port 8282 if not specified in binds
348
+ port = 8282
349
+ host = "127.0.0.1"
350
+
351
+ # Try to parse bind address if available
352
+ if len(args.binds) > 0:
353
+ try:
354
+ bind_parts = args.binds[0].split(":")
355
+ if len(bind_parts) == 2:
356
+ host = bind_parts[0]
357
+ port = int(bind_parts[1])
358
+ elif len(bind_parts) == 1:
359
+ host = bind_parts[0]
360
+ except:
361
+ pass
362
+
363
+ url = f"http://{host}:{port}/cxb/"
364
+
365
+ def open_browser():
366
+ time.sleep(2) # Give server a moment to start
367
+ webbrowser.open(url)
368
+
369
+ import threading
370
+ threading.Thread(target=open_browser, daemon=True).start()
326
371
 
327
372
  return run(config)
328
373
 
@@ -336,13 +381,30 @@ def main():
336
381
 
337
382
  match sys.argv[0]:
338
383
  case "hyper":
339
- if len(sys.argv) == 1:
340
- print("Running Hypercorn with default settings")
341
- default_params = "main:app --config hypercorn_config.toml"
342
- print(f">{default_params}")
343
- sys.argv = ["hyper"] + default_params.split(" ")
344
384
  hypercorn_main()
345
- case "server":
385
+ case "server" | "serve":
386
+ # Check for --open-cxb flag in server command arguments
387
+ open_cxb = False
388
+ if "--open-cxb" in sys.argv:
389
+ open_cxb = True
390
+ sys.argv.remove("--open-cxb")
391
+
392
+ if "--cxb-config" in sys.argv:
393
+ idx = sys.argv.index("--cxb-config")
394
+ if idx + 1 < len(sys.argv):
395
+ os.environ["DMART_CXB_CONFIG"] = sys.argv[idx + 1]
396
+ sys.argv.pop(idx + 1)
397
+ sys.argv.pop(idx)
398
+
399
+ if open_cxb:
400
+ url = f"http://{settings.listening_host}:{settings.listening_port}/cxb/"
401
+ def open_browser():
402
+ time.sleep(2) # Give server a moment to start
403
+ webbrowser.open(url)
404
+
405
+ import threading
406
+ threading.Thread(target=open_browser, daemon=True).start()
407
+
346
408
  asyncio.run(server())
347
409
  case "health-check":
348
410
  parser = argparse.ArgumentParser(
languages/loader.py CHANGED
@@ -1,4 +1,3 @@
1
- import glob
2
1
  import json
3
2
  from pathlib import Path
4
3
 
main.py CHANGED
@@ -5,6 +5,7 @@ from starlette.datastructures import UploadFile
5
5
  from contextlib import asynccontextmanager
6
6
  import asyncio
7
7
  import json
8
+ import os
8
9
  from os import getpid
9
10
  import sys
10
11
  import time
@@ -24,11 +25,12 @@ from fastapi.logger import logger
24
25
  from fastapi.encoders import jsonable_encoder
25
26
  from fastapi.exceptions import RequestValidationError
26
27
  from utils.access_control import access_control
27
- from fastapi.responses import ORJSONResponse
28
+ from fastapi.responses import ORJSONResponse, FileResponse
28
29
  from hypercorn.asyncio import serve
29
30
  from hypercorn.config import Config
30
31
  from starlette.concurrency import iterate_in_threadpool
31
32
  from starlette.exceptions import HTTPException as StarletteHTTPException
33
+ from starlette.staticfiles import StaticFiles
32
34
  import models.api as api
33
35
  from utils.settings import settings
34
36
  from asgi_correlation_id import CorrelationIdMiddleware
@@ -462,6 +464,51 @@ app.include_router(
462
464
  # load plugins
463
465
  asyncio.run(plugin_manager.load_plugins(app, capture_body))
464
466
 
467
+ # Serve CXB
468
+ cxb_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "cxb")
469
+ if not os.path.exists(cxb_path):
470
+ # Try relative to project root (development mode)
471
+ project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
472
+ cxb_dist_path = os.path.join(project_root, "cxb", "dist", "client")
473
+ if os.path.isdir(cxb_dist_path):
474
+ cxb_path = cxb_dist_path
475
+
476
+ if os.path.isdir(cxb_path):
477
+ @app.get("/cxb/config.json", include_in_schema=False)
478
+ async def get_cxb_config():
479
+ # Priority 0: Explicit path from flag
480
+ cxb_config = os.getenv("DMART_CXB_CONFIG")
481
+ if cxb_config and os.path.exists(cxb_config):
482
+ return FileResponse(cxb_config)
483
+
484
+ # Priority 1: Check in current directory (where dmart is run)
485
+ if os.path.exists("config.json"):
486
+ return FileResponse("config.json")
487
+
488
+ # Priority 2: Check in spaces folder (user directory)
489
+ user_config = settings.spaces_folder / "config.json"
490
+ if user_config.exists():
491
+ return FileResponse(user_config)
492
+
493
+ # Priority 3: Check in bundled CXB directory
494
+ bundled_config = os.path.join(cxb_path, "config.json")
495
+ if os.path.exists(bundled_config):
496
+ return FileResponse(bundled_config)
497
+
498
+ # Fallback to defaults generated from settings
499
+ return {
500
+ "title": "DMART Unified Data Platform",
501
+ "footer": "dmart.cc unified data platform",
502
+ "short_name": "dmart",
503
+ "display_name": "dmart",
504
+ "description": "dmart unified data platform",
505
+ "default_language": "en",
506
+ "languages": { "ar": "العربية", "en": "English" },
507
+ "backend": f"{settings.app_url}" if settings.app_url else f"http://{settings.listening_host}:{settings.listening_port}",
508
+ "websocket": settings.websocket_url if settings.websocket_url else f"ws://{settings.listening_host}:{settings.websocket_port}/ws"
509
+ }
510
+
511
+ app.mount("/cxb", StaticFiles(directory=cxb_path, html=True), name="cxb")
465
512
 
466
513
  @app.options("/{x:path}", include_in_schema=False)
467
514
  async def myoptions():
@@ -503,4 +550,3 @@ if __name__ == "__main__":
503
550
  asyncio.run(main())
504
551
  except Exception as e:
505
552
  print("[!1server]", e)
506
-
@@ -69,7 +69,7 @@ class Plugin(PluginBase):
69
69
  retrieve_json_payload=True,
70
70
  space_name="management",
71
71
  subpath="notifications/system",
72
- search=f"@payload.body.on_space:{data.space_name} @payload.body.on_subpath:{data.subpath.lstrip("/")} @payload.body.on_action:{data.action_type}",
72
+ search=f"@payload.body.on_space:{data.space_name} @payload.body.on_subpath:{data.subpath.lstrip('/')} @payload.body.on_action:{data.action_type}",
73
73
  limit=30,
74
74
  offset=0
75
75
  ), "dmart")
utils/generate_email.py CHANGED
@@ -1,4 +1,3 @@
1
- import os
2
1
  from jinja2 import Environment, FileSystemLoader
3
2
  from pathlib import Path
4
3
 
utils/settings.py CHANGED
@@ -5,7 +5,6 @@ import os
5
5
  import re
6
6
  import string
7
7
  import random
8
- import sys
9
8
  from venv import logger
10
9
 
11
10
  from pydantic import Field
@@ -78,6 +77,7 @@ class Settings(BaseSettings):
78
77
  session_inactivity_ttl: int = 0 # Set initially to 0 to disable session timeout. Possible value : 60 * 60 * 24 * 7 # 7 days
79
78
  request_timeout: int = 35 # In seconds the time of dmart requests.
80
79
  jq_timeout: int = 2 # secs
80
+ is_sha_required: bool = False
81
81
 
82
82
  url_shorter_expires: int = 60 * 60 * 48 # 48 hours
83
83
 
@@ -157,7 +157,7 @@ try:
157
157
  Settings()
158
158
  )
159
159
  except Exception as e:
160
- # logger.error(f"Failed to load settings.\nError: {e}")
160
+ logger.error(f"Failed to load settings.\nError: {e}")
161
161
  # sys.exit(1)
162
162
  pass
163
163
 
File without changes