django-cfg 1.4.10__py3-none-any.whl → 1.4.13__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 (225) hide show
  1. django_cfg/apps/agents/management/commands/create_agent.py +1 -1
  2. django_cfg/apps/agents/management/commands/orchestrator_status.py +3 -3
  3. django_cfg/apps/newsletter/serializers.py +40 -3
  4. django_cfg/apps/newsletter/views/campaigns.py +12 -3
  5. django_cfg/apps/newsletter/views/emails.py +14 -3
  6. django_cfg/apps/newsletter/views/subscriptions.py +12 -2
  7. django_cfg/apps/payments/views/api/currencies.py +49 -6
  8. django_cfg/apps/payments/views/api/webhooks.py +72 -7
  9. django_cfg/apps/payments/views/overview/serializers.py +34 -1
  10. django_cfg/apps/payments/views/overview/views.py +2 -1
  11. django_cfg/apps/payments/views/serializers/payments.py +6 -6
  12. django_cfg/apps/urls.py +106 -45
  13. django_cfg/core/base/config_model.py +2 -2
  14. django_cfg/core/constants.py +1 -1
  15. django_cfg/core/generation/integration_generators/__init__.py +1 -1
  16. django_cfg/core/generation/integration_generators/api.py +73 -49
  17. django_cfg/core/integration/display/startup.py +30 -22
  18. django_cfg/core/integration/url_integration.py +15 -16
  19. django_cfg/management/commands/check_endpoints.py +11 -160
  20. django_cfg/management/commands/check_settings.py +13 -348
  21. django_cfg/management/commands/clear_constance.py +13 -201
  22. django_cfg/management/commands/create_token.py +13 -321
  23. django_cfg/management/commands/generate_clients.py +23 -0
  24. django_cfg/management/commands/list_urls.py +13 -306
  25. django_cfg/management/commands/migrate_all.py +13 -126
  26. django_cfg/management/commands/migrator.py +13 -396
  27. django_cfg/management/commands/rundramatiq.py +15 -247
  28. django_cfg/management/commands/rundramatiq_simulator.py +12 -429
  29. django_cfg/management/commands/runserver_ngrok.py +15 -160
  30. django_cfg/management/commands/script.py +12 -488
  31. django_cfg/management/commands/show_config.py +12 -215
  32. django_cfg/management/commands/show_urls.py +12 -342
  33. django_cfg/management/commands/superuser.py +15 -295
  34. django_cfg/management/commands/task_clear.py +14 -217
  35. django_cfg/management/commands/task_status.py +13 -248
  36. django_cfg/management/commands/test_email.py +15 -86
  37. django_cfg/management/commands/test_telegram.py +14 -61
  38. django_cfg/management/commands/test_twilio.py +15 -105
  39. django_cfg/management/commands/tree.py +13 -383
  40. django_cfg/management/commands/validate_openapi.py +10 -0
  41. django_cfg/middleware/README.md +1 -1
  42. django_cfg/middleware/user_activity.py +3 -3
  43. django_cfg/models/__init__.py +2 -2
  44. django_cfg/models/api/drf/spectacular.py +6 -6
  45. django_cfg/models/django/__init__.py +2 -2
  46. django_cfg/models/django/openapi.py +162 -0
  47. django_cfg/modules/django_admin/management/commands/check_endpoints.py +169 -0
  48. django_cfg/modules/django_admin/management/commands/check_settings.py +355 -0
  49. django_cfg/modules/django_admin/management/commands/clear_constance.py +208 -0
  50. django_cfg/modules/django_admin/management/commands/create_token.py +328 -0
  51. django_cfg/modules/django_admin/management/commands/list_urls.py +313 -0
  52. django_cfg/modules/django_admin/management/commands/migrate_all.py +133 -0
  53. django_cfg/modules/django_admin/management/commands/migrator.py +403 -0
  54. django_cfg/modules/django_admin/management/commands/script.py +496 -0
  55. django_cfg/modules/django_admin/management/commands/show_config.py +225 -0
  56. django_cfg/modules/django_admin/management/commands/show_urls.py +361 -0
  57. django_cfg/modules/django_admin/management/commands/superuser.py +302 -0
  58. django_cfg/modules/django_admin/management/commands/tree.py +390 -0
  59. django_cfg/modules/django_client/__init__.py +20 -0
  60. django_cfg/modules/django_client/apps.py +35 -0
  61. django_cfg/modules/django_client/core/__init__.py +56 -0
  62. django_cfg/modules/django_client/core/archive/__init__.py +11 -0
  63. django_cfg/modules/django_client/core/archive/manager.py +134 -0
  64. django_cfg/modules/django_client/core/cli/__init__.py +12 -0
  65. django_cfg/modules/django_client/core/cli/main.py +235 -0
  66. django_cfg/modules/django_client/core/config/__init__.py +18 -0
  67. django_cfg/modules/django_client/core/config/config.py +208 -0
  68. django_cfg/modules/django_client/core/config/group.py +101 -0
  69. django_cfg/modules/django_client/core/config/service.py +209 -0
  70. django_cfg/modules/django_client/core/generator/__init__.py +115 -0
  71. django_cfg/modules/django_client/core/generator/base.py +838 -0
  72. django_cfg/modules/django_client/core/generator/python/__init__.py +16 -0
  73. django_cfg/modules/django_client/core/generator/python/async_client_gen.py +174 -0
  74. django_cfg/modules/django_client/core/generator/python/files_generator.py +180 -0
  75. django_cfg/modules/django_client/core/generator/python/generator.py +182 -0
  76. django_cfg/modules/django_client/core/generator/python/models_generator.py +318 -0
  77. django_cfg/modules/django_client/core/generator/python/operations_generator.py +278 -0
  78. django_cfg/modules/django_client/core/generator/python/sync_client_gen.py +102 -0
  79. django_cfg/modules/django_client/core/generator/python/templates/__init__.py.jinja +9 -0
  80. django_cfg/modules/django_client/core/generator/python/templates/api_wrapper.py.jinja +153 -0
  81. django_cfg/modules/django_client/core/generator/python/templates/app_init.py.jinja +6 -0
  82. django_cfg/modules/django_client/core/generator/python/templates/client/app_client.py.jinja +18 -0
  83. django_cfg/modules/django_client/core/generator/python/templates/client/flat_client.py.jinja +38 -0
  84. django_cfg/modules/django_client/core/generator/python/templates/client/main_client.py.jinja +68 -0
  85. django_cfg/modules/django_client/core/generator/python/templates/client/main_client_file.py.jinja +14 -0
  86. django_cfg/modules/django_client/core/generator/python/templates/client/operation_method.py.jinja +9 -0
  87. django_cfg/modules/django_client/core/generator/python/templates/client/sub_client.py.jinja +18 -0
  88. django_cfg/modules/django_client/core/generator/python/templates/client/sync_main_client.py.jinja +50 -0
  89. django_cfg/modules/django_client/core/generator/python/templates/client/sync_operation_method.py.jinja +9 -0
  90. django_cfg/modules/django_client/core/generator/python/templates/client/sync_sub_client.py.jinja +18 -0
  91. django_cfg/modules/django_client/core/generator/python/templates/client_file.py.jinja +13 -0
  92. django_cfg/modules/django_client/core/generator/python/templates/main_init.py.jinja +52 -0
  93. django_cfg/modules/django_client/core/generator/python/templates/models/app_models.py.jinja +17 -0
  94. django_cfg/modules/django_client/core/generator/python/templates/models/enum_class.py.jinja +17 -0
  95. django_cfg/modules/django_client/core/generator/python/templates/models/enums.py.jinja +8 -0
  96. django_cfg/modules/django_client/core/generator/python/templates/models/models.py.jinja +17 -0
  97. django_cfg/modules/django_client/core/generator/python/templates/models/schema_class.py.jinja +21 -0
  98. django_cfg/modules/django_client/core/generator/python/templates/pyproject.toml.jinja +55 -0
  99. django_cfg/modules/django_client/core/generator/python/templates/utils/logger.py.jinja +255 -0
  100. django_cfg/modules/django_client/core/generator/python/templates/utils/retry.py.jinja +271 -0
  101. django_cfg/modules/django_client/core/generator/python/templates/utils/schema.py.jinja +12 -0
  102. django_cfg/modules/django_client/core/generator/typescript/__init__.py +14 -0
  103. django_cfg/modules/django_client/core/generator/typescript/client_generator.py +165 -0
  104. django_cfg/modules/django_client/core/generator/typescript/fetchers_generator.py +428 -0
  105. django_cfg/modules/django_client/core/generator/typescript/files_generator.py +207 -0
  106. django_cfg/modules/django_client/core/generator/typescript/generator.py +432 -0
  107. django_cfg/modules/django_client/core/generator/typescript/hooks_generator.py +536 -0
  108. django_cfg/modules/django_client/core/generator/typescript/models_generator.py +245 -0
  109. django_cfg/modules/django_client/core/generator/typescript/operations_generator.py +298 -0
  110. django_cfg/modules/django_client/core/generator/typescript/schemas_generator.py +329 -0
  111. django_cfg/modules/django_client/core/generator/typescript/templates/api_instance.ts.jinja +131 -0
  112. django_cfg/modules/django_client/core/generator/typescript/templates/app_index.ts.jinja +2 -0
  113. django_cfg/modules/django_client/core/generator/typescript/templates/client/app_client.ts.jinja +18 -0
  114. django_cfg/modules/django_client/core/generator/typescript/templates/client/client.ts.jinja +403 -0
  115. django_cfg/modules/django_client/core/generator/typescript/templates/client/flat_client.ts.jinja +109 -0
  116. django_cfg/modules/django_client/core/generator/typescript/templates/client/main_client_file.ts.jinja +10 -0
  117. django_cfg/modules/django_client/core/generator/typescript/templates/client/operation.ts.jinja +61 -0
  118. django_cfg/modules/django_client/core/generator/typescript/templates/client/sub_client.ts.jinja +15 -0
  119. django_cfg/modules/django_client/core/generator/typescript/templates/client_file.ts.jinja +9 -0
  120. django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/fetchers.ts.jinja +45 -0
  121. django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/index.ts.jinja +30 -0
  122. django_cfg/modules/django_client/core/generator/typescript/templates/index.ts.jinja +5 -0
  123. django_cfg/modules/django_client/core/generator/typescript/templates/main_index.ts.jinja +268 -0
  124. django_cfg/modules/django_client/core/generator/typescript/templates/models/app_models.ts.jinja +8 -0
  125. django_cfg/modules/django_client/core/generator/typescript/templates/models/enums.ts.jinja +4 -0
  126. django_cfg/modules/django_client/core/generator/typescript/templates/models/models.ts.jinja +8 -0
  127. django_cfg/modules/django_client/core/generator/typescript/templates/package.json.jinja +52 -0
  128. django_cfg/modules/django_client/core/generator/typescript/templates/schemas/index.ts.jinja +21 -0
  129. django_cfg/modules/django_client/core/generator/typescript/templates/schemas/schema.ts.jinja +24 -0
  130. django_cfg/modules/django_client/core/generator/typescript/templates/tsconfig.json.jinja +20 -0
  131. django_cfg/modules/django_client/core/generator/typescript/templates/utils/errors.ts.jinja +116 -0
  132. django_cfg/modules/django_client/core/generator/typescript/templates/utils/http.ts.jinja +98 -0
  133. django_cfg/modules/django_client/core/generator/typescript/templates/utils/logger.ts.jinja +259 -0
  134. django_cfg/modules/django_client/core/generator/typescript/templates/utils/retry.ts.jinja +175 -0
  135. django_cfg/modules/django_client/core/generator/typescript/templates/utils/schema.ts.jinja +7 -0
  136. django_cfg/modules/django_client/core/generator/typescript/templates/utils/storage.ts.jinja +158 -0
  137. django_cfg/modules/django_client/core/groups/__init__.py +13 -0
  138. django_cfg/modules/django_client/core/groups/detector.py +178 -0
  139. django_cfg/modules/django_client/core/groups/manager.py +314 -0
  140. django_cfg/modules/django_client/core/ir/__init__.py +57 -0
  141. django_cfg/modules/django_client/core/ir/context.py +387 -0
  142. django_cfg/modules/django_client/core/ir/operation.py +518 -0
  143. django_cfg/modules/django_client/core/ir/schema.py +353 -0
  144. django_cfg/modules/django_client/core/parser/__init__.py +74 -0
  145. django_cfg/modules/django_client/core/parser/base.py +648 -0
  146. django_cfg/modules/django_client/core/parser/models/__init__.py +74 -0
  147. django_cfg/modules/django_client/core/parser/models/base.py +212 -0
  148. django_cfg/modules/django_client/core/parser/models/components.py +160 -0
  149. django_cfg/modules/django_client/core/parser/models/openapi.py +203 -0
  150. django_cfg/modules/django_client/core/parser/models/operation.py +207 -0
  151. django_cfg/modules/django_client/core/parser/models/schema.py +266 -0
  152. django_cfg/modules/django_client/core/parser/openapi30.py +56 -0
  153. django_cfg/modules/django_client/core/parser/openapi31.py +64 -0
  154. django_cfg/modules/django_client/core/validation/__init__.py +22 -0
  155. django_cfg/modules/django_client/core/validation/checker.py +134 -0
  156. django_cfg/modules/django_client/core/validation/fixer.py +216 -0
  157. django_cfg/modules/django_client/core/validation/reporter.py +480 -0
  158. django_cfg/modules/django_client/core/validation/rules/__init__.py +11 -0
  159. django_cfg/modules/django_client/core/validation/rules/base.py +96 -0
  160. django_cfg/modules/django_client/core/validation/rules/type_hints.py +288 -0
  161. django_cfg/modules/django_client/core/validation/safety.py +266 -0
  162. django_cfg/modules/django_client/management/__init__.py +3 -0
  163. django_cfg/modules/django_client/management/commands/__init__.py +3 -0
  164. django_cfg/modules/django_client/management/commands/generate_client.py +427 -0
  165. django_cfg/modules/django_client/management/commands/validate_openapi.py +343 -0
  166. django_cfg/modules/django_client/pytest.ini +30 -0
  167. django_cfg/modules/django_client/spectacular/__init__.py +10 -0
  168. django_cfg/modules/django_client/spectacular/async_detection.py +187 -0
  169. django_cfg/modules/django_client/spectacular/enum_naming.py +192 -0
  170. django_cfg/modules/django_client/urls.py +72 -0
  171. django_cfg/{dashboard → modules/django_dashboard}/DEBUG_README.md +2 -2
  172. django_cfg/{dashboard → modules/django_dashboard}/REFACTORING_SUMMARY.md +1 -1
  173. django_cfg/modules/django_dashboard/management/__init__.py +0 -0
  174. django_cfg/modules/django_dashboard/management/commands/__init__.py +0 -0
  175. django_cfg/{dashboard → modules/django_dashboard}/management/commands/debug_dashboard.py +5 -5
  176. django_cfg/modules/django_dashboard/sections/documentation.py +391 -0
  177. django_cfg/modules/django_email/management/__init__.py +0 -0
  178. django_cfg/modules/django_email/management/commands/__init__.py +0 -0
  179. django_cfg/modules/django_email/management/commands/test_email.py +93 -0
  180. django_cfg/modules/django_logging/LOGGING_GUIDE.md +1 -1
  181. django_cfg/modules/django_logging/django_logger.py +6 -6
  182. django_cfg/modules/django_ngrok/management/__init__.py +0 -0
  183. django_cfg/modules/django_ngrok/management/commands/__init__.py +0 -0
  184. django_cfg/modules/django_ngrok/management/commands/runserver_ngrok.py +167 -0
  185. django_cfg/modules/django_tasks/management/__init__.py +0 -0
  186. django_cfg/modules/django_tasks/management/commands/__init__.py +0 -0
  187. django_cfg/modules/django_tasks/management/commands/rundramatiq.py +254 -0
  188. django_cfg/modules/django_tasks/management/commands/rundramatiq_simulator.py +437 -0
  189. django_cfg/modules/django_tasks/management/commands/task_clear.py +226 -0
  190. django_cfg/modules/django_tasks/management/commands/task_status.py +257 -0
  191. django_cfg/modules/django_telegram/management/__init__.py +0 -0
  192. django_cfg/modules/django_telegram/management/commands/__init__.py +0 -0
  193. django_cfg/modules/django_telegram/management/commands/test_telegram.py +68 -0
  194. django_cfg/modules/django_twilio/management/__init__.py +0 -0
  195. django_cfg/modules/django_twilio/management/commands/__init__.py +0 -0
  196. django_cfg/modules/django_twilio/management/commands/test_twilio.py +112 -0
  197. django_cfg/modules/django_unfold/callbacks/main.py +21 -10
  198. django_cfg/modules/django_unfold/callbacks/revolution.py +41 -36
  199. django_cfg/pyproject.toml +2 -6
  200. django_cfg/registry/third_party.py +5 -7
  201. django_cfg/routing/callbacks.py +1 -1
  202. django_cfg/static/admin/css/prose-unfold.css +666 -0
  203. django_cfg/templates/admin/index.html +8 -0
  204. django_cfg/templates/admin/index_new.html +13 -0
  205. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +15 -3
  206. django_cfg/templates/admin/sections/documentation_section.html +172 -0
  207. django_cfg/templates/admin/snippets/tabs/documentation_tab.html +231 -0
  208. {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/METADATA +2 -2
  209. {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/RECORD +224 -74
  210. django_cfg/management/commands/generate.py +0 -107
  211. /django_cfg/models/django/{revolution.py → revolution_legacy.py} +0 -0
  212. /django_cfg/{dashboard → modules/django_admin}/management/__init__.py +0 -0
  213. /django_cfg/{dashboard → modules/django_admin}/management/commands/__init__.py +0 -0
  214. /django_cfg/{dashboard → modules/django_dashboard}/__init__.py +0 -0
  215. /django_cfg/{dashboard → modules/django_dashboard}/components.py +0 -0
  216. /django_cfg/{dashboard → modules/django_dashboard}/debug.py +0 -0
  217. /django_cfg/{dashboard → modules/django_dashboard}/sections/__init__.py +0 -0
  218. /django_cfg/{dashboard → modules/django_dashboard}/sections/base.py +0 -0
  219. /django_cfg/{dashboard → modules/django_dashboard}/sections/commands.py +0 -0
  220. /django_cfg/{dashboard → modules/django_dashboard}/sections/overview.py +0 -0
  221. /django_cfg/{dashboard → modules/django_dashboard}/sections/stats.py +0 -0
  222. /django_cfg/{dashboard → modules/django_dashboard}/sections/system.py +0 -0
  223. {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/WHEEL +0 -0
  224. {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/entry_points.txt +0 -0
  225. {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,208 @@
1
+ """
2
+ Clear Constance Command for Django Config Toolkit
3
+ Clear Constance configuration cache and database records.
4
+ """
5
+
6
+ from django.core.management.base import BaseCommand
7
+ from django.core.cache import cache
8
+ from django.conf import settings
9
+ from django_cfg.modules.django_logging import get_logger
10
+
11
+
12
+
13
+
14
+ class Command(BaseCommand):
15
+ logger = get_logger('clear_constance')
16
+
17
+ # Web execution metadata
18
+ web_executable = False
19
+ requires_input = True
20
+ is_destructive = True
21
+
22
+ help = 'Clear Constance configuration cache and database records'
23
+
24
+ def add_arguments(self, parser):
25
+ parser.add_argument(
26
+ '--cache-only',
27
+ action='store_true',
28
+ help='Clear only cache, not database records'
29
+ )
30
+ parser.add_argument(
31
+ '--db-only',
32
+ action='store_true',
33
+ help='Clear only database records, not cache'
34
+ )
35
+ parser.add_argument(
36
+ '--confirm',
37
+ action='store_true',
38
+ help='Skip confirmation prompt'
39
+ )
40
+ parser.add_argument(
41
+ '--dry-run',
42
+ action='store_true',
43
+ help='Show what would be cleared without actually clearing'
44
+ )
45
+
46
+ def handle(self, *args, **options):
47
+ """Handle the command execution."""
48
+ self.logger.info("Starting clear_constance command")
49
+ self.stdout.write(self.style.SUCCESS('🧹 Constance Clear Tool - Django Config Toolkit\n'))
50
+
51
+ # Check if constance is installed
52
+ if 'constance' not in settings.INSTALLED_APPS:
53
+ self.stdout.write(self.style.ERROR('❌ Constance is not installed or not in INSTALLED_APPS'))
54
+ return
55
+
56
+ # Determine what to clear
57
+ clear_cache = not options['db_only']
58
+ clear_db = not options['cache_only']
59
+
60
+ if options['dry_run']:
61
+ self._show_dry_run(clear_cache, clear_db)
62
+ return
63
+
64
+ # Confirm action
65
+ if not options['confirm']:
66
+ if not self._confirm_clear(clear_cache, clear_db):
67
+ self.stdout.write('Operation cancelled')
68
+ return
69
+
70
+ # Perform clearing
71
+ self._clear_constance(clear_cache, clear_db)
72
+
73
+ def _show_dry_run(self, clear_cache, clear_db):
74
+ """Show what would be cleared in dry run mode."""
75
+ self.stdout.write(self.style.SUCCESS('=== Dry Run - What would be cleared ==='))
76
+
77
+ if clear_cache:
78
+ self.stdout.write('🗑️ Cache: All Django cache entries')
79
+ self.stdout.write('🗑️ Cache: Constance-specific cache entries')
80
+
81
+ if clear_db:
82
+ self.stdout.write('🗑️ Database: All Constance configuration records')
83
+
84
+ # Try to show current records count
85
+ try:
86
+ from django.apps import apps
87
+ Constance = apps.get_model('constance', 'Constance')
88
+ count = Constance.objects.count()
89
+ self.stdout.write(f' Current records: {count}')
90
+ except Exception as e:
91
+ self.stdout.write(f' Could not count records: {e}')
92
+
93
+ self.stdout.write('\n✅ Dry run completed - nothing was actually cleared')
94
+
95
+ def _confirm_clear(self, clear_cache, clear_db) -> bool:
96
+ """Confirm the clear operation with user."""
97
+ actions = []
98
+ if clear_cache:
99
+ actions.append('cache')
100
+ if clear_db:
101
+ actions.append('database records')
102
+
103
+ action_text = ' and '.join(actions)
104
+
105
+ self.stdout.write(
106
+ self.style.WARNING(f'⚠️ This will clear Constance {action_text}')
107
+ )
108
+ self.stdout.write('This action cannot be undone!')
109
+
110
+ response = input('Are you sure? [y/N]: ').lower().strip()
111
+ return response in ['y', 'yes']
112
+
113
+ def _clear_constance(self, clear_cache, clear_db):
114
+ """Clear Constance cache and/or database records."""
115
+ cleared_items = []
116
+
117
+ # Clear cache
118
+ if clear_cache:
119
+ try:
120
+ self.stdout.write('🧹 Clearing Django cache...')
121
+ cache.clear()
122
+ self.stdout.write(self.style.SUCCESS('✅ Django cache cleared'))
123
+ cleared_items.append('cache')
124
+
125
+ # Also try to clear specific constance cache keys
126
+ try:
127
+ from constance import config as constance_config
128
+ # Force reload of constance configuration
129
+ if hasattr(constance_config, '_backend'):
130
+ constance_config._backend = None
131
+ self.stdout.write(self.style.SUCCESS('✅ Constance cache backend reset'))
132
+ except Exception as e:
133
+ self.stdout.write(self.style.WARNING(f'⚠️ Could not reset Constance backend: {e}'))
134
+
135
+ except Exception as e:
136
+ self.stdout.write(self.style.ERROR(f'❌ Failed to clear cache: {e}'))
137
+
138
+ # Clear database records
139
+ if clear_db:
140
+ try:
141
+ self.stdout.write('🗑️ Clearing Constance database records...')
142
+
143
+ from django.apps import apps
144
+ Constance = apps.get_model('constance', 'Constance')
145
+
146
+ # Count records before deletion
147
+ count_before = Constance.objects.count()
148
+ self.stdout.write(f' Found {count_before} Constance records')
149
+
150
+ if count_before > 0:
151
+ # Delete all records
152
+ deleted_info = Constance.objects.all().delete()
153
+ deleted_count = deleted_info[0] if deleted_info else 0
154
+
155
+ self.stdout.write(self.style.SUCCESS(f'✅ Deleted {deleted_count} Constance records'))
156
+ cleared_items.append(f'{deleted_count} database records')
157
+ else:
158
+ self.stdout.write('ℹ️ No Constance records found to delete')
159
+
160
+ except Exception as e:
161
+ self.stdout.write(self.style.ERROR(f'❌ Failed to clear database records: {e}'))
162
+
163
+ # Show summary
164
+ if cleared_items:
165
+ self.stdout.write(
166
+ self.style.SUCCESS(f'\n🎉 Successfully cleared: {", ".join(cleared_items)}')
167
+ )
168
+ self.stdout.write('\n💡 Next steps:')
169
+ self.stdout.write(' 1. Restart your Django server')
170
+ self.stdout.write(' 2. Visit Django admin to reconfigure settings')
171
+ self.stdout.write(' 3. Check that boolean fields now work correctly')
172
+ else:
173
+ self.stdout.write(self.style.WARNING('⚠️ Nothing was cleared'))
174
+
175
+ def _show_current_status(self):
176
+ """Show current Constance status."""
177
+ self.stdout.write(self.style.SUCCESS('\n📊 Current Constance Status:'))
178
+
179
+ # Check database records
180
+ try:
181
+ from django.apps import apps
182
+ Constance = apps.get_model('constance', 'Constance')
183
+ db_count = Constance.objects.count()
184
+ self.stdout.write(f' Database records: {db_count}')
185
+
186
+ if db_count > 0:
187
+ self.stdout.write(' Recent records:')
188
+ for record in Constance.objects.all()[:5]:
189
+ self.stdout.write(f' - {record.key}: {record.value}')
190
+ if db_count > 5:
191
+ self.stdout.write(f' ... and {db_count - 5} more')
192
+ except Exception as e:
193
+ self.stdout.write(f' Database: Error - {e}')
194
+
195
+ # Check cache
196
+ try:
197
+ from django.core.cache import cache
198
+ # Try to get a test key to see if cache is working
199
+ test_key = 'constance_test_key'
200
+ cache.set(test_key, 'test_value', 1)
201
+ test_result = cache.get(test_key)
202
+
203
+ if test_result:
204
+ self.stdout.write(' Cache: Working')
205
+ else:
206
+ self.stdout.write(' Cache: Not working or empty')
207
+ except Exception as e:
208
+ self.stdout.write(f' Cache: Error - {e}')
@@ -0,0 +1,328 @@
1
+ """
2
+ Create Token Command for Django Config Toolkit
3
+ Generate API tokens and authentication tokens.
4
+ """
5
+
6
+ import os
7
+ import secrets
8
+ import string
9
+ from pathlib import Path
10
+ from django.core.management.base import BaseCommand
11
+ from django.contrib.auth import get_user_model
12
+ from django.conf import settings
13
+ import questionary
14
+ from datetime import datetime, timedelta
15
+ from django_cfg.modules.django_logging import get_logger
16
+
17
+ User = get_user_model()
18
+
19
+
20
+ logger = get_logger('create_token')
21
+
22
+ class Command(BaseCommand):
23
+ # Web execution metadata
24
+ web_executable = False
25
+ requires_input = True
26
+ is_destructive = False
27
+
28
+ help = 'Create API tokens and authentication tokens'
29
+
30
+ def add_arguments(self, parser):
31
+ parser.add_argument(
32
+ '--user',
33
+ type=str,
34
+ help='Username to create token for'
35
+ )
36
+ parser.add_argument(
37
+ '--type',
38
+ type=str,
39
+ choices=['api', 'auth', 'secret'],
40
+ help='Type of token to create'
41
+ )
42
+ parser.add_argument(
43
+ '--length',
44
+ type=int,
45
+ default=32,
46
+ help='Token length (default: 32)'
47
+ )
48
+ parser.add_argument(
49
+ '--expires',
50
+ type=int,
51
+ help='Token expiration in days'
52
+ )
53
+
54
+ def handle(self, *args, **options):
55
+ logger.info("Starting create_token command")
56
+ if options['user'] and options['type']:
57
+ self.create_token_for_user(
58
+ username=options['user'],
59
+ token_type=options['type'],
60
+ length=options['length'],
61
+ expires_days=options['expires']
62
+ )
63
+ else:
64
+ self.show_interactive_menu()
65
+
66
+ def show_interactive_menu(self):
67
+ """Show interactive menu with token creation options"""
68
+ self.stdout.write(self.style.SUCCESS('\n🔑 Token Creation Tool - Django Config Toolkit\n'))
69
+
70
+ choices = [
71
+ questionary.Choice('🔑 Create API Token', value='api'),
72
+ questionary.Choice('🔐 Create Auth Token', value='auth'),
73
+ questionary.Choice('🔒 Create Secret Key', value='secret'),
74
+ questionary.Choice('👤 Create Token for User', value='user'),
75
+ questionary.Choice('📝 Generate Django Secret Key', value='django_secret'),
76
+ questionary.Choice('❌ Exit', value='exit')
77
+ ]
78
+
79
+ choice = questionary.select(
80
+ 'Select token type:',
81
+ choices=choices
82
+ ).ask()
83
+
84
+ if choice == 'api':
85
+ self.create_api_token()
86
+ elif choice == 'auth':
87
+ self.create_auth_token()
88
+ elif choice == 'secret':
89
+ self.create_secret_key()
90
+ elif choice == 'user':
91
+ self.create_token_for_user_interactive()
92
+ elif choice == 'django_secret':
93
+ self.generate_django_secret_key()
94
+ elif choice == 'exit':
95
+ self.stdout.write('Goodbye! 👋')
96
+ return
97
+
98
+ def create_api_token(self):
99
+ """Create API token"""
100
+ self.stdout.write(self.style.SUCCESS('🔑 Creating API Token...'))
101
+
102
+ # Get token details
103
+ token_name = questionary.text('Token name:').ask()
104
+ if not token_name:
105
+ self.stdout.write(self.style.ERROR('❌ Token name is required'))
106
+ return
107
+
108
+ token_length = questionary.select(
109
+ 'Token length:',
110
+ choices=['32', '64', '128', '256']
111
+ ).ask()
112
+
113
+ expires = questionary.select(
114
+ 'Token expiration:',
115
+ choices=['Never', '30 days', '90 days', '1 year']
116
+ ).ask()
117
+
118
+ # Generate token
119
+ token = self.generate_token(int(token_length))
120
+
121
+ # Calculate expiration
122
+ expiration_date = None
123
+ if expires != 'Never':
124
+ days_map = {
125
+ '30 days': 30,
126
+ '90 days': 90,
127
+ '1 year': 365
128
+ }
129
+ expiration_date = datetime.now() + timedelta(days=days_map[expires])
130
+
131
+ # Save token (in a real app, you'd save to database)
132
+ self.save_token_to_file('api_token', token, token_name, expiration_date)
133
+
134
+ self.stdout.write(self.style.SUCCESS(f'✅ API Token created: {token}'))
135
+ self.stdout.write(f'📝 Name: {token_name}')
136
+ if expiration_date:
137
+ self.stdout.write(f'⏰ Expires: {expiration_date.strftime("%Y-%m-%d %H:%M:%S")}')
138
+
139
+ def create_auth_token(self):
140
+ """Create authentication token"""
141
+ self.stdout.write(self.style.SUCCESS('🔐 Creating Auth Token...'))
142
+
143
+ # Get token details
144
+ token_name = questionary.text('Token name:').ask()
145
+ if not token_name:
146
+ self.stdout.write(self.style.ERROR('❌ Token name is required'))
147
+ return
148
+
149
+ token_length = questionary.select(
150
+ 'Token length:',
151
+ choices=['32', '64', '128']
152
+ ).ask()
153
+
154
+ # Generate token
155
+ token = self.generate_token(int(token_length))
156
+
157
+ # Save token
158
+ self.save_token_to_file('auth_token', token, token_name)
159
+
160
+ self.stdout.write(self.style.SUCCESS(f'✅ Auth Token created: {token}'))
161
+ self.stdout.write(f'📝 Name: {token_name}')
162
+
163
+ def create_secret_key(self):
164
+ """Create secret key"""
165
+ self.stdout.write(self.style.SUCCESS('🔒 Creating Secret Key...'))
166
+
167
+ # Get key details
168
+ key_name = questionary.text('Secret key name:').ask()
169
+ if not key_name:
170
+ self.stdout.write(self.style.ERROR('❌ Key name is required'))
171
+ return
172
+
173
+ key_length = questionary.select(
174
+ 'Key length:',
175
+ choices=['32', '64', '128', '256']
176
+ ).ask()
177
+
178
+ # Generate secret key
179
+ secret_key = self.generate_secret_key(int(key_length))
180
+
181
+ # Save key
182
+ self.save_token_to_file('secret_key', secret_key, key_name)
183
+
184
+ self.stdout.write(self.style.SUCCESS(f'✅ Secret Key created: {secret_key}'))
185
+ self.stdout.write(f'📝 Name: {key_name}')
186
+
187
+ def create_token_for_user_interactive(self):
188
+ """Create token for user interactively"""
189
+ self.stdout.write(self.style.SUCCESS('👤 Creating Token for User...'))
190
+
191
+ # Get user
192
+ username = questionary.text('Username:').ask()
193
+ if not username:
194
+ self.stdout.write(self.style.ERROR('❌ Username is required'))
195
+ return
196
+
197
+ # Check if user exists
198
+ try:
199
+ user = User.objects.get(username=username)
200
+ except User.DoesNotExist:
201
+ self.stdout.write(self.style.ERROR(f'❌ User {username} does not exist'))
202
+ return
203
+
204
+ # Get token type
205
+ token_type = questionary.select(
206
+ 'Token type:',
207
+ choices=['API Token', 'Auth Token', 'Secret Key']
208
+ ).ask()
209
+
210
+ # Get token length
211
+ token_length = questionary.select(
212
+ 'Token length:',
213
+ choices=['32', '64', '128']
214
+ ).ask()
215
+
216
+ # Get expiration
217
+ expires = questionary.select(
218
+ 'Token expiration:',
219
+ choices=['Never', '30 days', '90 days', '1 year']
220
+ ).ask()
221
+
222
+ # Create token
223
+ self.create_token_for_user(
224
+ username=username,
225
+ token_type=token_type.lower().replace(' ', '_'),
226
+ length=int(token_length),
227
+ expires_days=None if expires == 'Never' else {
228
+ '30 days': 30,
229
+ '90 days': 90,
230
+ '1 year': 365
231
+ }[expires]
232
+ )
233
+
234
+ def create_token_for_user(self, username, token_type, length=32, expires_days=None):
235
+ """Create token for specific user"""
236
+ try:
237
+ user = User.objects.get(username=username)
238
+ except User.DoesNotExist:
239
+ self.stdout.write(self.style.ERROR(f'❌ User {username} does not exist'))
240
+ return
241
+
242
+ # Generate token
243
+ if token_type == 'secret_key':
244
+ token = self.generate_secret_key(length)
245
+ else:
246
+ token = self.generate_token(length)
247
+
248
+ # Calculate expiration
249
+ expiration_date = None
250
+ if expires_days:
251
+ expiration_date = datetime.now() + timedelta(days=expires_days)
252
+
253
+ # Save token
254
+ token_name = f"{username}_{token_type}_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
255
+ self.save_token_to_file(token_type, token, token_name, expiration_date, user)
256
+
257
+ self.stdout.write(self.style.SUCCESS(f'✅ {token_type.title()} created for {username}'))
258
+ self.stdout.write(f'🔑 Token: {token}')
259
+ self.stdout.write(f'📝 Name: {token_name}')
260
+ if expiration_date:
261
+ self.stdout.write(f'⏰ Expires: {expiration_date.strftime("%Y-%m-%d %H:%M:%S")}')
262
+
263
+ def generate_django_secret_key(self):
264
+ """Generate Django secret key"""
265
+ self.stdout.write(self.style.SUCCESS('🔐 Generating Django Secret Key...'))
266
+
267
+ # Generate Django-compatible secret key
268
+ secret_key = self.generate_django_secret()
269
+
270
+ # Save to file
271
+ self.save_token_to_file('django_secret', secret_key, 'django_secret_key')
272
+
273
+ self.stdout.write(self.style.SUCCESS(f'✅ Django Secret Key generated: {secret_key}'))
274
+ self.stdout.write('💡 Add this to your .env file as SECRET_KEY=...')
275
+
276
+ def generate_token(self, length=32):
277
+ """Generate random token"""
278
+ alphabet = string.ascii_letters + string.digits
279
+ return ''.join(secrets.choice(alphabet) for _ in range(length))
280
+
281
+ def generate_secret_key(self, length=64):
282
+ """Generate secret key"""
283
+ alphabet = string.ascii_letters + string.digits + string.punctuation
284
+ # Remove characters that might cause issues in config files
285
+ alphabet = alphabet.replace('"', '').replace("'", '').replace('\\', '')
286
+ return ''.join(secrets.choice(alphabet) for _ in range(length))
287
+
288
+ def generate_django_secret(self):
289
+ """Generate Django-compatible secret key"""
290
+ return ''.join(secrets.choice(string.ascii_letters + string.digits + string.punctuation) for _ in range(50))
291
+
292
+ def save_token_to_file(self, token_type, token, name, expiration_date=None, user=None):
293
+ """Save token to file"""
294
+ # Create tokens directory
295
+ tokens_dir = Path('tokens')
296
+ tokens_dir.mkdir(exist_ok=True)
297
+
298
+ # Create token file
299
+ timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
300
+ filename = f"{token_type}_{name}_{timestamp}.txt"
301
+ filepath = tokens_dir / filename
302
+
303
+ with open(filepath, 'w') as f:
304
+ f.write(f"Token Type: {token_type}\n")
305
+ f.write(f"Name: {name}\n")
306
+ f.write(f"Token: {token}\n")
307
+ f.write(f"Created: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
308
+ if expiration_date:
309
+ f.write(f"Expires: {expiration_date.strftime('%Y-%m-%d %H:%M:%S')}\n")
310
+ if user:
311
+ f.write(f"User: {user.username} ({user.email})\n")
312
+ f.write(f"\n# Add to your configuration:\n")
313
+ f.write(f"# {token_type.upper()}_KEY={token}\n")
314
+
315
+ self.stdout.write(f'💾 Token saved to: {filepath}')
316
+
317
+ # Also save to .env format
318
+ env_filename = f"{token_type}_{name}_{timestamp}.env"
319
+ env_filepath = tokens_dir / env_filename
320
+
321
+ with open(env_filepath, 'w') as f:
322
+ f.write(f"# {token_type.title()} - {name}\n")
323
+ f.write(f"# Created: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
324
+ if expiration_date:
325
+ f.write(f"# Expires: {expiration_date.strftime('%Y-%m-%d %H:%M:%S')}\n")
326
+ f.write(f"{token_type.upper()}_KEY={token}\n")
327
+
328
+ self.stdout.write(f'💾 Environment file saved to: {env_filepath}')