createsonline 0.1.26__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 (152) hide show
  1. createsonline/__init__.py +46 -0
  2. createsonline/admin/__init__.py +7 -0
  3. createsonline/admin/content.py +526 -0
  4. createsonline/admin/crud.py +805 -0
  5. createsonline/admin/field_builder.py +559 -0
  6. createsonline/admin/integration.py +482 -0
  7. createsonline/admin/interface.py +2562 -0
  8. createsonline/admin/model_creator.py +513 -0
  9. createsonline/admin/model_manager.py +388 -0
  10. createsonline/admin/modern_dashboard.py +498 -0
  11. createsonline/admin/permissions.py +264 -0
  12. createsonline/admin/user_forms.py +594 -0
  13. createsonline/ai/__init__.py +202 -0
  14. createsonline/ai/fields.py +1226 -0
  15. createsonline/ai/orm.py +325 -0
  16. createsonline/ai/services.py +1244 -0
  17. createsonline/app.py +506 -0
  18. createsonline/auth/__init__.py +8 -0
  19. createsonline/auth/management.py +228 -0
  20. createsonline/auth/models.py +552 -0
  21. createsonline/cli/__init__.py +5 -0
  22. createsonline/cli/commands/__init__.py +122 -0
  23. createsonline/cli/commands/database.py +416 -0
  24. createsonline/cli/commands/info.py +173 -0
  25. createsonline/cli/commands/initdb.py +218 -0
  26. createsonline/cli/commands/project.py +545 -0
  27. createsonline/cli/commands/serve.py +173 -0
  28. createsonline/cli/commands/shell.py +93 -0
  29. createsonline/cli/commands/users.py +148 -0
  30. createsonline/cli/main.py +2041 -0
  31. createsonline/cli/manage.py +274 -0
  32. createsonline/config/__init__.py +9 -0
  33. createsonline/config/app.py +2577 -0
  34. createsonline/config/database.py +179 -0
  35. createsonline/config/docs.py +384 -0
  36. createsonline/config/errors.py +160 -0
  37. createsonline/config/orm.py +43 -0
  38. createsonline/config/request.py +93 -0
  39. createsonline/config/settings.py +176 -0
  40. createsonline/data/__init__.py +23 -0
  41. createsonline/data/dataframe.py +925 -0
  42. createsonline/data/io.py +453 -0
  43. createsonline/data/series.py +557 -0
  44. createsonline/database/__init__.py +60 -0
  45. createsonline/database/abstraction.py +440 -0
  46. createsonline/database/assistant.py +585 -0
  47. createsonline/database/fields.py +442 -0
  48. createsonline/database/migrations.py +132 -0
  49. createsonline/database/models.py +604 -0
  50. createsonline/database.py +438 -0
  51. createsonline/http/__init__.py +28 -0
  52. createsonline/http/client.py +535 -0
  53. createsonline/ml/__init__.py +55 -0
  54. createsonline/ml/classification.py +552 -0
  55. createsonline/ml/clustering.py +680 -0
  56. createsonline/ml/metrics.py +542 -0
  57. createsonline/ml/neural.py +560 -0
  58. createsonline/ml/preprocessing.py +784 -0
  59. createsonline/ml/regression.py +501 -0
  60. createsonline/performance/__init__.py +19 -0
  61. createsonline/performance/cache.py +444 -0
  62. createsonline/performance/compression.py +335 -0
  63. createsonline/performance/core.py +419 -0
  64. createsonline/project_init.py +789 -0
  65. createsonline/routing.py +528 -0
  66. createsonline/security/__init__.py +34 -0
  67. createsonline/security/core.py +811 -0
  68. createsonline/security/encryption.py +349 -0
  69. createsonline/server.py +295 -0
  70. createsonline/static/css/admin.css +263 -0
  71. createsonline/static/css/common.css +358 -0
  72. createsonline/static/css/dashboard.css +89 -0
  73. createsonline/static/favicon.ico +0 -0
  74. createsonline/static/icons/icon-128x128.png +0 -0
  75. createsonline/static/icons/icon-128x128.webp +0 -0
  76. createsonline/static/icons/icon-16x16.png +0 -0
  77. createsonline/static/icons/icon-16x16.webp +0 -0
  78. createsonline/static/icons/icon-180x180.png +0 -0
  79. createsonline/static/icons/icon-180x180.webp +0 -0
  80. createsonline/static/icons/icon-192x192.png +0 -0
  81. createsonline/static/icons/icon-192x192.webp +0 -0
  82. createsonline/static/icons/icon-256x256.png +0 -0
  83. createsonline/static/icons/icon-256x256.webp +0 -0
  84. createsonline/static/icons/icon-32x32.png +0 -0
  85. createsonline/static/icons/icon-32x32.webp +0 -0
  86. createsonline/static/icons/icon-384x384.png +0 -0
  87. createsonline/static/icons/icon-384x384.webp +0 -0
  88. createsonline/static/icons/icon-48x48.png +0 -0
  89. createsonline/static/icons/icon-48x48.webp +0 -0
  90. createsonline/static/icons/icon-512x512.png +0 -0
  91. createsonline/static/icons/icon-512x512.webp +0 -0
  92. createsonline/static/icons/icon-64x64.png +0 -0
  93. createsonline/static/icons/icon-64x64.webp +0 -0
  94. createsonline/static/image/android-chrome-192x192.png +0 -0
  95. createsonline/static/image/android-chrome-512x512.png +0 -0
  96. createsonline/static/image/apple-touch-icon.png +0 -0
  97. createsonline/static/image/favicon-16x16.png +0 -0
  98. createsonline/static/image/favicon-32x32.png +0 -0
  99. createsonline/static/image/favicon.ico +0 -0
  100. createsonline/static/image/favicon.svg +17 -0
  101. createsonline/static/image/icon-128x128.png +0 -0
  102. createsonline/static/image/icon-128x128.webp +0 -0
  103. createsonline/static/image/icon-16x16.png +0 -0
  104. createsonline/static/image/icon-16x16.webp +0 -0
  105. createsonline/static/image/icon-180x180.png +0 -0
  106. createsonline/static/image/icon-180x180.webp +0 -0
  107. createsonline/static/image/icon-192x192.png +0 -0
  108. createsonline/static/image/icon-192x192.webp +0 -0
  109. createsonline/static/image/icon-256x256.png +0 -0
  110. createsonline/static/image/icon-256x256.webp +0 -0
  111. createsonline/static/image/icon-32x32.png +0 -0
  112. createsonline/static/image/icon-32x32.webp +0 -0
  113. createsonline/static/image/icon-384x384.png +0 -0
  114. createsonline/static/image/icon-384x384.webp +0 -0
  115. createsonline/static/image/icon-48x48.png +0 -0
  116. createsonline/static/image/icon-48x48.webp +0 -0
  117. createsonline/static/image/icon-512x512.png +0 -0
  118. createsonline/static/image/icon-512x512.webp +0 -0
  119. createsonline/static/image/icon-64x64.png +0 -0
  120. createsonline/static/image/icon-64x64.webp +0 -0
  121. createsonline/static/image/logo-header-h100.png +0 -0
  122. createsonline/static/image/logo-header-h100.webp +0 -0
  123. createsonline/static/image/logo-header-h200@2x.png +0 -0
  124. createsonline/static/image/logo-header-h200@2x.webp +0 -0
  125. createsonline/static/image/logo.png +0 -0
  126. createsonline/static/js/admin.js +274 -0
  127. createsonline/static/site.webmanifest +35 -0
  128. createsonline/static/templates/admin/base.html +87 -0
  129. createsonline/static/templates/admin/dashboard.html +217 -0
  130. createsonline/static/templates/admin/model_form.html +270 -0
  131. createsonline/static/templates/admin/model_list.html +202 -0
  132. createsonline/static/test_script.js +15 -0
  133. createsonline/static/test_styles.css +59 -0
  134. createsonline/static_files.py +365 -0
  135. createsonline/templates/404.html +100 -0
  136. createsonline/templates/admin_login.html +169 -0
  137. createsonline/templates/base.html +102 -0
  138. createsonline/templates/index.html +151 -0
  139. createsonline/templates.py +205 -0
  140. createsonline/testing.py +322 -0
  141. createsonline/utils.py +448 -0
  142. createsonline/validation/__init__.py +49 -0
  143. createsonline/validation/fields.py +598 -0
  144. createsonline/validation/models.py +504 -0
  145. createsonline/validation/validators.py +561 -0
  146. createsonline/views.py +184 -0
  147. createsonline-0.1.26.dist-info/METADATA +46 -0
  148. createsonline-0.1.26.dist-info/RECORD +152 -0
  149. createsonline-0.1.26.dist-info/WHEEL +5 -0
  150. createsonline-0.1.26.dist-info/entry_points.txt +2 -0
  151. createsonline-0.1.26.dist-info/licenses/LICENSE +21 -0
  152. createsonline-0.1.26.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2562 @@
1
+ # createsonline/admin/interface.py
2
+ """
3
+ CREATESONLINE Admin Interface - COMPLETE INTERNAL IMPLEMENTATION
4
+
5
+ Main admin interface implementation
6
+ and AI-enhanced features.
7
+ """
8
+ from typing import Dict, Any, Union
9
+ from datetime import datetime
10
+ import os
11
+
12
+ # Pure CREATESONLINE internal response classes
13
+ class InternalResponse:
14
+ def __init__(self, content=b"", status_code=200, headers=None, **kwargs):
15
+ if isinstance(content, str):
16
+ content = content.encode('utf-8')
17
+ self.content = content
18
+ self.body = content # For compatibility
19
+ self.status_code = status_code
20
+ self.headers = headers or {}
21
+
22
+ class InternalHTMLResponse(InternalResponse):
23
+ def __init__(self, content="", status_code=200, headers=None, **kwargs):
24
+ if headers is None:
25
+ headers = {}
26
+ headers['content-type'] = 'text/html; charset=utf-8'
27
+ super().__init__(content, status_code, headers, **kwargs)
28
+
29
+ class InternalJSONResponse(InternalResponse):
30
+ def __init__(self, data, status_code=200, headers=None, **kwargs):
31
+ if headers is None:
32
+ headers = {}
33
+ headers['content-type'] = 'application/json'
34
+ import json
35
+ content = json.dumps(data, indent=2)
36
+ super().__init__(content, status_code, headers, **kwargs)
37
+
38
+ class InternalRequest:
39
+ def __init__(self):
40
+ self.method = "GET"
41
+ self.url = "/"
42
+ self.path_params = {}
43
+ self.query_params = {}
44
+ self.headers = {}
45
+
46
+ async def json(self):
47
+ return {}
48
+
49
+ async def body(self):
50
+ return b''
51
+
52
+ # Use internal classes
53
+ Request = InternalRequest
54
+ HTMLResponse = InternalHTMLResponse
55
+ JSONResponse = InternalJSONResponse
56
+ Response = InternalResponse
57
+ Route = lambda path, endpoint, methods=None: {"path": path, "endpoint": endpoint, "methods": methods}
58
+
59
+ # ========================================
60
+ # INTERNAL TEMPLATE ENGINE
61
+ # ========================================
62
+
63
+ class CreatesonlineTemplateEngine:
64
+ """Pure Python template engine - no Jinja2 needed"""
65
+
66
+ def __init__(self):
67
+ self.templates = {}
68
+ self.template_dirs = []
69
+ self.globals = {
70
+ "datetime": datetime,
71
+ "len": len,
72
+ "str": str,
73
+ "int": int,
74
+ "float": float,
75
+ "bool": bool,
76
+ "list": list,
77
+ "dict": dict,
78
+ }
79
+
80
+ def add_template_dir(self, directory: str):
81
+ """Add template directory"""
82
+ self.template_dirs.append(directory)
83
+
84
+ def render_string(self, template_string: str, context: Dict[str, Any] = None) -> str:
85
+ """Render template string with context"""
86
+ context = context or {}
87
+ merged_context = {**self.globals, **context}
88
+
89
+ # Simple template substitution
90
+ result = template_string
91
+
92
+ # Handle {{ variable }} substitutions
93
+ import re
94
+ pattern = r'\{\{\s*([^}]+)\s*\}\}'
95
+
96
+ def replace_var(match):
97
+ var_expr = match.group(1).strip()
98
+ try:
99
+ # Simple variable lookup
100
+ if '.' in var_expr:
101
+ # Handle object.attribute
102
+ parts = var_expr.split('.')
103
+ value = merged_context
104
+ for part in parts:
105
+ if hasattr(value, part):
106
+ value = getattr(value, part)
107
+ elif isinstance(value, dict) and part in value:
108
+ value = value[part]
109
+ else:
110
+ return f"{{{{ {var_expr} }}}}" # Return original if not found
111
+ else:
112
+ # Simple variable
113
+ value = merged_context.get(var_expr, f"{{{{ {var_expr} }}}}")
114
+
115
+ return str(value) if value is not None else ""
116
+ except:
117
+ return f"{{{{ {var_expr} }}}}"
118
+
119
+ result = re.sub(pattern, replace_var, result)
120
+
121
+ # Handle {% if %} blocks (simple implementation)
122
+ if_pattern = r'\{\%\s*if\s+([^%]+)\s*\%\}(.*?)\{\%\s*endif\s*\%\}'
123
+
124
+ def replace_if(match):
125
+ condition = match.group(1).strip()
126
+ content = match.group(2)
127
+
128
+ try:
129
+ # Simple condition evaluation
130
+ if condition in merged_context:
131
+ if merged_context[condition]:
132
+ return content
133
+ elif condition.startswith('not '):
134
+ var = condition[4:].strip()
135
+ if var in merged_context and not merged_context[var]:
136
+ return content
137
+
138
+ return ""
139
+ except:
140
+ return content
141
+
142
+ result = re.sub(if_pattern, replace_if, result, flags=re.DOTALL)
143
+
144
+ # Handle {% for %} loops (simple implementation)
145
+ for_pattern = r'\{\%\s*for\s+(\w+)\s+in\s+(\w+)\s*\%\}(.*?)\{\%\s*endfor\s*\%\}'
146
+
147
+ def replace_for(match):
148
+ var_name = match.group(1)
149
+ list_name = match.group(2)
150
+ content = match.group(3)
151
+
152
+ try:
153
+ if list_name in merged_context:
154
+ items = merged_context[list_name]
155
+ if isinstance(items, (list, tuple)):
156
+ result_parts = []
157
+ for item in items:
158
+ item_context = {**merged_context, var_name: item}
159
+ item_result = self.render_string(content, item_context)
160
+ result_parts.append(item_result)
161
+ return "".join(result_parts)
162
+
163
+ return ""
164
+ except:
165
+ return content
166
+
167
+ result = re.sub(for_pattern, replace_for, result, flags=re.DOTALL)
168
+
169
+ return result
170
+
171
+ def render_template(self, template_name: str, context: Dict[str, Any] = None) -> str:
172
+ """Render template file with context"""
173
+ # Try to load template from cache
174
+ if template_name in self.templates:
175
+ template_string = self.templates[template_name]
176
+ else:
177
+ # Load template from file
178
+ template_string = self._load_template(template_name)
179
+ self.templates[template_name] = template_string
180
+
181
+ return self.render_string(template_string, context)
182
+
183
+ def _load_template(self, template_name: str) -> str:
184
+ """Load template from file"""
185
+ for template_dir in self.template_dirs:
186
+ template_path = os.path.join(template_dir, template_name)
187
+ if os.path.exists(template_path):
188
+ with open(template_path, 'r', encoding='utf-8') as f:
189
+ return f.read()
190
+
191
+ # Return default template if not found
192
+ return self._get_default_template(template_name)
193
+
194
+ def _get_default_template(self, template_name: str) -> str:
195
+ """Get default template for admin interface"""
196
+ if template_name == "admin/base.html":
197
+ return """<!DOCTYPE html>
198
+ <html lang="en">
199
+ <head>
200
+ <meta charset="UTF-8">
201
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
202
+ <title>{{ title }} - CREATESONLINE Admin</title>
203
+ <style>
204
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; background: #f8f9fa; }
205
+ .header { background: #000; color: white; padding: 1rem 2rem; display: flex; justify-content: space-between; align-items: center; }
206
+ .header h1 { margin: 0; font-size: 1.5rem; }
207
+ .nav { background: white; border-bottom: 1px solid #ddd; padding: 0 2rem; }
208
+ .nav ul { list-style: none; margin: 0; padding: 0; display: flex; }
209
+ .nav li { margin-right: 2rem; }
210
+ .nav a { text-decoration: none; color: #333; padding: 1rem 0; display: block; border-bottom: 2px solid transparent; }
211
+ .nav a:hover, .nav a.active { color: #000; border-color: #000; }
212
+ .container { max-width: 1200px; margin: 2rem auto; padding: 0 2rem; }
213
+ .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); margin-bottom: 2rem; }
214
+ .card-header { padding: 1.5rem; border-bottom: 1px solid #eee; }
215
+ .card-body { padding: 1.5rem; }
216
+ .btn { padding: 0.5rem 1rem; border: none; border-radius: 4px; cursor: pointer; text-decoration: none; display: inline-block; }
217
+ .btn-primary { background: #000; color: white; }
218
+ .btn-primary:hover { background: #333; }
219
+ .table { width: 100%; border-collapse: collapse; }
220
+ .table th, .table td { padding: 0.75rem; text-align: left; border-bottom: 1px solid #ddd; }
221
+ .table th { font-weight: 600; background: #f8f9fa; }
222
+ .status-badge { padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem; font-weight: 500; }
223
+ .status-active { background: #d4edda; color: #155724; }
224
+ .status-inactive { background: #f8d7da; color: #721c24; }
225
+ </style>
226
+ </head>
227
+ <body>
228
+ <header class="header">
229
+ <h1><img src="/static/image/favicon-32x32.png" alt="CREATESONLINE" style="width: 32px; height: 32px; vertical-align: middle; margin-right: 10px;">CREATESONLINE Admin</h1>
230
+ <div>
231
+ <span>{{ user.username|default:"Admin" }}</span>
232
+ <a href="/admin/logout" style="color: white; margin-left: 1rem;">Logout</a>
233
+ </div>
234
+ </header>
235
+
236
+ <nav class="nav">
237
+ <ul>
238
+ <li><a href="/admin" class="{% if request.path == '/admin' %}active{% endif %}">Dashboard</a></li>
239
+ <li><a href="/admin/users">Users</a></li>
240
+ <li><a href="/admin/ai-models">AI Models</a></li>
241
+ <li><a href="/admin/settings">Settings</a></li>
242
+ </ul>
243
+ </nav>
244
+
245
+ <div class="container">
246
+ {{ content }}
247
+ </div>
248
+ </body>
249
+ </html>"""
250
+
251
+ elif template_name == "admin/dashboard.html":
252
+ return """<div class="card">
253
+ <div class="card-header">
254
+ <h2>📊 Dashboard Overview</h2>
255
+ </div>
256
+ <div class="card-body">
257
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1rem; margin-bottom: 2rem;">
258
+ {% for metric in metrics %}
259
+ <div class="card">
260
+ <div class="card-body" style="text-align: center;">
261
+ <h3 style="margin: 0 0 0.5rem 0; color: #666; font-size: 0.9rem;">{{ metric.title }}</h3>
262
+ <div style="font-size: 2rem; font-weight: bold; color: #000;">{{ metric.value }}</div>
263
+ <div style="color: #28a745; font-size: 0.8rem;">{{ metric.change }}</div>
264
+ </div>
265
+ </div>
266
+ {% endfor %}
267
+ </div>
268
+
269
+ <div class="card">
270
+ <div class="card-header">
271
+ <h3>🔥 Recent Activity</h3>
272
+ </div>
273
+ <div class="card-body">
274
+ {% if activities %}
275
+ <ul style="list-style: none; padding: 0; margin: 0;">
276
+ {% for activity in activities %}
277
+ <li style="padding: 0.75rem 0; border-bottom: 1px solid #eee;">
278
+ <strong>{{ activity.title }}</strong><br>
279
+ <span style="color: #666; font-size: 0.9rem;">{{ activity.description }}</span>
280
+ <span style="float: right; color: #999; font-size: 0.8rem;">{{ activity.time }}</span>
281
+ </li>
282
+ {% endfor %}
283
+ </ul>
284
+ {% else %}
285
+ <p style="color: #666; text-align: center; margin: 2rem 0;">No recent activity</p>
286
+ {% endif %}
287
+ </div>
288
+ </div>
289
+ </div>
290
+ </div>"""
291
+
292
+ elif template_name == "admin/model_list.html":
293
+ return """<div class="card">
294
+ <div class="card-header">
295
+ <h2>{{ model_name }} Management</h2>
296
+ <a href="/admin/{{ app_label }}/{{ model_name }}/add" class="btn btn-primary">Add {{ model_name }}</a>
297
+ </div>
298
+ <div class="card-body">
299
+ {% if objects %}
300
+ <table class="table">
301
+ <thead>
302
+ <tr>
303
+ {% for field in list_display %}
304
+ <th>{{ field }}</th>
305
+ {% endfor %}
306
+ <th>Actions</th>
307
+ </tr>
308
+ </thead>
309
+ <tbody>
310
+ {% for obj in objects %}
311
+ <tr>
312
+ {% for field in list_display %}
313
+ <td>{{ obj[field] }}</td>
314
+ {% endfor %}
315
+ <td>
316
+ <a href="/admin/{{ app_label }}/{{ model_name }}/{{ obj.id }}">Edit</a>
317
+ <a href="/admin/{{ app_label }}/{{ model_name }}/{{ obj.id }}/delete" style="margin-left: 0.5rem; color: #dc3545;">Delete</a>
318
+ </td>
319
+ </tr>
320
+ {% endfor %}
321
+ </tbody>
322
+ </table>
323
+ {% else %}
324
+ <p style="text-align: center; color: #666; margin: 2rem 0;">No {{ model_name }} found.</p>
325
+ {% endif %}
326
+ </div>
327
+ </div>"""
328
+
329
+ else:
330
+ return f"<div>Template {template_name} not found</div>"
331
+
332
+ # Global template engine
333
+ _template_engine = CreatesonlineTemplateEngine()
334
+
335
+ def get_template_engine():
336
+ """Get the global template engine"""
337
+ return _template_engine
338
+
339
+ # ========================================
340
+ # ADMIN MODEL CLASSES
341
+ # ========================================
342
+
343
+ class ModelAdmin:
344
+ """
345
+ Base admin configuration for models.
346
+ Pure Python implementation with AI enhancements.
347
+ """
348
+
349
+ # Display configuration
350
+ list_display = ['id']
351
+ list_display_links = None
352
+ list_filter = []
353
+ list_select_related = []
354
+ list_per_page = 100
355
+ list_max_show_all = 200
356
+
357
+ # Search configuration
358
+ search_fields = []
359
+ search_help_text = None
360
+
361
+ # Ordering
362
+ ordering = None
363
+
364
+ # Form configuration
365
+ fields = None
366
+ exclude = None
367
+ fieldsets = None
368
+ readonly_fields = []
369
+
370
+ # Permissions
371
+ has_add_permission = True
372
+ has_change_permission = True
373
+ has_delete_permission = True
374
+ has_view_permission = True
375
+
376
+ # AI enhancements
377
+ ai_insights_enabled = True
378
+ smart_search_enabled = True
379
+ auto_suggestions_enabled = True
380
+ ai_field_recommendations = True
381
+
382
+ # Actions
383
+ actions = ['delete_selected']
384
+ actions_on_top = True
385
+ actions_on_bottom = False
386
+
387
+ def __init__(self, model, admin_site):
388
+ """Initialize model admin"""
389
+ self.model = model
390
+ self.admin_site = admin_site
391
+ self.opts = model if hasattr(model, '__tablename__') else type('MockOpts', (), {'verbose_name': model.__name__})()
392
+
393
+ # Setup display fields
394
+ if self.list_display_links is None and self.list_display:
395
+ self.list_display_links = [self.list_display[0]]
396
+
397
+ def get_list_display(self, request):
398
+ """Get list display fields for request"""
399
+ return self.list_display
400
+
401
+ def get_search_fields(self, request):
402
+ """Get search fields for request"""
403
+ return self.search_fields
404
+
405
+ def get_list_filter(self, request):
406
+ """Get list filter fields for request"""
407
+ return self.list_filter
408
+
409
+ def get_queryset(self, request):
410
+ """Get queryset for admin list view"""
411
+ # Mock implementation - would integrate with ORM
412
+ return []
413
+
414
+ def has_permission(self, request, permission_type):
415
+ """Check if user has permission for action"""
416
+ user = getattr(request, 'user', None)
417
+ if not user:
418
+ return True
419
+
420
+ # In production, implement proper permission checking
421
+ return True
422
+
423
+ def get_ai_insights(self, request, obj=None):
424
+ """Get AI insights for object or model"""
425
+ if not self.ai_insights_enabled:
426
+ return {}
427
+
428
+ insights = {
429
+ "model_health": "good",
430
+ "data_quality": 0.85,
431
+ "suggested_actions": [],
432
+ "anomalies": [],
433
+ "trends": {},
434
+ "ai_recommendations": []
435
+ }
436
+
437
+ if obj:
438
+ # Object-specific insights
439
+ insights["object_score"] = 0.9
440
+ insights["suggested_changes"] = []
441
+ insights["ai_predictions"] = {
442
+ "field_suggestions": {},
443
+ "quality_score": 0.87
444
+ }
445
+ else:
446
+ # Model-level insights
447
+ insights["total_records"] = 0
448
+ insights["recent_activity"] = []
449
+ insights["performance_metrics"] = {
450
+ "avg_response_time": "45ms",
451
+ "success_rate": "98.5%"
452
+ }
453
+
454
+ return insights
455
+
456
+ class UserAdmin(ModelAdmin):
457
+ """Admin configuration for User model with AI insights"""
458
+
459
+ list_display = ['username', 'email', 'first_name', 'last_name', 'is_staff', 'is_active', 'date_joined']
460
+ list_display_links = ['username']
461
+ list_filter = ['is_staff', 'is_superuser', 'is_active', 'groups']
462
+ search_fields = ['username', 'first_name', 'last_name', 'email']
463
+ ordering = ['username']
464
+
465
+ fieldsets = [
466
+ (None, {
467
+ 'fields': ['username', 'password']
468
+ }),
469
+ ('Personal info', {
470
+ 'fields': ['first_name', 'last_name', 'email']
471
+ }),
472
+ ('Permissions', {
473
+ 'fields': ['is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions']
474
+ }),
475
+ ('Important dates', {
476
+ 'fields': ['last_login', 'date_joined']
477
+ }),
478
+ ('AI Profile', {
479
+ 'fields': ['profile_picture', 'bio'],
480
+ 'classes': ['collapse']
481
+ })
482
+ ]
483
+
484
+ readonly_fields = ['last_login', 'date_joined']
485
+
486
+ def get_ai_insights(self, request, obj=None):
487
+ """Get AI insights for users"""
488
+ insights = super().get_ai_insights(request, obj)
489
+
490
+ if obj:
491
+ # User-specific AI insights
492
+ insights.update({
493
+ "login_patterns": "Regular weekday usage",
494
+ "activity_score": 0.8,
495
+ "security_risk": "low",
496
+ "suggested_permissions": [],
497
+ "account_health": "excellent",
498
+ "ai_usage_stats": {
499
+ "fields_computed": 42,
500
+ "predictions_requested": 15,
501
+ "content_generated": 8
502
+ }
503
+ })
504
+ else:
505
+ # User management insights
506
+ insights.update({
507
+ "active_users": 0,
508
+ "new_registrations": 0,
509
+ "permission_distribution": {},
510
+ "security_alerts": [],
511
+ "ai_adoption_rate": "76%"
512
+ })
513
+
514
+ return insights
515
+
516
+ class GroupAdmin(ModelAdmin):
517
+ """Admin configuration for Group model"""
518
+
519
+ list_display = ['name', 'description', 'user_count', 'permission_count']
520
+ search_fields = ['name', 'description']
521
+ ordering = ['name']
522
+
523
+ fieldsets = [
524
+ (None, {
525
+ 'fields': ['name', 'description']
526
+ }),
527
+ ('Permissions', {
528
+ 'fields': ['permissions']
529
+ })
530
+ ]
531
+
532
+ def user_count(self, obj):
533
+ """Get number of users in group"""
534
+ return getattr(obj, 'user_count', 0)
535
+ user_count.short_description = 'Users'
536
+
537
+ def permission_count(self, obj):
538
+ """Get number of permissions in group"""
539
+ return getattr(obj, 'permission_count', 0)
540
+ permission_count.short_description = 'Permissions'
541
+
542
+ class PermissionAdmin(ModelAdmin):
543
+ """Admin configuration for Permission model"""
544
+
545
+ list_display = ['name', 'codename', 'content_type']
546
+ list_filter = ['content_type']
547
+ search_fields = ['name', 'codename', 'content_type']
548
+ ordering = ['content_type', 'codename']
549
+
550
+ readonly_fields = ['codename', 'content_type']
551
+
552
+ # ========================================
553
+ # MAIN ADMIN SITE CLASS
554
+ # ========================================
555
+
556
+ class AdminSite:
557
+ """
558
+ Main admin site class
559
+ Manages model registration and provides admin interface.
560
+ """
561
+
562
+ def __init__(self, name='admin'):
563
+ """Initialize admin site"""
564
+ self.name = name
565
+ self._registry = {} # model -> admin_class mapping
566
+ self.site_title = "CREATESONLINE Administration"
567
+ self.site_header = "CREATESONLINE Admin"
568
+ self.index_title = "Site Administration"
569
+ self.site_url = "/"
570
+
571
+ # AI features
572
+ self.ai_enabled = True
573
+ self.smart_dashboard = True
574
+
575
+ # Authentication
576
+ self.require_authentication = True
577
+ self.authenticated_users = set() # Simple session storage
578
+ self.superusers = {} # Will be loaded from database or file
579
+
580
+ # Setup template engine
581
+ self.templates = get_template_engine()
582
+ self._setup_template_dirs()
583
+
584
+ # Load superusers
585
+ self._load_superusers()
586
+
587
+ def _is_authenticated(self, request) -> bool:
588
+ """Check if user is authenticated"""
589
+ if not self.require_authentication:
590
+ return True
591
+
592
+ # Simple session check using request headers or user agent as session
593
+ session_id = self._get_session_id(request)
594
+ return session_id in self.authenticated_users
595
+
596
+ def _get_session_id(self, request) -> str:
597
+ """Get session ID from request"""
598
+ # Simple session implementation using IP + User-Agent
599
+ headers = getattr(request, 'headers', {})
600
+ user_agent = headers.get('user-agent', '') if hasattr(headers, 'get') else ''
601
+
602
+ # Get client IP safely
603
+ client_ip = '127.0.0.1'
604
+ if hasattr(request, 'client'):
605
+ client = getattr(request, 'client')
606
+ if hasattr(client, 'host'):
607
+ client_ip = client.host
608
+ elif hasattr(client, 'get') and callable(client.get):
609
+ client_ip = client.get('host', '127.0.0.1')
610
+
611
+ return f"{client_ip}:{hash(user_agent) % 10000}"
612
+
613
+ def _authenticate_user(self, request, username: str, password: str) -> bool:
614
+ """Authenticate user and create session"""
615
+ if username not in self.superusers:
616
+ return False
617
+
618
+ stored_password = self.superusers[username]
619
+
620
+ # Check password format and verify accordingly
621
+ if stored_password.startswith('pbkdf2_sha256$'):
622
+ # PBKDF2 password (from database)
623
+ from createsonline.auth.models import verify_password
624
+ if verify_password(password, stored_password):
625
+ session_id = self._get_session_id(request)
626
+ self.authenticated_users.add(session_id)
627
+ return True
628
+ elif len(stored_password) == 64:
629
+ # SHA256 hash
630
+ import hashlib
631
+ password_hash = hashlib.sha256(password.encode()).hexdigest()
632
+ if password_hash == stored_password:
633
+ session_id = self._get_session_id(request)
634
+ self.authenticated_users.add(session_id)
635
+ return True
636
+ else:
637
+ # Plain text (legacy)
638
+ if stored_password == password:
639
+ session_id = self._get_session_id(request)
640
+ self.authenticated_users.add(session_id)
641
+ return True
642
+
643
+ return False
644
+
645
+ def _logout_user(self, request):
646
+ """Logout user and remove session"""
647
+ session_id = self._get_session_id(request)
648
+ self.authenticated_users.discard(session_id)
649
+
650
+ def _load_superusers(self):
651
+ """Load superusers from database"""
652
+ # AUTO-INITIALIZE DATABASE if tables don't exist
653
+ self._auto_init_database()
654
+
655
+ try:
656
+ # Try to load from database first
657
+ from createsonline.auth.models import User
658
+ from sqlalchemy import create_engine
659
+ from sqlalchemy.orm import sessionmaker
660
+ import os
661
+
662
+ database_url = os.getenv("DATABASE_URL", "sqlite:///./createsonline.db")
663
+ engine = create_engine(database_url, echo=False)
664
+ SessionLocal = sessionmaker(bind=engine)
665
+ session = SessionLocal()
666
+
667
+ try:
668
+ # Load all superusers from database
669
+ superusers = session.query(User).filter(User.is_superuser == True).all()
670
+ for user in superusers:
671
+ self.superusers[user.username] = user.password_hash
672
+
673
+ if self.superusers:
674
+ pass
675
+ return
676
+
677
+ except Exception as e:
678
+ pass
679
+ finally:
680
+ session.close()
681
+
682
+ except ImportError:
683
+ pass
684
+
685
+ # Fallback removed
686
+ try:
687
+ import json
688
+ with open("superuser.json", "r") as f:
689
+ user_data = json.load(f)
690
+ self.superusers[user_data["username"]] = user_data["password_hash"]
691
+ pass
692
+ return
693
+ except FileNotFoundError:
694
+ pass
695
+ except Exception as e:
696
+ pass
697
+
698
+ # If no users found, provide helpful message
699
+ if not self.superusers:
700
+ pass
701
+ pass
702
+
703
+ def add_superuser(self, username: str, password: str):
704
+ """Add a superuser"""
705
+ self.superusers[username] = password
706
+
707
+ def _auto_init_database(self):
708
+ """Automatically initialize database tables if they don't exist"""
709
+ try:
710
+ from sqlalchemy import create_engine, inspect
711
+ from sqlalchemy.orm import sessionmaker
712
+ from createsonline.auth.models import Base as AuthBase, User, create_superuser
713
+ import os
714
+
715
+ database_url = os.getenv("DATABASE_URL", "sqlite:///./createsonline.db")
716
+ engine = create_engine(database_url, echo=False)
717
+
718
+ # Check if tables exist
719
+ inspector = inspect(engine)
720
+ existing_tables = inspector.get_table_names()
721
+
722
+ if 'createsonline_users' not in existing_tables:
723
+ pass
724
+
725
+ # Import content models to register them
726
+ try:
727
+ from createsonline.admin import content
728
+ except:
729
+ pass
730
+
731
+ # Create all tables
732
+ AuthBase.metadata.create_all(engine)
733
+ pass
734
+
735
+ # Create default superuser from superuser.json if exists
736
+ try:
737
+ import json
738
+ if os.path.exists("superuser.json"):
739
+ SessionLocal = sessionmaker(bind=engine)
740
+ session = SessionLocal()
741
+
742
+ try:
743
+ with open("superuser.json", "r") as f:
744
+ data = json.load(f)
745
+
746
+ # Create user
747
+ user = User(
748
+ username=data["username"],
749
+ email=f"{data['username']}@createsonline.com",
750
+ password_hash=data["password_hash"],
751
+ is_staff=True,
752
+ is_superuser=True,
753
+ is_active=True,
754
+ email_verified=True
755
+ )
756
+ session.add(user)
757
+ session.commit()
758
+ pass
759
+ except Exception as e:
760
+ session.rollback()
761
+ pass
762
+ finally:
763
+ session.close()
764
+ except Exception as e:
765
+ pass
766
+
767
+ except Exception as e:
768
+ # Silently fail if SQLAlchemy not available
769
+ pass
770
+
771
+ def _setup_template_dirs(self):
772
+ """Setup template directories"""
773
+ current_dir = os.path.dirname(__file__)
774
+ template_dirs = [
775
+ os.path.join(current_dir, "templates"),
776
+ os.path.join(current_dir, "..", "static", "templates"),
777
+ os.path.join(current_dir, "..", "..", "templates"),
778
+ ]
779
+
780
+ for template_dir in template_dirs:
781
+ if os.path.exists(template_dir):
782
+ self.templates.add_template_dir(template_dir)
783
+
784
+ def register(self, model_or_iterable, admin_class=None, **options):
785
+ """Register model(s) with admin interface"""
786
+ if not admin_class:
787
+ admin_class = ModelAdmin
788
+
789
+ # Handle single model or iterable
790
+ if isinstance(model_or_iterable, (list, tuple)):
791
+ models = model_or_iterable
792
+ else:
793
+ models = [model_or_iterable]
794
+
795
+ for model in models:
796
+ # Use model name as string key for consistency
797
+ model_name = model.__name__ if hasattr(model, '__name__') else str(model)
798
+ if model_name in self._registry:
799
+ raise ValueError(f"Model {model_name} is already registered")
800
+
801
+ # Create admin instance
802
+ admin_instance = admin_class(model, self)
803
+ self._registry[model_name] = admin_instance
804
+
805
+ def unregister(self, model_or_iterable):
806
+ """Unregister model(s) from admin interface"""
807
+ if isinstance(model_or_iterable, (list, tuple)):
808
+ models = model_or_iterable
809
+ else:
810
+ models = [model_or_iterable]
811
+
812
+ for model in models:
813
+ model_name = model.__name__ if hasattr(model, '__name__') else str(model)
814
+ if model_name in self._registry:
815
+ del self._registry[model_name]
816
+
817
+ def is_registered(self, model):
818
+ """Check if model is registered"""
819
+ model_name = model.__name__ if hasattr(model, '__name__') else str(model)
820
+ return model_name in self._registry
821
+
822
+ def get_model_admin(self, model):
823
+ """Get admin instance for model"""
824
+ model_name = model.__name__ if hasattr(model, '__name__') else str(model)
825
+ return self._registry.get(model_name)
826
+
827
+ def get_registered_models(self):
828
+ """Get all registered models"""
829
+ return list(self._registry.keys())
830
+
831
+ def get_admin_routes(self):
832
+ """Get all admin routes"""
833
+ routes = [
834
+ # Main admin routes - now handles both login and dashboard
835
+ {"path": "/admin", "endpoint": self.admin_index, "methods": ["GET", "POST"]},
836
+ {"path": "/admin/logout", "endpoint": self.admin_logout, "methods": ["POST"]},
837
+
838
+ # AI dashboard
839
+ {"path": "/admin/ai/", "endpoint": self.ai_dashboard, "methods": ["GET"]},
840
+ {"path": "/admin/ai/insights/", "endpoint": self.ai_insights, "methods": ["GET"]},
841
+
842
+ # System routes
843
+ {"path": "/admin/system/", "endpoint": self.system_info, "methods": ["GET"]},
844
+ {"path": "/admin/health/", "endpoint": self.health_check, "methods": ["GET"]},
845
+ ]
846
+
847
+ # Add model-specific routes
848
+ for model, admin in self._registry.items():
849
+ model_name = model.__name__.lower()
850
+ app_label = getattr(model, '__module__', 'default').split('.')[-2] if hasattr(model, '__module__') else 'default'
851
+
852
+ routes.extend([
853
+ {"path": f"/admin/{app_label}/{model_name}/", "endpoint": self.changelist_view, "methods": ["GET"]},
854
+ {"path": f"/admin/{app_label}/{model_name}/add/", "endpoint": self.add_view, "methods": ["GET", "POST"]},
855
+ {"path": f"/admin/{app_label}/{model_name}/{{object_id}}/", "endpoint": self.change_view, "methods": ["GET", "POST"]},
856
+ {"path": f"/admin/{app_label}/{model_name}/{{object_id}}/delete/", "endpoint": self.delete_view, "methods": ["GET", "POST"]},
857
+ {"path": f"/admin/{app_label}/{model_name}/{{object_id}}/history/", "endpoint": self.history_view, "methods": ["GET"]},
858
+ ])
859
+
860
+ return routes
861
+
862
+ # ========================================
863
+ # VIEW IMPLEMENTATIONS
864
+ # ========================================
865
+
866
+ async def admin_index(self, request) -> Union[Dict, Any]:
867
+ """Admin main page - handles both login and dashboard automatically"""
868
+ request_method = getattr(request, 'method', 'GET')
869
+
870
+ # If POST request, try to authenticate
871
+ if request_method == "POST":
872
+ try:
873
+ # Handle login POST - check Content-Type to determine parsing method
874
+ content_type = getattr(request, 'headers', {}).get('content-type', '')
875
+
876
+ if 'application/json' in content_type:
877
+ # Parse JSON data
878
+ data = await request.json()
879
+ else:
880
+ # Parse form-urlencoded data with URL decoding
881
+ from urllib.parse import unquote_plus
882
+ body = await request.body() if hasattr(request, 'body') else b''
883
+ data = {}
884
+ if body:
885
+ body_str = body.decode('utf-8')
886
+ for pair in body_str.split('&'):
887
+ if '=' in pair:
888
+ key, value = pair.split('=', 1)
889
+ # URL decode both key and value
890
+ data[unquote_plus(key)] = unquote_plus(value)
891
+
892
+ username = data.get("username", "")
893
+ password = data.get("password", "")
894
+
895
+ # Authenticate user
896
+ if self._authenticate_user(request, username, password):
897
+ # Successful login - show dashboard
898
+ return await self._show_dashboard(request)
899
+ else:
900
+ # Failed login - show login with error
901
+ return await self._show_login(request, error="Invalid username or password")
902
+
903
+ except Exception as e:
904
+ pass
905
+ import traceback
906
+ traceback.print_exc()
907
+ return await self._show_login(request, error=f"Invalid request data: {str(e)}")
908
+
909
+ # GET request - check if authenticated
910
+ if self._is_authenticated(request):
911
+ # User is logged in - show dashboard
912
+ return await self._show_dashboard(request)
913
+ else:
914
+ # User not logged in - show login form
915
+ return await self._show_login(request)
916
+
917
+ async def _show_login(self, request, error: str = None):
918
+ """Show login form matching homepage UI"""
919
+ error_message = ""
920
+ if error:
921
+ error_message = f'<div class="error">{error}</div>'
922
+
923
+ login_html = f"""<!DOCTYPE html>
924
+ <html lang="en">
925
+ <head>
926
+ <meta charset="UTF-8">
927
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
928
+ <title>Admin Login - CREATESONLINE</title>
929
+ <style>
930
+ * {{
931
+ margin: 0;
932
+ padding: 0;
933
+ box-sizing: border-box;
934
+ }}
935
+
936
+ body {{
937
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
938
+ background: #0a0a0a;
939
+ color: #ffffff;
940
+ padding: 40px 20px;
941
+ min-height: 100vh;
942
+ display: flex;
943
+ align-items: center;
944
+ justify-content: center;
945
+ }}
946
+
947
+ .container {{
948
+ max-width: 500px;
949
+ width: 100%;
950
+ background: #1a1a1a;
951
+ padding: 50px;
952
+ border-radius: 12px;
953
+ border: 1px solid #2a2a2a;
954
+ }}
955
+
956
+ .logo {{
957
+ display: block;
958
+ margin: 0 auto 25px;
959
+ max-width: 300px;
960
+ width: 100%;
961
+ height: auto;
962
+ }}
963
+
964
+ h1 {{
965
+ font-size: 2.5em;
966
+ font-weight: 700;
967
+ text-align: center;
968
+ margin-bottom: 15px;
969
+ background: linear-gradient(135deg, #ffffff 0%, #a0a0a0 100%);
970
+ -webkit-background-clip: text;
971
+ -webkit-text-fill-color: transparent;
972
+ background-clip: text;
973
+ margin-bottom: 40px;
974
+ }}
975
+
976
+ .form-group {{
977
+ margin-bottom: 25px;
978
+ }}
979
+
980
+ .form-group label {{
981
+ display: block;
982
+ margin-bottom: 8px;
983
+ color: #ccc;
984
+ font-size: 0.95em;
985
+ font-weight: 500;
986
+ }}
987
+
988
+ .form-group input {{
989
+ width: 100%;
990
+ padding: 15px;
991
+ background: #0a0a0a;
992
+ border: 1px solid #2a2a2a;
993
+ border-radius: 8px;
994
+ color: #ffffff;
995
+ font-size: 1em;
996
+ transition: all 0.3s ease;
997
+ }}
998
+
999
+ .form-group input:focus {{
1000
+ outline: none;
1001
+ border-color: #555;
1002
+ box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.1);
1003
+ }}
1004
+
1005
+ .login-btn {{
1006
+ width: 100%;
1007
+ padding: 16px;
1008
+ background: #ffffff;
1009
+ color: #000;
1010
+ border: none;
1011
+ border-radius: 8px;
1012
+ font-size: 1.05em;
1013
+ font-weight: 600;
1014
+ cursor: pointer;
1015
+ transition: all 0.3s ease;
1016
+ margin-top: 10px;
1017
+ }}
1018
+
1019
+ .login-btn:hover {{
1020
+ background: #f0f0f0;
1021
+ transform: translateY(-2px);
1022
+ box-shadow: 0 8px 20px rgba(255, 255, 255, 0.15);
1023
+ }}
1024
+
1025
+ .login-btn:active {{
1026
+ transform: translateY(0);
1027
+ }}
1028
+
1029
+ .error {{
1030
+ color: #ff6b6b;
1031
+ background: rgba(255, 107, 107, 0.1);
1032
+ border: 1px solid rgba(255, 107, 107, 0.3);
1033
+ padding: 12px;
1034
+ border-radius: 8px;
1035
+ margin-bottom: 25px;
1036
+ text-align: center;
1037
+ font-size: 0.95em;
1038
+ }}
1039
+
1040
+ @media (max-width: 600px) {{
1041
+ .container {{
1042
+ padding: 30px;
1043
+ }}
1044
+ h1 {{
1045
+ font-size: 2em;
1046
+ }}
1047
+ }}
1048
+ </style>
1049
+ </head>
1050
+ <body>
1051
+ <div class="container">
1052
+ <img src="/logo.png" alt="CREATESONLINE" class="logo">
1053
+ <h1>Admin Login</h1>
1054
+
1055
+ {error_message}
1056
+
1057
+ <form method="POST" action="/admin">
1058
+ <div class="form-group">
1059
+ <label for="username">Username</label>
1060
+ <input type="text" id="username" name="username" required autofocus>
1061
+ </div>
1062
+
1063
+ <div class="form-group">
1064
+ <label for="password">Password</label>
1065
+ <input type="password" id="password" name="password" required>
1066
+ </div>
1067
+
1068
+ <button type="submit" class="login-btn">Sign In</button>
1069
+ </form>
1070
+ </div>
1071
+ </body>
1072
+ </html>"""
1073
+
1074
+ return InternalHTMLResponse(login_html)
1075
+
1076
+ async def _get_dashboard_metrics(self, request):
1077
+ """Get real-time dashboard metrics from backend"""
1078
+ try:
1079
+ # Simple metrics that don't require external dependencies
1080
+ metrics = [
1081
+ {
1082
+ "title": "Registered Models",
1083
+ "value": str(len(self._registry)),
1084
+ "change": f"{len(self._registry)} available"
1085
+ },
1086
+ {
1087
+ "title": "Framework Status",
1088
+ "value": "Operational",
1089
+ "change": "All systems running"
1090
+ },
1091
+ {
1092
+ "title": "AI Features",
1093
+ "value": "Enabled" if self.ai_enabled else "Disabled",
1094
+ "change": "Smart insights ready"
1095
+ },
1096
+ {
1097
+ "title": "Admin Interface",
1098
+ "value": "Active",
1099
+ "change": "CREATESONLINE ready"
1100
+ }
1101
+ ]
1102
+
1103
+ # Try to get system info if available
1104
+ try:
1105
+ import psutil
1106
+ cpu_percent = psutil.cpu_percent(interval=1)
1107
+ memory_percent = psutil.virtual_memory().percent
1108
+
1109
+ metrics[1] = {
1110
+ "title": "System CPU",
1111
+ "value": f"{cpu_percent:.1f}%",
1112
+ "change": "Current usage"
1113
+ }
1114
+ metrics[2] = {
1115
+ "title": "Memory Usage",
1116
+ "value": f"{memory_percent:.1f}%",
1117
+ "change": "RAM utilization"
1118
+ }
1119
+ except ImportError:
1120
+ pass # Use default metrics if psutil not available
1121
+ except Exception:
1122
+ pass # Use default metrics if system query fails
1123
+
1124
+ return metrics
1125
+
1126
+ except Exception as e:
1127
+ # Fallback to basic metrics if everything fails
1128
+ return [
1129
+ {"title": "Registered Models", "value": str(len(self._registry)), "change": "Admin ready"},
1130
+ {"title": "Framework Status", "value": "Operational", "change": "Running"},
1131
+ {"title": "AI Features", "value": "Available", "change": "Ready"},
1132
+ {"title": "Admin Interface", "value": "Active", "change": "Working"}
1133
+ ]
1134
+
1135
+ async def _get_recent_activities(self, request):
1136
+ """Get recent system activities from logs/database"""
1137
+ try:
1138
+ # Try to get real activities from system logs or database
1139
+ activities = []
1140
+
1141
+ # Get admin site initialization time
1142
+ init_time = datetime.utcnow()
1143
+ activities.append({
1144
+ "title": "Admin interface initialized",
1145
+ "description": f"CREATESONLINE admin system started with {len(self._registry)} registered models",
1146
+ "time": "Just now"
1147
+ })
1148
+
1149
+ # Get registered models as activities
1150
+ for model in list(self._registry.keys())[:3]: # Show last 3
1151
+ activities.append({
1152
+ "title": f"Model '{model.__name__}' registered",
1153
+ "description": f"Available in admin interface with management features",
1154
+ "time": "At startup"
1155
+ })
1156
+
1157
+ # Add AI features status
1158
+ if self.ai_enabled:
1159
+ activities.append({
1160
+ "title": "AI features activated",
1161
+ "description": "Smart insights, auto-suggestions, and intelligent analytics enabled",
1162
+ "time": "At startup"
1163
+ })
1164
+
1165
+ return activities[:5] # Return max 5 activities
1166
+
1167
+ except Exception as e:
1168
+ # Fallback activities
1169
+ return [
1170
+ {
1171
+ "title": "CREATESONLINE Admin ready",
1172
+ "description": "Admin interface successfully initialized and ready for use",
1173
+ "time": "Now"
1174
+ },
1175
+ {
1176
+ "title": "Framework status check",
1177
+ "description": "All core systems operational and responding normally",
1178
+ "time": "1 min ago"
1179
+ }
1180
+ ]
1181
+
1182
+ async def _get_registered_models_info(self, request):
1183
+ """Get detailed information about registered models"""
1184
+ models = []
1185
+
1186
+ for model, admin_class in self._registry.items():
1187
+ try:
1188
+ # Get app label from module path
1189
+ app_label = 'default'
1190
+ if hasattr(model, '__module__'):
1191
+ parts = model.__module__.split('.')
1192
+ if len(parts) >= 2:
1193
+ app_label = parts[-2]
1194
+
1195
+ # Get model metadata
1196
+ model_info = {
1197
+ "name": model.__name__,
1198
+ "app_label": app_label,
1199
+ "admin_url": f"/admin/{app_label}/{model.__name__.lower()}/",
1200
+ "admin_class": admin_class.__class__.__name__,
1201
+ "permissions": {
1202
+ "add": admin_class.has_add_permission,
1203
+ "change": admin_class.has_change_permission,
1204
+ "delete": admin_class.has_delete_permission,
1205
+ "view": admin_class.has_view_permission
1206
+ },
1207
+ "list_display": admin_class.list_display[:3], # Show first 3 fields
1208
+ "search_fields": admin_class.search_fields[:2] if admin_class.search_fields else [],
1209
+ "ai_enabled": admin_class.ai_insights_enabled,
1210
+ "description": f"Manage {model.__name__} records with {admin_class.__class__.__name__}"
1211
+ }
1212
+
1213
+ # Try to get record count if possible
1214
+ try:
1215
+ # In a real implementation, this would query the database
1216
+ # record_count = db.query(model).count()
1217
+ model_info["record_count"] = "N/A" # Placeholder
1218
+ except:
1219
+ model_info["record_count"] = "N/A"
1220
+
1221
+ models.append(model_info)
1222
+
1223
+ except Exception as e:
1224
+ # Fallback model info if detailed info fails
1225
+ models.append({
1226
+ "name": model.__name__,
1227
+ "app_label": "default",
1228
+ "admin_url": f"/admin/default/{model.__name__.lower()}/",
1229
+ "description": f"Manage {model.__name__} records",
1230
+ "record_count": "N/A"
1231
+ })
1232
+
1233
+ return models
1234
+
1235
+ async def _show_dashboard(self, request):
1236
+ """Show admin dashboard with real database data"""
1237
+ try:
1238
+ # Get database session
1239
+ from sqlalchemy import create_engine
1240
+ from sqlalchemy.orm import sessionmaker
1241
+ from createsonline.auth.models import User
1242
+ import os
1243
+
1244
+ database_url = os.getenv("DATABASE_URL", "sqlite:///./createsonline.db")
1245
+ engine = create_engine(database_url, echo=False)
1246
+ SessionLocal = sessionmaker(bind=engine)
1247
+ session = SessionLocal()
1248
+
1249
+ try:
1250
+ # Get all users
1251
+ all_users = session.query(User).all()
1252
+ user_count = len(all_users)
1253
+
1254
+ # Get registered models with counts (exclude Group and Permission)
1255
+ models_data = []
1256
+ excluded_models = ['Group', 'Permission']
1257
+
1258
+ for model_name, admin_class in self._registry.items():
1259
+ model_class = admin_class.model
1260
+
1261
+ # Skip Group and Permission models
1262
+ if model_class.__name__ in excluded_models:
1263
+ continue
1264
+
1265
+ try:
1266
+ count = session.query(model_class).count()
1267
+ models_data.append({
1268
+ "name": model_class.__name__,
1269
+ "name_lower": model_class.__name__.lower(),
1270
+ "verbose_name": model_class.__name__.replace('_', ' ').title(),
1271
+ "count": count,
1272
+ "icon": self._get_model_icon(model_class.__name__)
1273
+ })
1274
+ except:
1275
+ models_data.append({
1276
+ "name": model_class.__name__,
1277
+ "name_lower": model_class.__name__.lower(),
1278
+ "verbose_name": model_class.__name__.replace('_', ' ').title(),
1279
+ "count": 0,
1280
+ "icon": self._get_model_icon(model_class.__name__)
1281
+ })
1282
+
1283
+ # Build users table HTML
1284
+ users_rows = ""
1285
+ for user in all_users:
1286
+ role_badge = ""
1287
+ if user.is_superuser:
1288
+ role_badge = '<span class="badge badge-superuser">Superuser</span>'
1289
+ elif user.is_staff:
1290
+ role_badge = '<span class="badge badge-staff">Staff</span>'
1291
+ else:
1292
+ role_badge = '<span class="badge">User</span>'
1293
+
1294
+ users_rows += f"""
1295
+ <tr>
1296
+ <td><strong>{user.username}</strong></td>
1297
+ <td>{user.email}</td>
1298
+ <td>{role_badge}</td>
1299
+ <td>
1300
+ <a href="/admin/user/{user.id}/edit" class="btn-small">Edit</a>
1301
+ <a href="/admin/user/{user.id}/delete" class="btn-small btn-danger">Delete</a>
1302
+ </td>
1303
+ </tr>
1304
+ """
1305
+
1306
+ if not users_rows:
1307
+ users_rows = '<tr><td colspan="4" style="text-align: center; padding: 30px; color: #888;">No users yet</td></tr>'
1308
+
1309
+ # Build models grid HTML
1310
+ models_grid = ""
1311
+ for model in models_data:
1312
+ models_grid += f"""
1313
+ <a href="/admin/model-manager/{model['name_lower']}" class="model-card">
1314
+ <div class="model-icon">{model['icon']}</div>
1315
+ <div class="model-name">{model['verbose_name']}</div>
1316
+ <div class="model-count">{model['count']} records</div>
1317
+ <div class="model-actions">Manage Structure →</div>
1318
+ </a>
1319
+ """
1320
+
1321
+ session.close()
1322
+
1323
+ except Exception as e:
1324
+ pass
1325
+ session.close()
1326
+ # Fallback to empty data
1327
+ users_rows = '<tr><td colspan="4" style="text-align: center; padding: 30px; color: #888;">Error loading users</td></tr>'
1328
+ models_grid = '<div style="padding: 40px; text-align: center; color: #888;">Error loading models</div>'
1329
+ user_count = 0
1330
+
1331
+ except Exception as e:
1332
+ # Fallback to empty data
1333
+ users_rows = '<tr><td colspan="4" style="text-align: center; padding: 30px; color: #888;">Database not initialized</td></tr>'
1334
+ models_grid = '<div style="padding: 40px; text-align: center; color: #888;">Run: createsonline-admin migrate</div>'
1335
+ user_count = 0
1336
+
1337
+ dashboard_html = f"""<!DOCTYPE html>
1338
+ <html lang="en">
1339
+ <head>
1340
+ <meta charset="UTF-8">
1341
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1342
+ <title>Admin Dashboard - CREATESONLINE</title>
1343
+ <style>
1344
+ * {{
1345
+ margin: 0;
1346
+ padding: 0;
1347
+ box-sizing: border-box;
1348
+ }}
1349
+
1350
+ body {{
1351
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
1352
+ background: #fafafa;
1353
+ color: #1a1a1a;
1354
+ padding: 0;
1355
+ min-height: 100vh;
1356
+ }}
1357
+
1358
+ .container {{
1359
+ max-width: 1400px;
1360
+ margin: 0 auto;
1361
+ padding: 20px;
1362
+ }}
1363
+
1364
+ .header {{
1365
+ background: #000000;
1366
+ padding: 20px 40px;
1367
+ border-radius: 12px;
1368
+ margin-bottom: 30px;
1369
+ display: flex;
1370
+ justify-content: space-between;
1371
+ align-items: center;
1372
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
1373
+ }}
1374
+
1375
+ .logo {{
1376
+ display: flex;
1377
+ align-items: center;
1378
+ }}
1379
+
1380
+ .logo img {{
1381
+ height: 50px;
1382
+ width: auto;
1383
+ }}
1384
+
1385
+ .logout-btn {{
1386
+ padding: 10px 25px;
1387
+ background: rgba(255, 255, 255, 0.1);
1388
+ color: #ffffff;
1389
+ border: 1px solid rgba(255, 255, 255, 0.2);
1390
+ border-radius: 8px;
1391
+ font-weight: 600;
1392
+ cursor: pointer;
1393
+ transition: all 0.3s;
1394
+ }}
1395
+
1396
+ .logout-btn:hover {{
1397
+ background: rgba(255, 255, 255, 0.2);
1398
+ border-color: rgba(255, 255, 255, 0.3);
1399
+ }}
1400
+
1401
+ .section {{
1402
+ background: #ffffff;
1403
+ padding: 30px 40px;
1404
+ border-radius: 12px;
1405
+ margin-bottom: 30px;
1406
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
1407
+ border: 1px solid #e5e5e5;
1408
+ }}
1409
+
1410
+ .section-header {{
1411
+ display: flex;
1412
+ justify-content: space-between;
1413
+ align-items: center;
1414
+ margin-bottom: 25px;
1415
+ padding-bottom: 20px;
1416
+ border-bottom: 2px solid #f0f0f0;
1417
+ }}
1418
+
1419
+ h2 {{
1420
+ font-size: 1.8em;
1421
+ color: #1a1a1a;
1422
+ font-weight: 600;
1423
+ }}
1424
+
1425
+ .btn-create {{
1426
+ padding: 10px 25px;
1427
+ background: #000000;
1428
+ color: #ffffff;
1429
+ border: none;
1430
+ border-radius: 8px;
1431
+ font-weight: 600;
1432
+ cursor: pointer;
1433
+ text-decoration: none;
1434
+ display: inline-flex;
1435
+ align-items: center;
1436
+ gap: 8px;
1437
+ transition: all 0.3s;
1438
+ }}
1439
+
1440
+ .btn-create:hover {{
1441
+ background: #333333;
1442
+ transform: translateY(-2px);
1443
+ }}
1444
+
1445
+ .icon-plus {{
1446
+ font-size: 1.2em;
1447
+ font-weight: bold;
1448
+ }}
1449
+
1450
+ table {{
1451
+ width: 100%;
1452
+ border-collapse: collapse;
1453
+ }}
1454
+
1455
+ th {{
1456
+ text-align: left;
1457
+ padding: 15px;
1458
+ background: #fafafa;
1459
+ border-bottom: 2px solid #e5e5e5;
1460
+ font-weight: 600;
1461
+ color: #666;
1462
+ font-size: 0.9em;
1463
+ text-transform: uppercase;
1464
+ letter-spacing: 0.5px;
1465
+ }}
1466
+
1467
+ td {{
1468
+ padding: 15px;
1469
+ border-bottom: 1px solid #f0f0f0;
1470
+ color: #1a1a1a;
1471
+ }}
1472
+
1473
+ tr:hover {{
1474
+ background: #fafafa;
1475
+ }}
1476
+
1477
+ .badge {{
1478
+ display: inline-block;
1479
+ padding: 5px 12px;
1480
+ border-radius: 20px;
1481
+ font-size: 0.85em;
1482
+ font-weight: 600;
1483
+ }}
1484
+
1485
+ .badge-superuser {{
1486
+ background: #000000;
1487
+ color: #fff;
1488
+ }}
1489
+
1490
+ .badge-staff {{
1491
+ background: #666666;
1492
+ color: #fff;
1493
+ }}
1494
+
1495
+ .badge {{
1496
+ background: #e5e5e5;
1497
+ color: #666;
1498
+ }}
1499
+
1500
+ .btn-small {{
1501
+ padding: 6px 15px;
1502
+ background: #000000;
1503
+ color: #fff;
1504
+ border: none;
1505
+ border-radius: 6px;
1506
+ text-decoration: none;
1507
+ font-size: 0.9em;
1508
+ margin-right: 8px;
1509
+ transition: all 0.2s;
1510
+ display: inline-block;
1511
+ }}
1512
+
1513
+ .btn-small:hover {{
1514
+ background: #333333;
1515
+ transform: translateY(-1px);
1516
+ }}
1517
+
1518
+ .btn-danger {{
1519
+ background: #dc2626;
1520
+ color: #fff;
1521
+ }}
1522
+
1523
+ .btn-danger:hover {{
1524
+ background: #b91c1c;
1525
+ }}
1526
+
1527
+ .models-grid {{
1528
+ display: grid;
1529
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
1530
+ gap: 20px;
1531
+ }}
1532
+
1533
+ .model-card {{
1534
+ background: #000000;
1535
+ padding: 30px;
1536
+ border-radius: 12px;
1537
+ text-decoration: none;
1538
+ color: #ffffff;
1539
+ transition: all 0.3s;
1540
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
1541
+ display: flex;
1542
+ flex-direction: column;
1543
+ align-items: center;
1544
+ text-align: center;
1545
+ }}
1546
+
1547
+ .model-card:hover {{
1548
+ background: #1a1a1a;
1549
+ transform: translateY(-6px);
1550
+ box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2);
1551
+ }}
1552
+
1553
+ .model-icon {{
1554
+ font-size: 3em;
1555
+ margin-bottom: 15px;
1556
+ opacity: 0.9;
1557
+ }}
1558
+
1559
+ .model-name {{
1560
+ font-size: 1.2em;
1561
+ font-weight: 600;
1562
+ margin-bottom: 10px;
1563
+ }}
1564
+
1565
+ .model-count {{
1566
+ font-size: 2.5em;
1567
+ font-weight: 700;
1568
+ margin-bottom: 10px;
1569
+ color: #ffffff;
1570
+ }}
1571
+
1572
+ .model-actions {{
1573
+ color: rgba(255, 255, 255, 0.8);
1574
+ font-size: 0.9em;
1575
+ margin-top: auto;
1576
+ }}
1577
+
1578
+ .footer {{
1579
+ text-align: center;
1580
+ padding: 30px;
1581
+ color: #999;
1582
+ }}
1583
+
1584
+ .footer p {{
1585
+ margin: 5px 0;
1586
+ }}
1587
+ </style>
1588
+ </head>
1589
+ <body>
1590
+ <div class="container">
1591
+ <div class="header">
1592
+ <div class="logo">
1593
+ <img src="/logo.png" alt="Logo">
1594
+ </div>
1595
+ <form method="POST" action="/admin/logout" style="display: inline;">
1596
+ <button type="submit" class="logout-btn">Logout</button>
1597
+ </form>
1598
+ </div>
1599
+
1600
+ <!-- Users Section -->
1601
+ <div class="section">
1602
+ <div class="section-header">
1603
+ <h2>👥 Users ({user_count})</h2>
1604
+ <a href="/admin/user/add" class="btn-create">
1605
+ <span class="icon-plus">+</span>
1606
+ Create User
1607
+ </a>
1608
+ </div>
1609
+
1610
+ <table>
1611
+ <thead>
1612
+ <tr>
1613
+ <th>Username</th>
1614
+ <th>Email</th>
1615
+ <th>Role</th>
1616
+ <th>Actions</th>
1617
+ </tr>
1618
+ </thead>
1619
+ <tbody>
1620
+ {users_rows}
1621
+ </tbody>
1622
+ </table>
1623
+ </div>
1624
+
1625
+ <!-- Models Section -->
1626
+ <div class="section">
1627
+ <div class="section-header">
1628
+ <h2>📊 Models</h2>
1629
+ <a href="/admin/create-model" class="btn-create">
1630
+ <span class="icon-plus">+</span>
1631
+ Create Model
1632
+ </a>
1633
+ </div>
1634
+
1635
+ <div class="models-grid">
1636
+ {models_grid}
1637
+ </div>
1638
+ </div>
1639
+
1640
+ <div class="footer">
1641
+ <p><strong>CREATESONLINE Framework</strong> v0.1.6</p>
1642
+ <p style="margin-top: 5px; font-size: 0.9em;">AI-Native Web Framework • Pure Python • Zero Dependencies</p>
1643
+ </div>
1644
+ </div>
1645
+ </body>
1646
+ </html>
1647
+ """
1648
+ return HTMLResponse(dashboard_html)
1649
+
1650
+ def _get_model_icon(self, model_name: str) -> str:
1651
+ """Get icon for model type"""
1652
+ icons = {
1653
+ 'user': '👤',
1654
+ 'group': '👥',
1655
+ 'permission': '🔐',
1656
+ 'post': '📝',
1657
+ 'page': '📄',
1658
+ 'article': '📰',
1659
+ 'product': '🛍️',
1660
+ 'order': '🛒',
1661
+ 'category': '📁',
1662
+ 'tag': '🏷️',
1663
+ 'comment': '💬',
1664
+ 'media': '🖼️',
1665
+ 'file': '📎',
1666
+ }
1667
+
1668
+ model_lower = model_name.lower()
1669
+ for key, icon in icons.items():
1670
+ if key in model_lower:
1671
+ return icon
1672
+
1673
+ return '📊'
1674
+
1675
+ async def _show_dashboard_old(self, request):
1676
+ """Show admin dashboard matching homepage UI"""
1677
+ # Get registered models
1678
+ models_list = []
1679
+ for model, admin_class in self._registry.items():
1680
+ models_list.append({
1681
+ "name": model.__name__,
1682
+ "app_label": getattr(model._meta, 'app_label', 'Unknown') if hasattr(model, '_meta') else 'Unknown'
1683
+ })
1684
+
1685
+ dashboard_html = f"""<!DOCTYPE html>
1686
+ <html lang="en">
1687
+ <head>
1688
+ <meta charset="UTF-8">
1689
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1690
+ <title>Admin Dashboard - CREATESONLINE</title>
1691
+ <style>
1692
+ * {{
1693
+ margin: 0;
1694
+ padding: 0;
1695
+ box-sizing: border-box;
1696
+ }}
1697
+
1698
+ body {{
1699
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
1700
+ background: #0a0a0a;
1701
+ color: #ffffff;
1702
+ padding: 40px 20px;
1703
+ min-height: 100vh;
1704
+ }}
1705
+
1706
+ .container {{
1707
+ max-width: 1000px;
1708
+ margin: 0 auto;
1709
+ background: #1a1a1a;
1710
+ padding: 40px 50px;
1711
+ border-radius: 12px;
1712
+ border: 1px solid #2a2a2a;
1713
+ }}
1714
+
1715
+ .header {{
1716
+ display: flex;
1717
+ justify-content: space-between;
1718
+ align-items: center;
1719
+ margin-bottom: 40px;
1720
+ padding-bottom: 30px;
1721
+ border-bottom: 1px solid #2a2a2a;
1722
+ }}
1723
+
1724
+ h1 {{
1725
+ font-size: 2.5em;
1726
+ font-weight: 700;
1727
+ background: linear-gradient(135deg, #ffffff 0%, #a0a0a0 100%);
1728
+ -webkit-background-clip: text;
1729
+ -webkit-text-fill-color: transparent;
1730
+ background-clip: text;
1731
+ }}
1732
+
1733
+ .logout-btn {{
1734
+ padding: 12px 24px;
1735
+ background: #ffffff;
1736
+ color: #000;
1737
+ border: none;
1738
+ border-radius: 8px;
1739
+ font-weight: 600;
1740
+ cursor: pointer;
1741
+ transition: all 0.3s ease;
1742
+ }}
1743
+
1744
+ .logout-btn:hover {{
1745
+ background: #f0f0f0;
1746
+ transform: translateY(-2px);
1747
+ box-shadow: 0 8px 20px rgba(255, 255, 255, 0.15);
1748
+ }}
1749
+
1750
+ .section-title {{
1751
+ color: #ccc;
1752
+ font-size: 1.3em;
1753
+ margin-bottom: 20px;
1754
+ margin-top: 30px;
1755
+ }}
1756
+
1757
+ .models-grid {{
1758
+ display: grid;
1759
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
1760
+ gap: 20px;
1761
+ margin-bottom: 30px;
1762
+ }}
1763
+
1764
+ .model-card {{
1765
+ background: #0a0a0a;
1766
+ border: 1px solid #2a2a2a;
1767
+ padding: 30px;
1768
+ border-radius: 10px;
1769
+ transition: all 0.3s ease;
1770
+ cursor: pointer;
1771
+ }}
1772
+
1773
+ .model-card:hover {{
1774
+ border-color: #555;
1775
+ transform: translateY(-4px);
1776
+ box-shadow: 0 8px 20px rgba(255, 255, 255, 0.1);
1777
+ }}
1778
+
1779
+ .model-name {{
1780
+ color: #fff;
1781
+ font-size: 1.2em;
1782
+ font-weight: 600;
1783
+ margin-bottom: 8px;
1784
+ }}
1785
+
1786
+ .model-label {{
1787
+ color: #888;
1788
+ font-size: 0.9em;
1789
+ }}
1790
+
1791
+ .footer {{
1792
+ text-align: center;
1793
+ color: #666;
1794
+ margin-top: 40px;
1795
+ padding-top: 30px;
1796
+ border-top: 1px solid #2a2a2a;
1797
+ font-size: 0.9em;
1798
+ }}
1799
+
1800
+ @media (max-width: 768px) {{
1801
+ .container {{
1802
+ padding: 30px;
1803
+ }}
1804
+ h1 {{
1805
+ font-size: 2em;
1806
+ }}
1807
+ .header {{
1808
+ flex-direction: column;
1809
+ gap: 20px;
1810
+ align-items: flex-start;
1811
+ }}
1812
+ }}
1813
+ </style>
1814
+ </head>
1815
+ <body>
1816
+ <div class="container">
1817
+ <div class="header">
1818
+ <h1>Admin Dashboard</h1>
1819
+ <form method="POST" action="/admin/logout">
1820
+ <button type="submit" class="logout-btn">Logout</button>
1821
+ </form>
1822
+ </div>
1823
+
1824
+ <div class="section-title">Registered Models ({len(models_list)})</div>
1825
+
1826
+ <div class="models-grid">
1827
+ {''.join([f'''
1828
+ <div class="model-card">
1829
+ <div class="model-name">{model["name"]}</div>
1830
+ <div class="model-label">{model["app_label"]}</div>
1831
+ </div>
1832
+ ''' for model in models_list])}
1833
+ </div>
1834
+
1835
+ <div class="footer">
1836
+ <p>CREATESONLINE v0.1.6 Admin Interface</p>
1837
+ </div>
1838
+ </div>
1839
+ </body>
1840
+ </html>"""
1841
+
1842
+ return InternalHTMLResponse(dashboard_html)
1843
+
1844
+ async def _get_authenticated_admin_index(self, request):
1845
+ """Get the authenticated admin dashboard content"""
1846
+ # This is the same logic as admin_index but we know user is authenticated
1847
+ metrics = [
1848
+ {"title": "Total Users", "value": "1,247", "change": "+12% this month"},
1849
+ {"title": "AI Models Active", "value": "8", "change": "+2 new models"},
1850
+ {"title": "Predictions Today", "value": "15.2K", "change": "+8% vs yesterday"},
1851
+ {"title": "System Health", "value": "98%", "change": "All systems optimal"},
1852
+ ]
1853
+
1854
+ # Get recent activities
1855
+ activities = [
1856
+ {
1857
+ "title": "AI Model processed 150 predictions",
1858
+ "description": "Lead Scorer model with 94.2% accuracy",
1859
+ "time": "2 min ago"
1860
+ },
1861
+ {
1862
+ "title": "New user registration",
1863
+ "description": "john.doe@example.com assigned to Sales Team",
1864
+ "time": "5 min ago"
1865
+ },
1866
+ {
1867
+ "title": "System backup completed",
1868
+ "description": "Full database backup completed successfully",
1869
+ "time": "1 hour ago"
1870
+ }
1871
+ ]
1872
+
1873
+ # Get registered models
1874
+ models = []
1875
+ for model, admin_class in self._registry.items():
1876
+ models.append({
1877
+ "name": model.__name__,
1878
+ "app_label": getattr(model._meta, 'app_label', 'Unknown') if hasattr(model, '_meta') else 'Unknown',
1879
+ "admin_class": admin_class.__class__.__name__
1880
+ })
1881
+
1882
+ # Build context
1883
+ context = {
1884
+ "title": "CREATESONLINE Admin Dashboard",
1885
+ "header": "CREATESONLINE Admin",
1886
+ "site_name": self.name,
1887
+ "metrics": metrics,
1888
+ "activities": activities,
1889
+ "models": models,
1890
+ "registered_models": len(models),
1891
+ "framework_info": {
1892
+ "name": "CREATESONLINE",
1893
+ "version": "0.1.6",
1894
+ "status": "operational"
1895
+ }
1896
+ }
1897
+
1898
+ # Check if request expects HTML
1899
+ accept_header = ""
1900
+ if hasattr(request, 'headers'):
1901
+ accept_header = request.headers.get('accept', '').lower()
1902
+
1903
+ if 'text/html' in accept_header or not accept_header:
1904
+ # Return HTML dashboard
1905
+ basic_html = f"""<!DOCTYPE html>
1906
+ <html lang="en">
1907
+ <head>
1908
+ <meta charset="UTF-8">
1909
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1910
+ <title>{context['title']}</title>
1911
+ <style>
1912
+ body {{ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; background: linear-gradient(135deg, #000000 0%, #ffffff 100%); }}
1913
+ .container {{ max-width: 1200px; margin: 2rem auto; padding: 0 2rem; }}
1914
+ .header {{ background: linear-gradient(135deg, #000000 0%, #ffffff 100%); color: white; padding: 2rem; text-align: center; }}
1915
+ .card {{ background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); margin: 2rem 0; }}
1916
+ .card-header {{ padding: 1.5rem; border-bottom: 1px solid #eee; font-weight: bold; }}
1917
+ .card-body {{ padding: 1.5rem; }}
1918
+ .metrics {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1rem; }}
1919
+ .metric {{ text-align: center; padding: 1rem; background: #f8f9fa; border-radius: 8px; }}
1920
+ .metric-value {{ font-size: 2rem; font-weight: bold; color: #000; }}
1921
+ .metric-label {{ color: #666; margin-top: 0.5rem; }}
1922
+ .success {{ color: #28a745; font-weight: bold; text-align: center; padding: 1rem; background: #d4edda; border-radius: 8px; margin-bottom: 2rem; }}
1923
+ </style>
1924
+ </head>
1925
+ <body>
1926
+ <div class="success">✅ Login Successful! Welcome to CREATESONLINE Admin</div>
1927
+ <div class="header">
1928
+ <h1><img src="/static/image/favicon-32x32.png" alt="CREATESONLINE" style="width: 32px; height: 32px; vertical-align: middle; margin-right: 10px;">{context['header']}</h1>
1929
+ <p>AI-Native Framework Administration</p>
1930
+ </div>
1931
+
1932
+ <div class="container">
1933
+ <div class="card">
1934
+ <div class="card-header">📊 Dashboard Overview</div>
1935
+ <div class="card-body">
1936
+ <div class="metrics">
1937
+ {''.join([f'<div class="metric"><div class="metric-value">{metric["value"]}</div><div class="metric-label">{metric["title"]}</div></div>' for metric in context["metrics"]])}
1938
+ </div>
1939
+
1940
+ <h3>🎯 Registered Models ({context['registered_models']})</h3>
1941
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1rem; margin: 1rem 0;">
1942
+ {''.join([f'''
1943
+ <div style="border: 1px solid #ddd; border-radius: 8px; padding: 1rem; background: #f8f9fa;">
1944
+ <h4 style="margin: 0 0 0.5rem 0; color: #000;">
1945
+ <a href="{model["admin_url"]}" style="text-decoration: none; color: #000;">{model["name"]}</a>
1946
+ </h4>
1947
+ <p style="margin: 0.5rem 0; color: #666; font-size: 0.9rem;">{model.get("description", "")}</p>
1948
+ <div style="display: flex; gap: 0.5rem; margin-top: 0.5rem;">
1949
+ <span style="background: #000; color: white; padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.8rem;">{model["app_label"]}</span>
1950
+ {('<span style="background: #28a745; color: white; padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.8rem;">AI</span>' if model.get("ai_enabled") else '')}
1951
+ </div>
1952
+ </div>
1953
+ ''' for model in context["models"]])}
1954
+ </div>
1955
+
1956
+ <h3>🔥 Recent Activity</h3>
1957
+ {''.join([f'<div style="border-left: 3px solid #000; padding-left: 1rem; margin: 1rem 0; background: #f8f9fa; padding: 1rem; border-radius: 4px;"><strong>{activity["title"]}</strong><br><small style="color: #666;">{activity["description"]} - {activity["time"]}</small></div>' for activity in context["activities"]])}
1958
+
1959
+ <h3>🔗 Quick Actions</h3>
1960
+ <div style="display: flex; gap: 1rem; flex-wrap: wrap; margin: 1rem 0;">
1961
+ <a href="/admin/ai/" style="display: inline-block; background: #000; color: white; padding: 0.75rem 1rem; border-radius: 6px; text-decoration: none; font-weight: 500;"><img src="/static/image/favicon-16x16.png" alt="AI" style="width: 16px; height: 16px; vertical-align: middle; margin-right: 5px;">AI Dashboard</a>
1962
+ <a href="/admin/system/" style="display: inline-block; background: #6c757d; color: white; padding: 0.75rem 1rem; border-radius: 6px; text-decoration: none; font-weight: 500;">⚙️ System Info</a>
1963
+ <a href="/admin/health/" style="display: inline-block; background: #28a745; color: white; padding: 0.75rem 1rem; border-radius: 6px; text-decoration: none; font-weight: 500;">🏥 Health Check</a>
1964
+ <a href="/health" style="display: inline-block; background: #17a2b8; color: white; padding: 0.75rem 1rem; border-radius: 6px; text-decoration: none; font-weight: 500;">📊 Live Health</a>
1965
+ </div>
1966
+ </div>
1967
+ </div>
1968
+ </div>
1969
+ </body>
1970
+ </html>"""
1971
+ return HTMLResponse(basic_html)
1972
+ else:
1973
+ # Return JSON for API requests
1974
+ return JSONResponse(context)
1975
+
1976
+ async def admin_login(self, request):
1977
+ """Admin login view"""
1978
+ request_method = getattr(request, 'method', 'GET')
1979
+
1980
+ if request_method == "POST":
1981
+ # Handle login POST
1982
+ try:
1983
+ if hasattr(request, 'json'):
1984
+ data = await request.json()
1985
+ else:
1986
+ # Simple form data parsing
1987
+ body = await request.body() if hasattr(request, 'body') else b''
1988
+ data = {}
1989
+ if body:
1990
+ body_str = body.decode('utf-8')
1991
+ for pair in body_str.split('&'):
1992
+ if '=' in pair:
1993
+ key, value = pair.split('=', 1)
1994
+ data[key] = value
1995
+
1996
+ username = data.get("username", "")
1997
+ password = data.get("password", "")
1998
+
1999
+ # Authenticate user
2000
+ if self._authenticate_user(request, username, password):
2001
+ # Successful login - show dashboard directly (no redirect)
2002
+ # Create a new request context to show authenticated admin
2003
+ return await self._get_authenticated_admin_index(request)
2004
+ else:
2005
+ # Failed login - show error
2006
+ error_html = f"""<!DOCTYPE html>
2007
+ <html>
2008
+ <head>
2009
+ <meta charset="UTF-8">
2010
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
2011
+ <title>Login Failed - CREATESONLINE Admin</title>
2012
+ <style>
2013
+ body {{
2014
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
2015
+ margin: 0;
2016
+ background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
2017
+ min-height: 100vh;
2018
+ display: flex;
2019
+ align-items: center;
2020
+ justify-content: center;
2021
+ }}
2022
+ .card {{
2023
+ background: white;
2024
+ border-radius: 12px;
2025
+ box-shadow: 0 8px 32px rgba(0,0,0,0.1);
2026
+ width: 100%;
2027
+ max-width: 400px;
2028
+ overflow: hidden;
2029
+ }}
2030
+ .card-header {{
2031
+ background: linear-gradient(135deg, #dc3545 0%, #c82333 100%);
2032
+ color: white;
2033
+ padding: 2rem;
2034
+ text-align: center;
2035
+ }}
2036
+ .card-body {{ padding: 2rem; text-align: center; }}
2037
+ .error {{ color: #dc3545; margin-bottom: 1rem; }}
2038
+ .btn {{
2039
+ padding: 0.75rem 1.5rem;
2040
+ border: none;
2041
+ border-radius: 6px;
2042
+ cursor: pointer;
2043
+ text-decoration: none;
2044
+ background: #000;
2045
+ color: white;
2046
+ }}
2047
+ </style>
2048
+ </head>
2049
+ <body>
2050
+ <div class="card">
2051
+ <div class="card-header">
2052
+ <h2>❌ Login Failed</h2>
2053
+ </div>
2054
+ <div class="card-body">
2055
+ <div class="error">Invalid username or password</div>
2056
+ <p>Please check your credentials and try again.</p>
2057
+ <a href="/admin/login/" class="btn">← Back to Login</a>
2058
+ </div>
2059
+ </div>
2060
+ </body>
2061
+ </html>"""
2062
+ return HTMLResponse(error_html, status_code=401)
2063
+
2064
+ except Exception as e:
2065
+ return JSONResponse({
2066
+ "success": False,
2067
+ "error": "Invalid request data"
2068
+ }, status_code=400)
2069
+ else:
2070
+ # Return login form HTML
2071
+ login_html = """<!DOCTYPE html>
2072
+ <html lang="en">
2073
+ <head>
2074
+ <meta charset="UTF-8">
2075
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
2076
+ <title>Login - CREATESONLINE Admin</title>
2077
+ <style>
2078
+ body {
2079
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
2080
+ margin: 0;
2081
+ background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
2082
+ min-height: 100vh;
2083
+ display: flex;
2084
+ align-items: center;
2085
+ justify-content: center;
2086
+ }
2087
+ .card {
2088
+ background: white;
2089
+ border-radius: 12px;
2090
+ box-shadow: 0 8px 32px rgba(0,0,0,0.1);
2091
+ width: 100%;
2092
+ max-width: 400px;
2093
+ overflow: hidden;
2094
+ }
2095
+ .card-header {
2096
+ background: linear-gradient(135deg, #000 0%, #333 100%);
2097
+ color: white;
2098
+ padding: 2rem;
2099
+ text-align: center;
2100
+ }
2101
+ .card-header h2 {
2102
+ margin: 0;
2103
+ font-size: 1.5rem;
2104
+ font-weight: 300;
2105
+ }
2106
+ .card-body { padding: 2rem; }
2107
+ .form-group { margin-bottom: 1.5rem; }
2108
+ .form-group label {
2109
+ display: block;
2110
+ margin-bottom: 0.5rem;
2111
+ font-weight: 500;
2112
+ color: #333;
2113
+ }
2114
+ .form-group input {
2115
+ width: 100%;
2116
+ padding: 0.75rem;
2117
+ border: 2px solid #e9ecef;
2118
+ border-radius: 6px;
2119
+ font-size: 1rem;
2120
+ transition: border-color 0.2s;
2121
+ box-sizing: border-box;
2122
+ }
2123
+ .form-group input:focus {
2124
+ outline: none;
2125
+ border-color: #000;
2126
+ }
2127
+ .btn {
2128
+ width: 100%;
2129
+ padding: 0.75rem;
2130
+ border: none;
2131
+ border-radius: 6px;
2132
+ cursor: pointer;
2133
+ font-size: 1rem;
2134
+ font-weight: 500;
2135
+ transition: all 0.2s;
2136
+ }
2137
+ .btn-primary {
2138
+ background: #000;
2139
+ color: white;
2140
+ }
2141
+ .btn-primary:hover {
2142
+ background: #333;
2143
+ }
2144
+ </style>
2145
+ </head>
2146
+ <body>
2147
+ <div class="card">
2148
+ <div class="card-header">
2149
+ <h2><img src="/static/image/favicon-32x32.png" alt="CREATESONLINE" style="width: 32px; height: 32px; vertical-align: middle; margin-right: 10px;">CREATESONLINE Admin</h2>
2150
+ </div>
2151
+ <div class="card-body">
2152
+ <form method="post" action="/admin/login/">
2153
+ <div class="form-group">
2154
+ <label for="username">Username</label>
2155
+ <input type="text" id="username" name="username" required placeholder="Enter your username">
2156
+ </div>
2157
+ <div class="form-group">
2158
+ <label for="password">Password</label>
2159
+ <input type="password" id="password" name="password" required placeholder="Enter your password">
2160
+ </div>
2161
+ <button type="submit" class="btn btn-primary">Sign In</button>
2162
+ </form>
2163
+ </div>
2164
+ </div>
2165
+ </body>
2166
+ </html>"""
2167
+
2168
+ return HTMLResponse(login_html)
2169
+
2170
+ async def admin_logout(self, request):
2171
+ """Admin logout view"""
2172
+ # Logout user
2173
+ self._logout_user(request)
2174
+
2175
+ # Redirect to login page
2176
+ return await self._show_login(request)
2177
+
2178
+ async def ai_dashboard(self, request):
2179
+ """AI-enhanced admin dashboard"""
2180
+ ai_models = [
2181
+ {
2182
+ "name": "Lead Scorer",
2183
+ "status": "active",
2184
+ "accuracy": "94.2%",
2185
+ "predictions": "1,247",
2186
+ "last_updated": "2 hours ago"
2187
+ },
2188
+ {
2189
+ "name": "Content Generator",
2190
+ "status": "active",
2191
+ "model": "GPT-4",
2192
+ "generated": "856",
2193
+ "avg_time": "2.1s"
2194
+ },
2195
+ {
2196
+ "name": "Vector Search",
2197
+ "status": "active",
2198
+ "dimensions": "1536",
2199
+ "searches": "3,412",
2200
+ "query_time": "12ms"
2201
+ },
2202
+ {
2203
+ "name": "Sentiment Analyzer",
2204
+ "status": "training",
2205
+ "progress": "87%",
2206
+ "eta": "2h remaining",
2207
+ "version": "v2.1"
2208
+ }
2209
+ ]
2210
+
2211
+ context = {
2212
+ "title": "AI Dashboard",
2213
+ "framework": "CREATESONLINE",
2214
+ "ai_features": [
2215
+ "Smart Data Insights",
2216
+ "Automated Reporting",
2217
+ "Predictive Analytics",
2218
+ "Intelligent Search",
2219
+ "AI Recommendations"
2220
+ ],
2221
+ "system_health": {
2222
+ "models": len(self._registry),
2223
+ "ai_status": "operational",
2224
+ "insights_available": True
2225
+ },
2226
+ "ai_models": ai_models
2227
+ }
2228
+
2229
+ return JSONResponse(context)
2230
+
2231
+ async def ai_insights(self, request):
2232
+ """Get AI insights for admin"""
2233
+ insights = {}
2234
+
2235
+ # Get insights from all registered models
2236
+ for model, admin in self._registry.items():
2237
+ model_insights = admin.get_ai_insights(request)
2238
+ insights[model.__name__.lower()] = model_insights
2239
+
2240
+ return JSONResponse({
2241
+ "framework": "CREATESONLINE",
2242
+ "ai_insights": insights,
2243
+ "generated_at": datetime.utcnow().isoformat(),
2244
+ "confidence": 0.92,
2245
+ "recommendations": [
2246
+ "Consider enabling auto-scaling for AI models",
2247
+ "Review data quality metrics for improved predictions",
2248
+ "Optimize vector search indices for better performance"
2249
+ ]
2250
+ })
2251
+
2252
+ async def system_info(self, request):
2253
+ """System information view"""
2254
+ return JSONResponse({
2255
+ "framework": "CREATESONLINE",
2256
+ "admin_version": "0.1.6",
2257
+ "registered_models": len(self._registry),
2258
+ "ai_enabled": self.ai_enabled,
2259
+ "features": [
2260
+ "Model Registration",
2261
+ "CRUD Operations",
2262
+ "Permission Management",
2263
+ "AI Insights",
2264
+ "Smart Search",
2265
+ "Pure Python"
2266
+ ],
2267
+ "dependencies": {
2268
+ "external": "None - Pure Python",
2269
+ "optional": ["SQLAlchemy", "OpenAI"],
2270
+ "template_engine": "Internal CREATESONLINE Engine"
2271
+ }
2272
+ })
2273
+
2274
+ async def health_check(self, request):
2275
+ """Admin health check"""
2276
+ return JSONResponse({
2277
+ "status": "healthy",
2278
+ "admin_site": self.name,
2279
+ "models_registered": len(self._registry),
2280
+ "ai_operational": self.ai_enabled,
2281
+ "template_engine": "working",
2282
+ "timestamp": datetime.utcnow().isoformat()
2283
+ })
2284
+
2285
+ async def changelist_view(self, request):
2286
+ """Model list view"""
2287
+ # Extract model info from request path
2288
+ path_parts = request.url.path.strip('/').split('/')
2289
+ if len(path_parts) >= 3:
2290
+ app_label = path_parts[1]
2291
+ model_name = path_parts[2]
2292
+ else:
2293
+ app_label = "default"
2294
+ model_name = "model"
2295
+
2296
+ context = {
2297
+ "view": "changelist",
2298
+ "title": f"{model_name.title()} List",
2299
+ "framework": "CREATESONLINE",
2300
+ "model_name": model_name,
2301
+ "app_label": app_label,
2302
+ "objects": [], # Would be populated from database
2303
+ "list_display": ["id", "name", "created_at", "status"],
2304
+ "can_add": True,
2305
+ "can_change": True,
2306
+ "can_delete": True
2307
+ }
2308
+
2309
+ # Check if HTML response requested
2310
+ if hasattr(request, 'headers') and request.headers.get('accept', '').startswith('text/html'):
2311
+ list_html = self.templates.render_template("admin/model_list.html", context)
2312
+ full_html = self.templates.render_template("admin/base.html", {
2313
+ **context,
2314
+ "content": list_html
2315
+ })
2316
+ return HTMLResponse(full_html)
2317
+ else:
2318
+ return JSONResponse(context)
2319
+
2320
+ async def add_view(self, request):
2321
+ """Model add view"""
2322
+ return JSONResponse({
2323
+ "view": "add",
2324
+ "title": "Add Model",
2325
+ "framework": "CREATESONLINE",
2326
+ "form_fields": [
2327
+ {"name": "name", "type": "text", "required": True},
2328
+ {"name": "description", "type": "textarea", "required": False},
2329
+ {"name": "is_active", "type": "checkbox", "default": True}
2330
+ ]
2331
+ })
2332
+
2333
+ async def change_view(self, request):
2334
+ """Model change view"""
2335
+ object_id = getattr(request, 'path_params', {}).get("object_id", "1")
2336
+ return JSONResponse({
2337
+ "view": "change",
2338
+ "object_id": object_id,
2339
+ "title": "Change Model",
2340
+ "framework": "CREATESONLINE",
2341
+ "object": {"id": object_id, "name": "Example Object"},
2342
+ "form_fields": [
2343
+ {"name": "name", "type": "text", "value": "Example Object"},
2344
+ {"name": "description", "type": "textarea", "value": ""},
2345
+ {"name": "is_active", "type": "checkbox", "value": True}
2346
+ ]
2347
+ })
2348
+
2349
+ async def delete_view(self, request):
2350
+ """Model delete view"""
2351
+ object_id = getattr(request, 'path_params', {}).get("object_id", "1")
2352
+ return JSONResponse({
2353
+ "view": "delete",
2354
+ "object_id": object_id,
2355
+ "title": "Delete Model",
2356
+ "framework": "CREATESONLINE",
2357
+ "object": {"id": object_id, "name": "Example Object"},
2358
+ "related_objects": [],
2359
+ "confirm_message": f"Are you sure you want to delete this object?"
2360
+ })
2361
+
2362
+ async def history_view(self, request):
2363
+ """Model history view"""
2364
+ object_id = getattr(request, 'path_params', {}).get("object_id", "1")
2365
+ return JSONResponse({
2366
+ "view": "history",
2367
+ "object_id": object_id,
2368
+ "title": "Model History",
2369
+ "framework": "CREATESONLINE",
2370
+ "history": [
2371
+ {
2372
+ "action": "Created",
2373
+ "user": "admin",
2374
+ "timestamp": datetime.utcnow().isoformat(),
2375
+ "changes": "Initial creation"
2376
+ }
2377
+ ]
2378
+ })
2379
+
2380
+ # ========================================
2381
+ # ADMIN UTILITIES AND HELPERS
2382
+ # ========================================
2383
+
2384
+ class AdminRegistry:
2385
+ """Registry for admin configurations"""
2386
+
2387
+ def __init__(self):
2388
+ self.sites = {}
2389
+ self.default_site = AdminSite()
2390
+
2391
+ def register_site(self, name: str, site: AdminSite):
2392
+ """Register an admin site"""
2393
+ self.sites[name] = site
2394
+
2395
+ def get_site(self, name: str = 'default') -> AdminSite:
2396
+ """Get admin site by name"""
2397
+ if name == 'default':
2398
+ return self.default_site
2399
+ return self.sites.get(name, self.default_site)
2400
+
2401
+ # Global admin registry
2402
+ _admin_registry = AdminRegistry()
2403
+
2404
+ def get_admin_registry():
2405
+ """Get the global admin registry"""
2406
+ return _admin_registry
2407
+
2408
+ # Create default admin site instance
2409
+ admin_site = AdminSite(name='admin')
2410
+
2411
+ # ========================================
2412
+ # DECORATORS AND UTILITIES
2413
+ # ========================================
2414
+
2415
+ def register(*models, admin_class=None, site=None):
2416
+ """
2417
+ Decorator for registering models with admin interface
2418
+
2419
+ Usage:
2420
+ @admin.register(MyModel)
2421
+ class MyModelAdmin(ModelAdmin):
2422
+ list_display = ['name', 'created_at']
2423
+ """
2424
+ def decorator(admin_class_inner):
2425
+ target_site = site or admin_site
2426
+ for model in models:
2427
+ target_site.register(model, admin_class_inner)
2428
+ return admin_class_inner
2429
+
2430
+ if admin_class is not None:
2431
+ # Direct registration: admin.register(Model, AdminClass)
2432
+ target_site = site or admin_site
2433
+ for model in models:
2434
+ target_site.register(model, admin_class)
2435
+ return admin_class
2436
+
2437
+ return decorator
2438
+
2439
+ def admin_required(func):
2440
+ """Decorator to require admin authentication"""
2441
+ async def wrapper(*args, **kwargs):
2442
+
2443
+ # In production, implement proper authentication
2444
+ return await func(*args, **kwargs)
2445
+ return wrapper
2446
+
2447
+ def staff_required(func):
2448
+ """Decorator to require staff authentication"""
2449
+ async def wrapper(*args, **kwargs):
2450
+
2451
+ # In production, implement proper authentication
2452
+ return await func(*args, **kwargs)
2453
+ return wrapper
2454
+
2455
+ # ========================================
2456
+ # INTEGRATION HELPERS
2457
+ # ========================================
2458
+
2459
+ def autodiscover():
2460
+ """
2461
+ Auto-discover admin configurations in applications.
2462
+ Auto-discover admin modules.
2463
+ """
2464
+ try:
2465
+ # Auto-register auth models if available
2466
+ from createsonline.auth.models import User, Group, Permission
2467
+
2468
+ # Register auth models with admin
2469
+ admin_site.register(User, UserAdmin)
2470
+ admin_site.register(Group, GroupAdmin)
2471
+ admin_site.register(Permission, PermissionAdmin)
2472
+
2473
+
2474
+ except ImportError:
2475
+
2476
+ def setup_admin_routes(app):
2477
+ """Setup admin routes for CREATESONLINE application"""
2478
+ routes = admin_site.get_admin_routes()
2479
+
2480
+ # Pure CREATESONLINE internal routing - no external dependencies
2481
+ for route_config in routes:
2482
+ if hasattr(app, 'routes') or hasattr(app, 'add_route'):
2483
+ # Register route with internal CREATESONLINE routing system
2484
+ path = route_config["path"]
2485
+ endpoint = route_config["endpoint"]
2486
+ methods = route_config.get("methods", ["GET"])
2487
+
2488
+ # Add route to app (handled by CREATESONLINE internal routing)
2489
+ if hasattr(app, 'add_route'):
2490
+ app.add_route(path, endpoint, methods=methods)
2491
+ elif hasattr(app, 'routes'):
2492
+ app.routes.append({
2493
+ "path": path,
2494
+ "endpoint": endpoint,
2495
+ "methods": methods
2496
+ })
2497
+
2498
+ return routes
2499
+
2500
+ def get_admin_context():
2501
+ """Get admin context for templates"""
2502
+ return {
2503
+ "site_title": admin_site.site_title,
2504
+ "site_header": admin_site.site_header,
2505
+ "index_title": admin_site.index_title,
2506
+ "site_url": admin_site.site_url,
2507
+ "ai_enabled": admin_site.ai_enabled,
2508
+ "registered_models": len(admin_site._registry),
2509
+ "framework": "CREATESONLINE",
2510
+ "version": "0.1.6"
2511
+ }
2512
+
2513
+ # ========================================
2514
+ # TESTING AND Example FUNCTIONS
2515
+ # ========================================
2516
+ def get_admin_info():
2517
+ """Get admin interface information"""
2518
+ return {
2519
+ "module": "createsonline.admin",
2520
+ "version": "0.1.6",
2521
+ "description": "Admin interface for CREATESONLINE",
2522
+ "features": [
2523
+ "Model registration",
2524
+ "AI-enhanced insights",
2525
+ "Internal template engine",
2526
+ "Smart dashboard",
2527
+ "Permission management",
2528
+ "CRUD operations",
2529
+ "Custom actions"
2530
+ ],
2531
+ "registered_models": len(admin_site._registry),
2532
+ "template_engine": "Internal CREATESONLINE Engine",
2533
+ "ai_enabled": admin_site.ai_enabled,
2534
+ "dependencies": {
2535
+ "required": "None - Pure Python",
2536
+ "optional": ["SQLAlchemy"],
2537
+ "template_system": "Internal"
2538
+ }
2539
+ }
2540
+
2541
+ # Auto-discover admin configurations on import
2542
+ autodiscover()
2543
+
2544
+ # Export admin components
2545
+ __all__ = [
2546
+ 'AdminSite',
2547
+ 'ModelAdmin',
2548
+ 'admin_site',
2549
+ 'UserAdmin',
2550
+ 'GroupAdmin',
2551
+ 'PermissionAdmin',
2552
+ 'register',
2553
+ 'admin_required',
2554
+ 'staff_required',
2555
+ 'autodiscover',
2556
+ 'setup_admin_routes',
2557
+ 'get_admin_context',
2558
+ 'get_admin_info',
2559
+ 'get_template_engine',
2560
+ 'CreatesonlineTemplateEngine'
2561
+ ]
2562
+