django-cfg 1.4.93__py3-none-any.whl → 1.4.94__py3-none-any.whl

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

Potentially problematic release.


This version of django-cfg might be problematic. Click here for more details.

django_cfg/__init__.py CHANGED
@@ -32,7 +32,7 @@ Example:
32
32
  default_app_config = "django_cfg.apps.DjangoCfgConfig"
33
33
 
34
34
  # Version information
35
- __version__ = "1.4.93"
35
+ __version__ = "1.4.94"
36
36
  __license__ = "MIT"
37
37
 
38
38
  # Import registry for organized lazy loading
@@ -28,21 +28,24 @@ logger = logging.getLogger(__name__)
28
28
 
29
29
  class ZipExtractionMixin:
30
30
  """
31
- Mixin for automatic ZIP extraction with smart update detection.
31
+ Mixin for automatic ZIP extraction with age-based refresh.
32
32
 
33
33
  Provides intelligent ZIP archive handling:
34
34
  - Auto-extraction when directory doesn't exist
35
- - Auto-reextraction when ZIP is modified < 5 minutes ago
36
- - Timestamp comparison to avoid unnecessary extractions
35
+ - Auto-reextraction when extracted directory is older than 10 minutes
36
+ - Simple age-based logic (no ZIP timestamp comparison needed)
37
37
 
38
38
  Usage:
39
39
  class MyView(ZipExtractionMixin, View):
40
40
  app_name = 'myapp' # Will look for myapp.zip
41
41
  """
42
42
 
43
+ # Maximum age of extracted directory before re-extraction (in seconds)
44
+ MAX_DIRECTORY_AGE = 600 # 10 minutes = 600 seconds
45
+
43
46
  def extract_zip_if_needed(self, base_dir: Path, zip_path: Path, app_name: str) -> bool:
44
47
  """
45
- Extract ZIP archive if needed based on modification times.
48
+ Extract ZIP archive if needed based on directory age.
46
49
 
47
50
  Args:
48
51
  base_dir: Target directory for extraction
@@ -57,33 +60,25 @@ class ZipExtractionMixin:
57
60
  # Priority 1: If directory doesn't exist at all - always extract
58
61
  if not base_dir.exists():
59
62
  should_extract = True
60
- logger.info(f"[{app_name}] Directory {base_dir} doesn't exist, will extract")
61
-
62
- # Priority 2: Directory exists - check if ZIP is fresh and needs update
63
- elif zip_path.exists():
64
- # Get ZIP modification time
65
- zip_mtime = datetime.fromtimestamp(zip_path.stat().st_mtime)
66
- time_since_modification = (datetime.now() - zip_mtime).total_seconds()
67
-
68
- # If ZIP was modified less than 5 minutes ago - check for updates
69
- if time_since_modification < 300: # 5 minutes = 300 seconds
70
- # Compare ZIP time with directory time
71
- dir_mtime = datetime.fromtimestamp(base_dir.stat().st_mtime)
72
-
73
- # If ZIP is newer than directory, re-extract
74
- if zip_mtime > dir_mtime:
75
- logger.info(f"[{app_name}] ZIP is fresh ({time_since_modification:.0f}s) and newer, re-extracting")
76
- try:
77
- shutil.rmtree(base_dir)
78
- should_extract = True
79
- except Exception as e:
80
- logger.error(f"[{app_name}] Failed to remove old directory: {e}")
81
- return False
82
- else:
83
- logger.debug(f"[{app_name}] ZIP is fresh but directory is up-to-date")
63
+ logger.info(f"[{app_name}] Directory doesn't exist, will extract")
64
+
65
+ # Priority 2: Directory exists - check if it's too old
66
+ else:
67
+ # Get directory modification time
68
+ dir_mtime = datetime.fromtimestamp(base_dir.stat().st_mtime)
69
+ directory_age = (datetime.now() - dir_mtime).total_seconds()
70
+
71
+ # If directory is older than MAX_DIRECTORY_AGE - re-extract
72
+ if directory_age > self.MAX_DIRECTORY_AGE:
73
+ logger.info(f"[{app_name}] Directory is old ({directory_age:.0f}s > {self.MAX_DIRECTORY_AGE}s), re-extracting")
74
+ try:
75
+ shutil.rmtree(base_dir)
76
+ should_extract = True
77
+ except Exception as e:
78
+ logger.error(f"[{app_name}] Failed to remove old directory: {e}")
79
+ return False
84
80
  else:
85
- # ZIP is old (> 5 min) - use existing directory, no checks needed
86
- logger.debug(f"[{app_name}] ZIP is old ({time_since_modification:.0f}s), using existing")
81
+ logger.debug(f"[{app_name}] Directory is fresh ({directory_age:.0f}s), using existing")
87
82
 
88
83
  # Extract ZIP if needed
89
84
  if should_extract:
@@ -102,7 +97,7 @@ class ZipExtractionMixin:
102
97
  logger.error(f"[{app_name}] ZIP not found: {zip_path}")
103
98
  return False
104
99
 
105
- # Directory exists and is up-to-date
100
+ # Directory exists and is fresh
106
101
  return True
107
102
 
108
103
 
@@ -113,17 +108,18 @@ class NextJSStaticView(ZipExtractionMixin, View):
113
108
 
114
109
  Features:
115
110
  - Serves Next.js static export files like a static file server
116
- - Smart ZIP extraction: auto-updates when archive modified < 5 minutes ago
111
+ - Smart ZIP extraction: auto-refreshes when directory older than 10 minutes
117
112
  - Automatically injects JWT tokens for authenticated users
118
113
  - Tokens injected into HTML responses only
119
114
  - Handles Next.js client-side routing (.html fallback)
120
115
  - Automatically serves index.html for directory paths
121
116
  - X-Frame-Options exempt to allow embedding in iframes
122
117
 
123
- ZIP Update Logic:
124
- - If ZIP modified < 5 minutes ago: removes old files and re-extracts
125
- - If ZIP modified > 5 minutes ago: uses existing extraction
126
- - This enables hot-reload during development while staying fast in production
118
+ ZIP Extraction Logic:
119
+ - If directory doesn't exist: extract from ZIP
120
+ - If directory older than 10 minutes: remove and re-extract
121
+ - If directory fresh (< 10 min): use existing files
122
+ - This enables auto-refresh during development while caching in production
127
123
 
128
124
  Path resolution examples:
129
125
  - /cfg/admin/ → /cfg/admin/index.html
@@ -4,8 +4,8 @@ Views for Next.js admin integration.
4
4
  Serves Next.js static files with SPA routing support and JWT injection.
5
5
 
6
6
  Features:
7
- - Automatic extraction of ZIP archives with smart update detection
8
- - Auto-reextraction when ZIP is modified within last 5 minutes
7
+ - Automatic extraction of ZIP archives with age-based refresh
8
+ - Auto-reextraction when directory is older than 10 minutes
9
9
  - SPA routing with fallback strategies
10
10
  - JWT token injection for authenticated users
11
11
  """
@@ -31,13 +31,19 @@ class NextJsAdminView(ZipExtractionMixin, LoginRequiredMixin, View):
31
31
 
32
32
  Features:
33
33
  - Serves Next.js static build files
34
+ - Smart ZIP extraction: auto-refreshes when directory older than 10 minutes
34
35
  - Automatic JWT token injection for authenticated users
35
36
  - SPA routing support (path/to/route → path/to/route/index.html)
36
37
  - Development mode support (proxies to dev server conceptually)
37
38
 
39
+ ZIP Extraction Logic:
40
+ - If directory doesn't exist: extract from ZIP
41
+ - If directory older than 10 minutes: remove and re-extract
42
+ - If directory fresh (< 10 min): use existing files
43
+
38
44
  URL Examples:
39
- /cfg/admin/ → private.html (or index.html)
40
- /cfg/admin/private/centrifugo private/centrifugo/index.html
45
+ /cfg/admin/ → admin/index.html
46
+ /cfg/admin/crypto admin/crypto/index.html
41
47
  /cfg/admin/_next/static/... → _next/static/...
42
48
  """
43
49
 
django_cfg/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "django-cfg"
7
- version = "1.4.93"
7
+ version = "1.4.94"
8
8
  description = "Modern Django framework with type-safe Pydantic v2 configuration, Next.js admin integration, real-time WebSockets, and 8 enterprise apps. Replace settings.py with validated models, 90% less code. Production-ready with AI agents, auto-generated TypeScript clients, and zero-config features."
9
9
  readme = "README.md"
10
10
  keywords = [ "django", "configuration", "pydantic", "settings", "type-safety", "pydantic-settings", "django-environ", "startup-validation", "ide-autocomplete", "nextjs-admin", "react-admin", "websocket", "centrifugo", "real-time", "typescript-generation", "ai-agents", "enterprise-django", "django-settings", "type-safe-config", "modern-django",]
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-cfg
3
- Version: 1.4.93
3
+ Version: 1.4.94
4
4
  Summary: Modern Django framework with type-safe Pydantic v2 configuration, Next.js admin integration, real-time WebSockets, and 8 enterprise apps. Replace settings.py with validated models, 90% less code. Production-ready with AI agents, auto-generated TypeScript clients, and zero-config features.
5
5
  Project-URL: Homepage, https://djangocfg.com
6
6
  Project-URL: Documentation, https://djangocfg.com
@@ -1,5 +1,5 @@
1
1
  django_cfg/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- django_cfg/__init__.py,sha256=ixk70FXf2taCPetppHWPXJltlOmnzJ1Cql1usdNzh2g,1620
2
+ django_cfg/__init__.py,sha256=-eM13Z1Uie8EJUy2384SNHfso1SqzwpkDsmrQgWQN3w,1620
3
3
  django_cfg/apps.py,sha256=72m3uuvyqGiLx6gOfE-BD3P61jddCCERuBOYpxTX518,1605
4
4
  django_cfg/config.py,sha256=y4Z3rnYsHBE0TehpwAIPaxr---mkvyKrZGGsNwYso74,1398
5
5
  django_cfg/apps/__init__.py,sha256=JtDmEYt1OcleWM2ZaeX0LKDnRQzPOavfaXBWG4ECB5Q,26
@@ -228,7 +228,7 @@ django_cfg/apps/frontend/apps.py,sha256=9eEK0Tq2nOsol7xSK5aobdwTDJTJrWx6Iy1I1DQb
228
228
  django_cfg/apps/frontend/setup.py,sha256=TxKQwQw4xTF6VSyhrQBzbUsdsVQR9JHdjc36LZKeQh4,2444
229
229
  django_cfg/apps/frontend/test_routing.py,sha256=fshJOR9ln7m3gXY9EI1_ix_6E5xua6DR264b16RIF-w,4832
230
230
  django_cfg/apps/frontend/urls.py,sha256=Vz22_2i2w1J0KQYDCxHnTF5rUf32kUUSBDJZrP07XgY,284
231
- django_cfg/apps/frontend/views.py,sha256=u4B7YNwaAupu1yoxYLR8JNVAOmmtgYjHEmSpI3J53LQ,13937
231
+ django_cfg/apps/frontend/views.py,sha256=JkObFoAJATT2gy6TnyFi0LrjQm7HGWaRy3vr2zdTs4E,13586
232
232
  django_cfg/apps/frontend/templates/frontend/404.html,sha256=LCFig_dcgDDmYKhgOLu8R2KDs_aQS6Es6rAxLTAEXWs,2175
233
233
  django_cfg/apps/knowbase/README.md,sha256=HXt_J6WCN-LsMhA7p9mdvih07_vp_r_hkPdmqHhNEeo,3965
234
234
  django_cfg/apps/knowbase/__init__.py,sha256=cfGnxDQwjajPhUoleKkgvdabJcB0LdXEglnsBojKkPo,1045
@@ -1021,7 +1021,7 @@ django_cfg/modules/django_unfold/models/navigation.py,sha256=PPEeqA2HBaA1-VjADiX
1021
1021
  django_cfg/modules/nextjs_admin/__init__.py,sha256=lfrZYyNRExH3Z5De8G4hQBIZoFlW5Ejze3couNrztbY,312
1022
1022
  django_cfg/modules/nextjs_admin/apps.py,sha256=HxVUMmWTKdYpwJ00iIfWVFsBzsawsOVhEPZqjk_izjI,347
1023
1023
  django_cfg/modules/nextjs_admin/urls.py,sha256=7n0yStm0WNchw14Rtu_mgsIA3WKQsYP9WZt3-YOUWjU,603
1024
- django_cfg/modules/nextjs_admin/views.py,sha256=kOyfb3eQoDB68ojoxhf4KgZEcSkSW8n5xSkceKQHcMg,9405
1024
+ django_cfg/modules/nextjs_admin/views.py,sha256=lOcK-XLucWn-W4LORgk0XBEJkolYvSvsnp4Pq77oBBY,9657
1025
1025
  django_cfg/modules/nextjs_admin/models/__init__.py,sha256=WGw9KXcYd1O9AoA_bpMoz2gLZUlRzjGmUBjjbObcUi0,100
1026
1026
  django_cfg/modules/nextjs_admin/models/config.py,sha256=0ADqLuiywSCQfx_z9dkwjFCca3lr3F2uQffIjTr_QXw,5864
1027
1027
  django_cfg/modules/nextjs_admin/templatetags/__init__.py,sha256=ChVBnJggCIY8rMhfyJFoA8k0qKo-8FtJknrk54Vx4wM,51
@@ -1051,7 +1051,7 @@ django_cfg/static/admin/js/alpine/dashboard-tabs.js,sha256=ob8Q_I9lFLDv_hFERXgTy
1051
1051
  django_cfg/static/admin/js/alpine/system-metrics.js,sha256=m-Fg55K_vpHXToD46PXL9twl4OBF_V9MONvbSWbQqDw,440
1052
1052
  django_cfg/static/admin/js/alpine/toggle-section.js,sha256=T141NFmy0fRJyGGuuaCJRjJXwPam-xxtQNW1hi8BJbc,672
1053
1053
  django_cfg/static/frontend/admin.zip,sha256=n2G8ajruLqojcjqvw2pMEPjgpSTTwcmJxkeIQxJVw9U,7626768
1054
- django_cfg/static/frontend/nextjs_admin.zip,sha256=ezN08XaFPMMkQMGh2MWWif1QZkhStiBy1azw37KxKWw,7548354
1054
+ django_cfg/static/frontend/nextjs_admin.zip,sha256=BsnMjrr9vx1Lh1zadBEBFGeP-Cn_eLjhWfkWk1m0fEw,7548272
1055
1055
  django_cfg/static/js/api-loader.mjs,sha256=boGqqRGnFR-Mzo_RQOjhAzNvsb7QxZddSwMKROzkk9Q,5163
1056
1056
  django_cfg/static/js/api/base.mjs,sha256=KUxZHHdELAV8mNnACpwJRvaQhdJxp-n5LFEQ4oUZxBo,4707
1057
1057
  django_cfg/static/js/api/index.mjs,sha256=_-Q04jjHcgwi4CGfiaLyiOR6NW7Yu1HBhJWp2J1cjpc,2538
@@ -1085,9 +1085,9 @@ django_cfg/utils/version_check.py,sha256=WO51J2m2e-wVqWCRwbultEwu3q1lQasV67Mw2aa
1085
1085
  django_cfg/CHANGELOG.md,sha256=jtT3EprqEJkqSUh7IraP73vQ8PmKUMdRtznQsEnqDZk,2052
1086
1086
  django_cfg/CONTRIBUTING.md,sha256=DU2kyQ6PU0Z24ob7O_OqKWEYHcZmJDgzw-lQCmu6uBg,3041
1087
1087
  django_cfg/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
1088
- django_cfg/pyproject.toml,sha256=ca2iRiN-r6x_tF37fS5CdEMhQBO_Te6O9IA1IV7Du3U,8572
1089
- django_cfg-1.4.93.dist-info/METADATA,sha256=f0X6l8mcxPU4yKPF7Jyql1DvdN1m_Wj3BvJJkheKCj4,23733
1090
- django_cfg-1.4.93.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
1091
- django_cfg-1.4.93.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
1092
- django_cfg-1.4.93.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
1093
- django_cfg-1.4.93.dist-info/RECORD,,
1088
+ django_cfg/pyproject.toml,sha256=J8hwDLhnybgAGUsvUNdXFimKoEuj7Lc9SH1n5f_Q0gY,8572
1089
+ django_cfg-1.4.94.dist-info/METADATA,sha256=Xhg1mauCgGfgGYkYBGmGWH19KcqlRxLi4z-kGAGFvVs,23733
1090
+ django_cfg-1.4.94.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
1091
+ django_cfg-1.4.94.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
1092
+ django_cfg-1.4.94.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
1093
+ django_cfg-1.4.94.dist-info/RECORD,,