django-cfg 1.1.82__py3-none-any.whl โ†’ 1.2.0__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 (244) hide show
  1. django_cfg/__init__.py +20 -448
  2. django_cfg/apps/accounts/README.md +3 -3
  3. django_cfg/apps/accounts/admin/__init__.py +0 -2
  4. django_cfg/apps/accounts/admin/activity.py +2 -9
  5. django_cfg/apps/accounts/admin/filters.py +0 -42
  6. django_cfg/apps/accounts/admin/inlines.py +8 -8
  7. django_cfg/apps/accounts/admin/otp.py +5 -5
  8. django_cfg/apps/accounts/admin/registration_source.py +1 -8
  9. django_cfg/apps/accounts/admin/user.py +12 -20
  10. django_cfg/apps/accounts/managers/user_manager.py +2 -129
  11. django_cfg/apps/accounts/migrations/0006_remove_twilioresponse_otp_secret_and_more.py +46 -0
  12. django_cfg/apps/accounts/models.py +3 -123
  13. django_cfg/apps/accounts/serializers/otp.py +40 -44
  14. django_cfg/apps/accounts/serializers/profile.py +0 -2
  15. django_cfg/apps/accounts/services/otp_service.py +98 -186
  16. django_cfg/apps/accounts/signals.py +25 -15
  17. django_cfg/apps/accounts/utils/auth_email_service.py +84 -0
  18. django_cfg/apps/accounts/views/otp.py +35 -36
  19. django_cfg/apps/agents/README.md +129 -0
  20. django_cfg/apps/agents/__init__.py +68 -0
  21. django_cfg/apps/agents/admin/__init__.py +17 -0
  22. django_cfg/apps/agents/admin/execution_admin.py +460 -0
  23. django_cfg/apps/agents/admin/registry_admin.py +360 -0
  24. django_cfg/apps/agents/admin/toolsets_admin.py +482 -0
  25. django_cfg/apps/agents/apps.py +29 -0
  26. django_cfg/apps/agents/core/__init__.py +20 -0
  27. django_cfg/apps/agents/core/agent.py +281 -0
  28. django_cfg/apps/agents/core/dependencies.py +154 -0
  29. django_cfg/apps/agents/core/exceptions.py +66 -0
  30. django_cfg/apps/agents/core/models.py +106 -0
  31. django_cfg/apps/agents/core/orchestrator.py +391 -0
  32. django_cfg/apps/agents/examples/__init__.py +3 -0
  33. django_cfg/apps/agents/examples/simple_example.py +161 -0
  34. django_cfg/apps/agents/integration/__init__.py +14 -0
  35. django_cfg/apps/agents/integration/middleware.py +80 -0
  36. django_cfg/apps/agents/integration/registry.py +345 -0
  37. django_cfg/apps/agents/integration/signals.py +50 -0
  38. django_cfg/apps/agents/management/__init__.py +3 -0
  39. django_cfg/apps/agents/management/commands/__init__.py +3 -0
  40. django_cfg/apps/agents/management/commands/create_agent.py +365 -0
  41. django_cfg/apps/agents/management/commands/orchestrator_status.py +191 -0
  42. django_cfg/apps/agents/managers/__init__.py +23 -0
  43. django_cfg/apps/agents/managers/execution.py +236 -0
  44. django_cfg/apps/agents/managers/registry.py +254 -0
  45. django_cfg/apps/agents/managers/toolsets.py +496 -0
  46. django_cfg/apps/agents/migrations/0001_initial.py +286 -0
  47. django_cfg/apps/agents/migrations/__init__.py +5 -0
  48. django_cfg/apps/agents/models/__init__.py +15 -0
  49. django_cfg/apps/agents/models/execution.py +215 -0
  50. django_cfg/apps/agents/models/registry.py +220 -0
  51. django_cfg/apps/agents/models/toolsets.py +305 -0
  52. django_cfg/apps/agents/patterns/__init__.py +24 -0
  53. django_cfg/apps/agents/patterns/content_agents.py +234 -0
  54. django_cfg/apps/agents/toolsets/__init__.py +15 -0
  55. django_cfg/apps/agents/toolsets/cache_toolset.py +285 -0
  56. django_cfg/apps/agents/toolsets/django_toolset.py +220 -0
  57. django_cfg/apps/agents/toolsets/file_toolset.py +324 -0
  58. django_cfg/apps/agents/toolsets/orm_toolset.py +319 -0
  59. django_cfg/apps/agents/urls.py +46 -0
  60. django_cfg/apps/knowbase/README.md +150 -0
  61. django_cfg/apps/knowbase/__init__.py +27 -0
  62. django_cfg/apps/knowbase/admin/__init__.py +23 -0
  63. django_cfg/apps/knowbase/admin/archive_admin.py +857 -0
  64. django_cfg/apps/knowbase/admin/chat_admin.py +386 -0
  65. django_cfg/apps/knowbase/admin/document_admin.py +650 -0
  66. django_cfg/apps/knowbase/admin/external_data_admin.py +685 -0
  67. django_cfg/apps/knowbase/apps.py +81 -0
  68. django_cfg/apps/knowbase/config/README.md +176 -0
  69. django_cfg/apps/knowbase/config/__init__.py +51 -0
  70. django_cfg/apps/knowbase/config/constance_fields.py +186 -0
  71. django_cfg/apps/knowbase/config/constance_settings.py +200 -0
  72. django_cfg/apps/knowbase/config/settings.py +444 -0
  73. django_cfg/apps/knowbase/examples/__init__.py +3 -0
  74. django_cfg/apps/knowbase/examples/external_data_usage.py +191 -0
  75. django_cfg/apps/knowbase/management/__init__.py +0 -0
  76. django_cfg/apps/knowbase/management/commands/__init__.py +0 -0
  77. django_cfg/apps/knowbase/management/commands/knowbase_stats.py +158 -0
  78. django_cfg/apps/knowbase/management/commands/setup_knowbase.py +59 -0
  79. django_cfg/apps/knowbase/managers/__init__.py +22 -0
  80. django_cfg/apps/knowbase/managers/archive.py +426 -0
  81. django_cfg/apps/knowbase/managers/base.py +32 -0
  82. django_cfg/apps/knowbase/managers/chat.py +141 -0
  83. django_cfg/apps/knowbase/managers/document.py +203 -0
  84. django_cfg/apps/knowbase/managers/external_data.py +471 -0
  85. django_cfg/apps/knowbase/migrations/0001_initial.py +427 -0
  86. django_cfg/apps/knowbase/migrations/0002_archiveitem_archiveitemchunk_documentarchive_and_more.py +434 -0
  87. django_cfg/apps/knowbase/migrations/__init__.py +5 -0
  88. django_cfg/apps/knowbase/mixins/__init__.py +15 -0
  89. django_cfg/apps/knowbase/mixins/config.py +108 -0
  90. django_cfg/apps/knowbase/mixins/creator.py +81 -0
  91. django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +199 -0
  92. django_cfg/apps/knowbase/mixins/external_data_mixin.py +813 -0
  93. django_cfg/apps/knowbase/mixins/service.py +362 -0
  94. django_cfg/apps/knowbase/models/__init__.py +41 -0
  95. django_cfg/apps/knowbase/models/archive.py +599 -0
  96. django_cfg/apps/knowbase/models/base.py +58 -0
  97. django_cfg/apps/knowbase/models/chat.py +157 -0
  98. django_cfg/apps/knowbase/models/document.py +267 -0
  99. django_cfg/apps/knowbase/models/external_data.py +376 -0
  100. django_cfg/apps/knowbase/serializers/__init__.py +68 -0
  101. django_cfg/apps/knowbase/serializers/archive_serializers.py +386 -0
  102. django_cfg/apps/knowbase/serializers/chat_serializers.py +137 -0
  103. django_cfg/apps/knowbase/serializers/document_serializers.py +94 -0
  104. django_cfg/apps/knowbase/serializers/external_data_serializers.py +256 -0
  105. django_cfg/apps/knowbase/serializers/public_serializers.py +74 -0
  106. django_cfg/apps/knowbase/services/__init__.py +40 -0
  107. django_cfg/apps/knowbase/services/archive/__init__.py +42 -0
  108. django_cfg/apps/knowbase/services/archive/archive_service.py +541 -0
  109. django_cfg/apps/knowbase/services/archive/chunking_service.py +791 -0
  110. django_cfg/apps/knowbase/services/archive/exceptions.py +52 -0
  111. django_cfg/apps/knowbase/services/archive/extraction_service.py +508 -0
  112. django_cfg/apps/knowbase/services/archive/vectorization_service.py +362 -0
  113. django_cfg/apps/knowbase/services/base.py +53 -0
  114. django_cfg/apps/knowbase/services/chat_service.py +239 -0
  115. django_cfg/apps/knowbase/services/document_service.py +144 -0
  116. django_cfg/apps/knowbase/services/embedding/__init__.py +43 -0
  117. django_cfg/apps/knowbase/services/embedding/async_processor.py +244 -0
  118. django_cfg/apps/knowbase/services/embedding/batch_processor.py +250 -0
  119. django_cfg/apps/knowbase/services/embedding/batch_result.py +61 -0
  120. django_cfg/apps/knowbase/services/embedding/models.py +229 -0
  121. django_cfg/apps/knowbase/services/embedding/processors.py +148 -0
  122. django_cfg/apps/knowbase/services/embedding/utils.py +176 -0
  123. django_cfg/apps/knowbase/services/prompt_builder.py +191 -0
  124. django_cfg/apps/knowbase/services/search_service.py +293 -0
  125. django_cfg/apps/knowbase/signals/__init__.py +21 -0
  126. django_cfg/apps/knowbase/signals/archive_signals.py +211 -0
  127. django_cfg/apps/knowbase/signals/chat_signals.py +37 -0
  128. django_cfg/apps/knowbase/signals/document_signals.py +143 -0
  129. django_cfg/apps/knowbase/signals/external_data_signals.py +157 -0
  130. django_cfg/apps/knowbase/tasks/__init__.py +39 -0
  131. django_cfg/apps/knowbase/tasks/archive_tasks.py +316 -0
  132. django_cfg/apps/knowbase/tasks/document_processing.py +341 -0
  133. django_cfg/apps/knowbase/tasks/external_data_tasks.py +341 -0
  134. django_cfg/apps/knowbase/tasks/maintenance.py +195 -0
  135. django_cfg/apps/knowbase/urls.py +43 -0
  136. django_cfg/apps/knowbase/utils/__init__.py +12 -0
  137. django_cfg/apps/knowbase/utils/chunk_settings.py +261 -0
  138. django_cfg/apps/knowbase/utils/text_processing.py +375 -0
  139. django_cfg/apps/knowbase/utils/validation.py +99 -0
  140. django_cfg/apps/knowbase/views/__init__.py +28 -0
  141. django_cfg/apps/knowbase/views/archive_views.py +469 -0
  142. django_cfg/apps/knowbase/views/base.py +49 -0
  143. django_cfg/apps/knowbase/views/chat_views.py +181 -0
  144. django_cfg/apps/knowbase/views/document_views.py +183 -0
  145. django_cfg/apps/knowbase/views/public_views.py +129 -0
  146. django_cfg/apps/leads/admin.py +70 -0
  147. django_cfg/apps/newsletter/admin.py +234 -0
  148. django_cfg/apps/newsletter/admin_filters.py +124 -0
  149. django_cfg/apps/support/admin.py +196 -0
  150. django_cfg/apps/support/admin_filters.py +71 -0
  151. django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
  152. django_cfg/apps/urls.py +5 -4
  153. django_cfg/cli/README.md +1 -1
  154. django_cfg/cli/commands/create_project.py +2 -2
  155. django_cfg/cli/commands/info.py +1 -1
  156. django_cfg/config.py +44 -0
  157. django_cfg/core/config.py +29 -82
  158. django_cfg/core/environment.py +1 -1
  159. django_cfg/core/generation.py +19 -107
  160. django_cfg/{integration.py โ†’ core/integration.py} +18 -16
  161. django_cfg/core/validation.py +1 -1
  162. django_cfg/management/__init__.py +1 -1
  163. django_cfg/management/commands/__init__.py +1 -1
  164. django_cfg/management/commands/auto_generate.py +482 -0
  165. django_cfg/management/commands/migrator.py +19 -101
  166. django_cfg/management/commands/test_email.py +1 -1
  167. django_cfg/middleware/README.md +0 -158
  168. django_cfg/middleware/__init__.py +0 -2
  169. django_cfg/middleware/user_activity.py +3 -3
  170. django_cfg/models/api.py +145 -0
  171. django_cfg/models/base.py +287 -0
  172. django_cfg/models/cache.py +4 -4
  173. django_cfg/models/constance.py +25 -88
  174. django_cfg/models/database.py +9 -9
  175. django_cfg/models/drf.py +3 -36
  176. django_cfg/models/email.py +163 -0
  177. django_cfg/models/environment.py +276 -0
  178. django_cfg/models/limits.py +1 -1
  179. django_cfg/models/logging.py +366 -0
  180. django_cfg/models/revolution.py +41 -2
  181. django_cfg/models/security.py +125 -0
  182. django_cfg/models/services.py +1 -1
  183. django_cfg/modules/__init__.py +2 -56
  184. django_cfg/modules/base.py +78 -52
  185. django_cfg/modules/django_currency/service.py +2 -2
  186. django_cfg/modules/django_email.py +2 -2
  187. django_cfg/modules/django_health.py +267 -0
  188. django_cfg/modules/django_llm/llm/client.py +79 -17
  189. django_cfg/modules/django_llm/translator/translator.py +2 -2
  190. django_cfg/modules/django_logger.py +2 -2
  191. django_cfg/modules/django_ngrok.py +2 -2
  192. django_cfg/modules/django_tasks.py +68 -3
  193. django_cfg/modules/django_telegram.py +3 -3
  194. django_cfg/modules/django_twilio/sendgrid_service.py +2 -2
  195. django_cfg/modules/django_twilio/service.py +2 -2
  196. django_cfg/modules/django_twilio/simple_service.py +2 -2
  197. django_cfg/modules/django_twilio/twilio_service.py +2 -2
  198. django_cfg/modules/django_unfold/__init__.py +69 -0
  199. django_cfg/modules/{unfold โ†’ django_unfold}/callbacks.py +23 -22
  200. django_cfg/modules/django_unfold/dashboard.py +278 -0
  201. django_cfg/modules/django_unfold/icons/README.md +145 -0
  202. django_cfg/modules/django_unfold/icons/__init__.py +12 -0
  203. django_cfg/modules/django_unfold/icons/constants.py +2851 -0
  204. django_cfg/modules/django_unfold/icons/generate_icons.py +486 -0
  205. django_cfg/modules/django_unfold/models/__init__.py +42 -0
  206. django_cfg/modules/django_unfold/models/config.py +601 -0
  207. django_cfg/modules/django_unfold/models/dashboard.py +206 -0
  208. django_cfg/modules/django_unfold/models/dropdown.py +40 -0
  209. django_cfg/modules/django_unfold/models/navigation.py +73 -0
  210. django_cfg/modules/django_unfold/models/tabs.py +25 -0
  211. django_cfg/modules/{unfold โ†’ django_unfold}/system_monitor.py +2 -2
  212. django_cfg/modules/django_unfold/utils.py +140 -0
  213. django_cfg/registry/__init__.py +23 -0
  214. django_cfg/registry/core.py +61 -0
  215. django_cfg/registry/exceptions.py +11 -0
  216. django_cfg/registry/modules.py +12 -0
  217. django_cfg/registry/services.py +26 -0
  218. django_cfg/registry/third_party.py +52 -0
  219. django_cfg/routing/__init__.py +19 -0
  220. django_cfg/routing/callbacks.py +198 -0
  221. django_cfg/routing/routers.py +48 -0
  222. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +8 -9
  223. django_cfg/templatetags/__init__.py +0 -0
  224. django_cfg/templatetags/django_cfg.py +33 -0
  225. django_cfg/urls.py +33 -0
  226. django_cfg/utils/path_resolution.py +1 -1
  227. django_cfg/utils/smart_defaults.py +7 -61
  228. django_cfg/utils/toolkit.py +663 -0
  229. {django_cfg-1.1.82.dist-info โ†’ django_cfg-1.2.0.dist-info}/METADATA +83 -86
  230. django_cfg-1.2.0.dist-info/RECORD +441 -0
  231. django_cfg/archive/django_sample.zip +0 -0
  232. django_cfg/models/unfold.py +0 -271
  233. django_cfg/modules/unfold/__init__.py +0 -29
  234. django_cfg/modules/unfold/dashboard.py +0 -318
  235. django_cfg/pyproject.toml +0 -370
  236. django_cfg/routers.py +0 -83
  237. django_cfg-1.1.82.dist-info/RECORD +0 -278
  238. /django_cfg/{exceptions.py โ†’ core/exceptions.py} +0 -0
  239. /django_cfg/modules/{unfold โ†’ django_unfold}/models.py +0 -0
  240. /django_cfg/modules/{unfold โ†’ django_unfold}/tailwind.py +0 -0
  241. /django_cfg/{version_check.py โ†’ utils/version_check.py} +0 -0
  242. {django_cfg-1.1.82.dist-info โ†’ django_cfg-1.2.0.dist-info}/WHEEL +0 -0
  243. {django_cfg-1.1.82.dist-info โ†’ django_cfg-1.2.0.dist-info}/entry_points.txt +0 -0
  244. {django_cfg-1.1.82.dist-info โ†’ django_cfg-1.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -5,7 +5,6 @@ Custom Django middleware components for Django CFG applications.
5
5
  ## ๐Ÿ“‹ Contents
6
6
 
7
7
  - [UserActivityMiddleware](#useractivitymiddleware) - User activity tracking
8
- - [PublicEndpointsMiddleware](#publicendpointsmiddleware) - Ignore invalid JWT tokens on public endpoints
9
8
 
10
9
  ## UserActivityMiddleware
11
10
 
@@ -159,160 +158,3 @@ class MyProjectConfig(DjangoConfig):
159
158
  # GET /api/users/?format=json
160
159
  # PUT /cfg/newsletter/subscribe/
161
160
  ```
162
-
163
- ## PublicEndpointsMiddleware
164
-
165
- Middleware that temporarily removes invalid JWT tokens from public endpoints to prevent authentication errors.
166
-
167
- ### โœจ Features
168
-
169
- - โœ… **Automatic activation** - No configuration needed, works out of the box
170
- - โœ… **Smart endpoint detection** - Configurable regex patterns for public endpoints
171
- - โœ… **JWT token detection** - Only processes requests with Bearer tokens
172
- - โœ… **Temporary removal** - Auth headers are restored after request processing
173
- - โœ… **Performance optimized** - Compiled regex patterns for fast matching
174
- - โœ… **Detailed logging** - Debug information for troubleshooting
175
- - โœ… **Statistics tracking** - Monitor middleware usage and effectiveness
176
-
177
- ### ๐ŸŽฏ Problem Solved
178
-
179
- When a frontend sends an invalid/expired JWT token to a public endpoint (like OTP request), Django's authentication middleware tries to authenticate the user and fails with "User not found" errors, even though the endpoint has `AllowAny` permissions.
180
-
181
- This middleware temporarily removes the `Authorization` header for public endpoints, allowing them to work without authentication errors.
182
-
183
- ### ๐Ÿš€ Automatic Integration
184
-
185
- The middleware is **automatically included** in all Django CFG projects:
186
-
187
- ```python
188
- class MyConfig(DjangoConfig):
189
- # No configuration needed - PublicEndpointsMiddleware is always active
190
- pass
191
- ```
192
-
193
- ### ๐ŸŽฏ Default Public Endpoints
194
-
195
- The middleware protects these endpoints by default:
196
-
197
- ```python
198
- DEFAULT_PUBLIC_PATTERNS = [
199
- r'^/api/accounts/otp/', # OTP endpoints (request, verify)
200
- r'^/cfg/accounts/otp/', # CFG OTP endpoints
201
- r'^/api/accounts/token/refresh/', # Token refresh
202
- r'^/cfg/accounts/token/refresh/', # CFG Token refresh
203
- r'^/api/health/', # Health check endpoints
204
- r'^/cfg/api/health/', # CFG Health check endpoints
205
- r'^/admin/login/', # Django admin login
206
- r'^/api/schema/', # API schema endpoints
207
- r'^/api/docs/', # API documentation
208
- ]
209
- ```
210
-
211
- ### โš™๏ธ Custom Configuration
212
-
213
- You can customize public endpoint patterns in your Django settings:
214
-
215
- ```python
216
- # settings.py (optional)
217
- PUBLIC_ENDPOINT_PATTERNS = [
218
- r'^/api/accounts/otp/',
219
- r'^/api/public/',
220
- r'^/api/webhooks/',
221
- # Add your custom patterns here
222
- ]
223
- ```
224
-
225
- ### ๐Ÿ” How It Works
226
-
227
- 1. **Request Processing**: Middleware checks if the request path matches public endpoint patterns
228
- 2. **Token Detection**: If a Bearer token is present, it's temporarily removed
229
- 3. **Request Handling**: Django processes the request without authentication
230
- 4. **Token Restoration**: The original Authorization header is restored after processing
231
-
232
- ### ๐Ÿ“Š Statistics
233
-
234
- Get middleware statistics for monitoring:
235
-
236
- ```python
237
- from django_cfg.middleware import PublicEndpointsMiddleware
238
-
239
- # In your view or management command
240
- middleware = PublicEndpointsMiddleware()
241
- stats = middleware.get_stats()
242
-
243
- print(stats)
244
- # {
245
- # 'requests_processed': 1250,
246
- # 'tokens_ignored': 45,
247
- # 'public_endpoints_hit': 120,
248
- # 'public_patterns_count': 9,
249
- # 'middleware_active': True
250
- # }
251
- ```
252
-
253
- ### ๐Ÿ” Logging
254
-
255
- The middleware logs activity at DEBUG level:
256
-
257
- ```python
258
- # settings.py
259
- LOGGING = {
260
- 'loggers': {
261
- 'django_cfg.middleware.public_endpoints': {
262
- 'level': 'DEBUG',
263
- 'handlers': ['console'],
264
- },
265
- },
266
- }
267
- ```
268
-
269
- ### ๐ŸŽ›๏ธ Manual Integration
270
-
271
- If you need to include the middleware manually (not recommended):
272
-
273
- ```python
274
- # settings.py
275
- MIDDLEWARE = [
276
- 'django.middleware.security.SecurityMiddleware',
277
- 'corsheaders.middleware.CorsMiddleware',
278
- 'django_cfg.middleware.PublicEndpointsMiddleware', # Add early in stack
279
- # ... other middleware
280
- ]
281
- ```
282
-
283
- ### ๐Ÿšจ Important Notes
284
-
285
- 1. **Always Active**: Middleware is included by default in all Django CFG projects
286
- 2. **Performance**: Uses compiled regex patterns for fast endpoint matching
287
- 3. **Safety**: Only removes Authorization headers temporarily, restores them after processing
288
- 4. **Logging**: All actions are logged for debugging and monitoring
289
-
290
- ### ๐Ÿ’ก Usage Examples
291
-
292
- The middleware works automatically with no configuration needed:
293
-
294
- ```python
295
- # Your DjangoConfig
296
- class MyProjectConfig(DjangoConfig):
297
- # PublicEndpointsMiddleware is automatically active
298
- pass
299
-
300
- # These requests will work even with invalid tokens:
301
- # POST /api/accounts/otp/request/ (with expired Bearer token)
302
- # POST /cfg/accounts/otp/verify/ (with invalid Bearer token)
303
- # GET /api/health/ (with any Bearer token)
304
- ```
305
-
306
- ### ๐Ÿ”ง Frontend Integration
307
-
308
- Perfect companion to frontend error handling:
309
-
310
- ```typescript
311
- // Frontend automatically clears invalid tokens on 401/403
312
- // Middleware ensures public endpoints work during token cleanup
313
- const response = await api.requestOTP({
314
- identifier: "user@example.com",
315
- channel: "email"
316
- });
317
- // โœ… Works even if localStorage has invalid token
318
- ```
@@ -5,9 +5,7 @@ Provides middleware components for Django CFG applications.
5
5
  """
6
6
 
7
7
  from .user_activity import UserActivityMiddleware
8
- from .public_endpoints import PublicEndpointsMiddleware
9
8
 
10
9
  __all__ = [
11
10
  'UserActivityMiddleware',
12
- 'PublicEndpointsMiddleware',
13
11
  ]
@@ -9,12 +9,12 @@ import logging
9
9
  from django.utils import timezone
10
10
  from django.contrib.auth import get_user_model
11
11
  from django.utils.deprecation import MiddlewareMixin
12
- from django_cfg.modules.base import BaseModule
12
+ from django_cfg.modules.base import BaseCfgModule
13
13
 
14
14
  logger = logging.getLogger(__name__)
15
15
 
16
16
 
17
- class UserActivityMiddleware(MiddlewareMixin, BaseModule):
17
+ class UserActivityMiddleware(MiddlewareMixin, BaseCfgModule):
18
18
  """
19
19
  Middleware to track user activity via last_login field.
20
20
 
@@ -31,7 +31,7 @@ class UserActivityMiddleware(MiddlewareMixin, BaseModule):
31
31
  def __init__(self, get_response=None):
32
32
  """Initialize the middleware."""
33
33
  super().__init__(get_response)
34
- BaseModule.__init__(self)
34
+ BaseCfgModule.__init__(self)
35
35
  self.get_response = get_response
36
36
 
37
37
  # Cache for tracking last update times (in memory)
@@ -0,0 +1,145 @@
1
+ """
2
+ API Configuration Model
3
+
4
+ Django REST Framework and API settings with Pydantic 2.
5
+ """
6
+
7
+ from typing import Dict, Any, List
8
+ from pydantic import Field, field_validator
9
+ from .base import BaseConfig
10
+
11
+
12
+ class APIConfig(BaseConfig):
13
+ """
14
+ ๐ŸŒ API Configuration - REST Framework and API settings
15
+
16
+ Configures Django REST Framework, pagination, throttling,
17
+ and API documentation settings.
18
+ """
19
+
20
+ # Pagination settings
21
+ page_size: int = Field(
22
+ default=20,
23
+ ge=1,
24
+ le=100,
25
+ description="Default API pagination page size"
26
+ )
27
+
28
+ max_page_size: int = Field(
29
+ default=100,
30
+ ge=1,
31
+ le=1000,
32
+ description="Maximum API pagination page size"
33
+ )
34
+
35
+ # Throttling settings
36
+ rate_limit_enabled: bool = Field(
37
+ default=True,
38
+ description="Enable API rate limiting"
39
+ )
40
+
41
+ rate_limit_anon: str = Field(
42
+ default="100/hour",
43
+ description="Rate limit for anonymous users"
44
+ )
45
+
46
+ rate_limit_user: str = Field(
47
+ default="1000/hour",
48
+ description="Rate limit for authenticated users"
49
+ )
50
+
51
+ # Documentation settings
52
+ docs_enabled: bool = Field(
53
+ default=True,
54
+ description="Enable API documentation"
55
+ )
56
+
57
+ docs_title: str = Field(
58
+ default="API Documentation",
59
+ description="API documentation title"
60
+ )
61
+
62
+ docs_version: str = Field(
63
+ default="v1",
64
+ description="API version"
65
+ )
66
+
67
+ # CORS settings for API
68
+ api_cors_origins: List[str] = Field(
69
+ default_factory=list,
70
+ description="CORS origins specifically for API endpoints"
71
+ )
72
+
73
+ @field_validator('page_size', 'max_page_size')
74
+ @classmethod
75
+ def validate_page_sizes(cls, v: int) -> int:
76
+ """Validate pagination sizes."""
77
+ if v <= 0:
78
+ raise ValueError("Page size must be positive")
79
+ return v
80
+
81
+ @field_validator('rate_limit_anon', 'rate_limit_user')
82
+ @classmethod
83
+ def validate_rate_limits(cls, v: str) -> str:
84
+ """Validate rate limit format."""
85
+ import re
86
+ pattern = r'^\d+/(second|minute|hour|day)$'
87
+ if not re.match(pattern, v):
88
+ raise ValueError("Rate limit must be in format: number/(second|minute|hour|day)")
89
+ return v
90
+
91
+ def to_django_settings(self) -> Dict[str, Any]:
92
+ """Convert to Django REST Framework settings."""
93
+ settings = {
94
+ 'REST_FRAMEWORK': {
95
+ 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
96
+ 'PAGE_SIZE': self.page_size,
97
+ 'MAX_PAGE_SIZE': self.max_page_size,
98
+
99
+ 'DEFAULT_AUTHENTICATION_CLASSES': [
100
+ 'rest_framework.authentication.SessionAuthentication',
101
+ 'rest_framework.authentication.TokenAuthentication',
102
+ 'rest_framework_simplejwt.authentication.JWTAuthentication',
103
+ ],
104
+
105
+ 'DEFAULT_PERMISSION_CLASSES': [
106
+ 'rest_framework.permissions.IsAuthenticated',
107
+ ],
108
+
109
+ 'DEFAULT_RENDERER_CLASSES': [
110
+ 'rest_framework.renderers.JSONRenderer',
111
+ 'rest_framework.renderers.BrowsableAPIRenderer',
112
+ ],
113
+
114
+ 'DEFAULT_FILTER_BACKENDS': [
115
+ 'django_filters.rest_framework.DjangoFilterBackend',
116
+ 'rest_framework.filters.OrderingFilter',
117
+ 'rest_framework.filters.SearchFilter',
118
+ ],
119
+ }
120
+ }
121
+
122
+ # Add throttling if enabled
123
+ if self.rate_limit_enabled:
124
+ settings['REST_FRAMEWORK']['DEFAULT_THROTTLE_CLASSES'] = [
125
+ 'rest_framework.throttling.AnonRateThrottle',
126
+ 'rest_framework.throttling.UserRateThrottle',
127
+ ]
128
+ settings['REST_FRAMEWORK']['DEFAULT_THROTTLE_RATES'] = {
129
+ 'anon': self.rate_limit_anon,
130
+ 'user': self.rate_limit_user,
131
+ }
132
+
133
+ # API documentation settings
134
+ if self.docs_enabled:
135
+ settings.update({
136
+ 'SPECTACULAR_SETTINGS': {
137
+ 'TITLE': self.docs_title,
138
+ 'DESCRIPTION': 'API for Django application',
139
+ 'VERSION': self.docs_version,
140
+ 'SERVE_INCLUDE_SCHEMA': False,
141
+ 'SCHEMA_PATH_PREFIX': '/api/',
142
+ }
143
+ })
144
+
145
+ return settings
@@ -0,0 +1,287 @@
1
+ """
2
+ Base Configuration Model
3
+
4
+ Foundation for all Django configuration models using Pydantic 2.
5
+ """
6
+
7
+ import os
8
+ from pathlib import Path
9
+ from typing import Dict, Any, Optional
10
+
11
+ from pydantic import BaseModel, ConfigDict, Field
12
+ from pydantic_settings import BaseSettings, SettingsConfigDict
13
+ from pydantic_settings.sources import DotEnvSettingsSource
14
+
15
+
16
+ class ConfigValidationError(Exception):
17
+ """Configuration validation error with helpful developer messages."""
18
+
19
+ def __init__(self, field: str, value: Any, message: str):
20
+ self.field = field
21
+ self.value = value
22
+ self.message = message
23
+ super().__init__(f"โŒ Config error in '{field}': {message} (got: {value})")
24
+
25
+
26
+ class BaseConfig(BaseSettings):
27
+ """
28
+ ๐Ÿ”ฅ Base configuration model with amazing developer experience
29
+
30
+ Features:
31
+ - Automatic .env file detection (.env.dev, .env.prod, etc.)
32
+ - Nested environment variables with __ delimiter
33
+ - Type-safe validation with Pydantic 2
34
+ - Helpful error messages for developers
35
+ - Environment-specific configuration
36
+ """
37
+
38
+ model_config = SettingsConfigDict(
39
+ # Environment file settings
40
+ env_file='.env',
41
+ env_file_encoding='utf-8',
42
+ env_nested_delimiter='__',
43
+ case_sensitive=False,
44
+
45
+ # Validation settings
46
+ validate_assignment=True,
47
+ validate_default=True,
48
+ extra='ignore',
49
+
50
+ # Performance settings
51
+ frozen=False,
52
+ arbitrary_types_allowed=False,
53
+ use_enum_values=True,
54
+ )
55
+
56
+ @classmethod
57
+ def settings_customise_sources(
58
+ cls,
59
+ settings_cls,
60
+ init_settings,
61
+ env_settings,
62
+ dotenv_settings,
63
+ file_secret_settings,
64
+ ):
65
+ """Custom settings sources with automatic env file detection."""
66
+ env_file = cls._detect_env_file()
67
+ if env_file:
68
+ dotenv_settings = DotEnvSettingsSource(
69
+ settings_cls,
70
+ env_file=env_file,
71
+ env_file_encoding='utf-8'
72
+ )
73
+
74
+ return (
75
+ init_settings,
76
+ env_settings,
77
+ dotenv_settings,
78
+ file_secret_settings,
79
+ )
80
+
81
+ def __init__(self, **kwargs):
82
+ """Initialize with smart .env file detection."""
83
+ try:
84
+ super().__init__(**kwargs)
85
+ except Exception as e:
86
+ self._handle_validation_error(e)
87
+ raise
88
+
89
+ @classmethod
90
+ def _detect_env_file(cls) -> Optional[str]:
91
+ """
92
+ ๐Ÿ” Smart environment file detection
93
+
94
+ Priority order:
95
+ 1. .env.local (local overrides, should be git-ignored)
96
+ 2. .env.dev / .env.development (development)
97
+ 3. .env.prod / .env.production (production)
98
+ 4. config.env.dev / config.env.prod (project-specific)
99
+ 5. .env (fallback)
100
+ """
101
+ env_files = [
102
+ '.env.local',
103
+ '.env.dev',
104
+ '.env.development',
105
+ '.env.prod',
106
+ '.env.production',
107
+ 'config.env.dev',
108
+ 'config.env.prod',
109
+ '.env'
110
+ ]
111
+
112
+ for env_file in env_files:
113
+ if Path(env_file).exists():
114
+ return env_file
115
+
116
+ return None
117
+
118
+ def _show_debug(self) -> bool:
119
+ """Check if we should show debug info."""
120
+ return os.getenv('DEBUG', 'false').lower() in ('true', '1', 'yes')
121
+
122
+ def _handle_validation_error(self, error: Exception):
123
+ """Provide helpful validation error messages for developers."""
124
+ if hasattr(error, 'errors'):
125
+ print("โŒ Django Configuration Validation Errors:")
126
+ print("=" * 50)
127
+
128
+ for err in error.errors():
129
+ field = '.'.join(str(x) for x in err['loc'])
130
+ message = err['msg']
131
+ input_val = err.get('input', 'N/A')
132
+
133
+ print(f"๐Ÿ”ด Field: {field}")
134
+ print(f" Error: {message}")
135
+ print(f" Value: {input_val}")
136
+ print(f" ๐Ÿ’ก Fix: Check your .env file or environment variables")
137
+ print()
138
+
139
+ print("๐Ÿ“š Documentation: https://django-config-toolkit.readthedocs.io/")
140
+ print("=" * 50)
141
+
142
+ def to_django_settings(self) -> Dict[str, Any]:
143
+ """Convert configuration to Django-compatible settings dictionary."""
144
+ return self.model_dump(exclude_none=True, by_alias=True)
145
+
146
+ def get_field_info(self) -> Dict[str, Dict[str, Any]]:
147
+ """Get detailed field information for developers."""
148
+ field_info = {}
149
+
150
+ for field_name, field in self.model_fields.items():
151
+ field_info[field_name] = {
152
+ 'description': field.description or f"Configuration for {field_name}",
153
+ 'type': str(field.annotation) if hasattr(field, 'annotation') else 'Any',
154
+ 'default': field.default if field.default is not ... else None,
155
+ 'required': field.is_required(),
156
+ 'env_var': field_name.upper(),
157
+ }
158
+
159
+ return field_info
160
+
161
+ def print_field_help(self):
162
+ """Print helpful field information for developers."""
163
+ print(f"๐Ÿ“‹ {self.__class__.__name__} Configuration Fields:")
164
+ print("=" * 60)
165
+
166
+ for field_name, info in self.get_field_info().items():
167
+ current_value = getattr(self, field_name, None)
168
+
169
+ # Hide sensitive values
170
+ if any(word in field_name.lower() for word in ['secret', 'password', 'key', 'token']):
171
+ display_value = "***HIDDEN***" if current_value else "Not set"
172
+ else:
173
+ display_value = current_value
174
+
175
+ print(f"๐Ÿ”ง {field_name}:")
176
+ print(f" ๐Ÿ“ {info['description']}")
177
+ print(f" ๐Ÿท๏ธ Type: {info['type']}")
178
+ print(f" ๐ŸŒ Env: {info['env_var']}")
179
+ print(f" ๐Ÿ’พ Current: {display_value}")
180
+ if info['default'] is not None:
181
+ print(f" ๐ŸŽฏ Default: {info['default']}")
182
+ if info['required']:
183
+ print(f" โš ๏ธ Required: Yes")
184
+ print()
185
+
186
+ @classmethod
187
+ def create_env_example(cls, filename: str = ".env.example") -> None:
188
+ """
189
+ ๐Ÿš€ Create example .env file for developers
190
+
191
+ This generates a complete .env.example file with all fields,
192
+ descriptions, and example values.
193
+ """
194
+ lines = [
195
+ "# ๐Ÿš€ Django Configuration Environment Variables",
196
+ "# Generated by Django Config Toolkit",
197
+ "# Copy this file to .env and customize your settings",
198
+ "",
199
+ f"# === {cls.__name__} Configuration ===",
200
+ "",
201
+ ]
202
+
203
+ # Create temporary instance to get field info
204
+ try:
205
+ temp_instance = cls()
206
+ field_info = temp_instance.get_field_info()
207
+ except:
208
+ # Fallback if instance creation fails
209
+ field_info = {}
210
+ for field_name, field in cls.model_fields.items():
211
+ field_info[field_name] = {
212
+ 'description': field.description or f"Configure {field_name}",
213
+ 'default': field.default if field.default is not ... else None,
214
+ 'env_var': field_name.upper(),
215
+ }
216
+
217
+ for field_name, info in field_info.items():
218
+ # Add description
219
+ lines.append(f"# {info['description']}")
220
+
221
+ # Generate example value
222
+ default_val = info.get('default')
223
+ if default_val is not None:
224
+ example_value = default_val
225
+ elif 'secret' in field_name.lower() or 'key' in field_name.lower():
226
+ example_value = "your-secret-key-change-this-to-something-secure"
227
+ elif 'password' in field_name.lower():
228
+ example_value = "your-secure-password"
229
+ elif 'url' in field_name.lower():
230
+ if 'database' in field_name.lower():
231
+ example_value = "postgresql://user:password@localhost:5432/dbname"
232
+ elif 'redis' in field_name.lower():
233
+ example_value = "redis://localhost:6379/0"
234
+ else:
235
+ example_value = "https://example.com"
236
+ elif 'debug' in field_name.lower():
237
+ example_value = "true"
238
+ elif 'port' in field_name.lower():
239
+ example_value = "5432"
240
+ elif 'timeout' in field_name.lower():
241
+ example_value = "30"
242
+ else:
243
+ example_value = "change-me"
244
+
245
+ # Add environment variable
246
+ lines.append(f"{info['env_var']}={example_value}")
247
+ lines.append("")
248
+
249
+ # Write file
250
+ with open(filename, 'w', encoding='utf-8') as f:
251
+ f.write('\n'.join(lines))
252
+
253
+ print(f"โœ… Created example environment file: {filename}")
254
+ print(f"๐Ÿ’ก Copy it to .env and customize your settings!")
255
+ print(f"๐Ÿ“š More info: https://django-config-toolkit.readthedocs.io/")
256
+
257
+ def validate_for_environment(self, environment: str = "development") -> bool:
258
+ """
259
+ ๐Ÿงช Validate configuration for specific environment
260
+
261
+ Args:
262
+ environment: Target environment (development/production/testing)
263
+
264
+ Returns:
265
+ True if configuration is valid for the environment
266
+ """
267
+ try:
268
+ # Perform environment-specific validation
269
+ if environment == "production":
270
+ return self._validate_production()
271
+ elif environment == "development":
272
+ return self._validate_development()
273
+ else:
274
+ return True
275
+ except Exception as e:
276
+ print(f"โŒ Validation failed for {environment}: {e}")
277
+ return False
278
+
279
+ def _validate_production(self) -> bool:
280
+ """Validate production-specific requirements."""
281
+ # Override in subclasses for specific validation
282
+ return True
283
+
284
+ def _validate_development(self) -> bool:
285
+ """Validate development-specific requirements."""
286
+ # Override in subclasses for specific validation
287
+ return True
@@ -12,10 +12,10 @@ from typing import Dict, Optional, Any, Literal, Union
12
12
  from pydantic import BaseModel, Field, field_validator, model_validator
13
13
  from urllib.parse import urlparse
14
14
 
15
- from django_cfg.exceptions import CacheError, ValidationError
15
+ from django_cfg.core.exceptions import CacheError, ValidationError
16
16
 
17
17
 
18
- class CacheBackend(BaseModel):
18
+ class CacheConfig(BaseModel):
19
19
  """
20
20
  Type-safe cache backend configuration.
21
21
 
@@ -160,7 +160,7 @@ class CacheBackend(BaseModel):
160
160
  return v
161
161
 
162
162
  @model_validator(mode='after')
163
- def validate_configuration_consistency(self) -> 'CacheBackend':
163
+ def validate_configuration_consistency(self) -> 'CacheConfig':
164
164
  """Validate cache configuration consistency."""
165
165
  # Warn about compression with JSON serializer
166
166
  if self.compress and self.serializer == "json":
@@ -336,5 +336,5 @@ class CacheBackend(BaseModel):
336
336
 
337
337
  # Export all models
338
338
  __all__ = [
339
- "CacheBackend",
339
+ "CacheConfig",
340
340
  ]