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,179 @@
1
+ # createsonline/config/database.py
2
+ """
3
+ CREATESONLINE Database Configuration
4
+ Wrapper around the database abstraction layer for configuration management.
5
+ Supports SQLite, PostgreSQL, and MySQL database connections.
6
+
7
+ The actual database connection is handled by createsonline.database.abstraction.Database
8
+ """
9
+ import os
10
+ from typing import Optional, Dict, Any
11
+ from urllib.parse import urlparse
12
+
13
+
14
+ class DatabaseConfig:
15
+ """Database configuration manager"""
16
+
17
+ # Supported database engines
18
+ SUPPORTED_ENGINES = ['sqlite', 'postgresql', 'mysql', 'mariadb']
19
+
20
+ @staticmethod
21
+ def get_database_url() -> str:
22
+ """
23
+ Get database URL from environment variable or config
24
+
25
+ This integrates with createsonline.database.abstraction.Database which
26
+ uses the same DATABASE_URL environment variable.
27
+
28
+ Environment variable priority:
29
+ 1. DATABASE_URL - Complete database URL (used by Database class)
30
+ 2. DATABASE_ENGINE - Type of database (sqlite, postgresql, mysql)
31
+ 3. Uses SQLite as default
32
+
33
+ Supported formats:
34
+ - sqlite:///./db.sqlite3
35
+ - postgresql://user:password@localhost:5432/dbname
36
+ - postgresql+psycopg2://user:password@localhost:5432/dbname
37
+ - mysql+pymysql://user:password@localhost:3306/dbname
38
+ - mysql://user:password@localhost:3306/dbname
39
+
40
+ Returns:
41
+ Database URL string
42
+ """
43
+ # Check for complete DATABASE_URL first (same as Database.abstraction)
44
+ database_url = os.getenv('DATABASE_URL')
45
+ if database_url:
46
+ return database_url
47
+
48
+ # Build from individual components
49
+ engine = os.getenv('DATABASE_ENGINE', 'sqlite').lower()
50
+
51
+ if engine == 'sqlite':
52
+ db_path = os.getenv('DATABASE_PATH', './db.sqlite3')
53
+ return f'sqlite:///{db_path}'
54
+
55
+ elif engine in ['postgresql', 'postgres']:
56
+ return DatabaseConfig._build_postgresql_url()
57
+
58
+ elif engine in ['mysql', 'mariadb']:
59
+ return DatabaseConfig._build_mysql_url()
60
+
61
+ else:
62
+ raise ValueError(f"Unsupported database engine: {engine}")
63
+
64
+ @staticmethod
65
+ def _build_postgresql_url() -> str:
66
+ """Build PostgreSQL connection URL from environment variables"""
67
+ user = os.getenv('DB_USER', 'postgres')
68
+ password = os.getenv('DB_PASSWORD', 'postgres')
69
+ host = os.getenv('DB_HOST', 'localhost')
70
+ port = os.getenv('DB_PORT', '5432')
71
+ database = os.getenv('DB_NAME', 'createsonline')
72
+
73
+ # Use psycopg2 driver (can be overridden)
74
+ driver = os.getenv('DB_DRIVER', 'psycopg2')
75
+
76
+ if password:
77
+ return f'postgresql+{driver}://{user}:{password}@{host}:{port}/{database}'
78
+ else:
79
+ return f'postgresql+{driver}://{user}@{host}:{port}/{database}'
80
+
81
+ @staticmethod
82
+ def _build_mysql_url() -> str:
83
+ """Build MySQL/MariaDB connection URL from environment variables"""
84
+ user = os.getenv('DB_USER', 'root')
85
+ password = os.getenv('DB_PASSWORD', '')
86
+ host = os.getenv('DB_HOST', 'localhost')
87
+ port = os.getenv('DB_PORT', '3306')
88
+ database = os.getenv('DB_NAME', 'createsonline')
89
+
90
+ # Default to pymysql driver for compatibility
91
+ driver = os.getenv('DB_DRIVER', 'pymysql')
92
+
93
+ if password:
94
+ return f'mysql+{driver}://{user}:{password}@{host}:{port}/{database}'
95
+ else:
96
+ return f'mysql+{driver}://{user}@{host}:{port}/{database}'
97
+
98
+ @staticmethod
99
+ def get_connection_info() -> Dict[str, Any]:
100
+ """
101
+ Get parsed connection information
102
+
103
+ Returns:
104
+ Dictionary with connection details
105
+ """
106
+ url = DatabaseConfig.get_database_url()
107
+ parsed = urlparse(url)
108
+
109
+ return {
110
+ 'engine': parsed.scheme.split('+')[0],
111
+ 'driver': parsed.scheme.split('+')[1] if '+' in parsed.scheme else None,
112
+ 'user': parsed.username,
113
+ 'password': parsed.password,
114
+ 'host': parsed.hostname,
115
+ 'port': parsed.port,
116
+ 'database': parsed.path.lstrip('/'),
117
+ 'url': url
118
+ }
119
+
120
+ @staticmethod
121
+ def validate_connection() -> bool:
122
+ """
123
+ Validate database connection without running full migrations
124
+
125
+ Returns:
126
+ True if connection is valid, False otherwise
127
+ """
128
+ try:
129
+ from createsonline.database.abstraction import Database
130
+ db = Database.get_instance()
131
+ # Try to create a session and execute a simple query
132
+ with db.session() as session:
133
+ session.execute("SELECT 1")
134
+ return True
135
+ except Exception as e:
136
+ print(f"Database connection error: {e}")
137
+ return False
138
+
139
+ @staticmethod
140
+ def get_environment_template() -> str:
141
+ """
142
+ Get template for .env file with database configuration examples
143
+
144
+ Returns:
145
+ String with .env template
146
+ """
147
+ return """# CREATESONLINE Database Configuration
148
+
149
+ # Option 1: Complete Database URL (uncomment and use one)
150
+ # DATABASE_URL=sqlite:///./db.sqlite3
151
+ # DATABASE_URL=postgresql://user:password@localhost:5432/createsonline
152
+ # DATABASE_URL=mysql+pymysql://root:password@localhost:3306/createsonline
153
+
154
+ # Option 2: Individual Components (used if DATABASE_URL not set)
155
+ # Database Engine: sqlite, postgresql, mysql, mariadb
156
+ DATABASE_ENGINE=sqlite
157
+
158
+ # SQLite Configuration (used when DATABASE_ENGINE=sqlite)
159
+ DATABASE_PATH=./db.sqlite3
160
+
161
+ # PostgreSQL Configuration (used when DATABASE_ENGINE=postgresql)
162
+ # DB_USER=postgres
163
+ # DB_PASSWORD=your_password
164
+ # DB_HOST=localhost
165
+ # DB_PORT=5432
166
+ # DB_NAME=createsonline
167
+ # DB_DRIVER=psycopg2
168
+
169
+ # MySQL/MariaDB Configuration (used when DATABASE_ENGINE=mysql)
170
+ # DB_USER=root
171
+ # DB_PASSWORD=your_password
172
+ # DB_HOST=localhost
173
+ # DB_PORT=3306
174
+ # DB_NAME=createsonline
175
+ # DB_DRIVER=pymysql
176
+ """
177
+
178
+
179
+ __all__ = ['DatabaseConfig']
@@ -0,0 +1,384 @@
1
+ """
2
+ CREATESONLINE API Documentation Generator
3
+
4
+ Generates beautiful HTML API documentation from route definitions.
5
+ """
6
+ import json
7
+ from datetime import datetime
8
+ import platform
9
+ import sys
10
+ from typing import Dict, Any
11
+
12
+
13
+ class APIDocumentationGenerator:
14
+ """Generate HTML API documentation for CREATESONLINE applications"""
15
+
16
+ def __init__(self, app):
17
+ self.app = app
18
+
19
+ def generate_beautiful_api_docs(self):
20
+ """Generate beautiful HTML API documentation with dynamic backend data"""
21
+ spec = self._build_api_spec()
22
+ html_content = self._render_html_template(spec)
23
+ return self._create_html_response(html_content)
24
+
25
+ def _build_api_spec(self) -> Dict[str, Any]:
26
+ """Build OpenAPI specification"""
27
+ return {
28
+ "openapi": "3.0.0",
29
+ "info": {
30
+ "title": self.app.title,
31
+ "description": self.app.description,
32
+ "version": self.app.version,
33
+ "x-framework": "CREATESONLINE",
34
+ "x-ai-enabled": len(self.app._ai_features) > 0,
35
+ "x-mode": "internal",
36
+ "x-timestamp": datetime.utcnow().isoformat(),
37
+ "x-python-version": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
38
+ "x-platform": platform.system(),
39
+ "x-architecture": platform.machine()
40
+ },
41
+ "servers": [
42
+ {
43
+ "url": "/",
44
+ "description": "CREATESONLINE Development Server",
45
+ "variables": {
46
+ "protocol": {"default": "http", "enum": ["http", "https"]},
47
+ "host": {"default": "127.0.0.1:8000"}
48
+ }
49
+ }
50
+ ],
51
+ "paths": self._generate_enhanced_api_paths(),
52
+ "components": {
53
+ "schemas": self._generate_api_schemas(),
54
+ "securitySchemes": {
55
+ "ApiKeyAuth": {"type": "apiKey", "in": "header", "name": "X-API-Key"},
56
+ "BearerAuth": {"type": "http", "scheme": "bearer"}
57
+ }
58
+ },
59
+ "x-system-info": {
60
+ "framework": "CREATESONLINE",
61
+ "mode": "AI-Native",
62
+ "features": self.app._ai_features,
63
+ "total_routes": len(self.app._internal_routes),
64
+ "ai_routes": len([r for r in self.app._internal_routes.keys() if 'ai' in r.lower()]),
65
+ "admin_routes": len([r for r in self.app._internal_routes.keys() if 'admin' in r.lower()]),
66
+ "startup_time": datetime.utcnow().isoformat(),
67
+ "health_status": "operational",
68
+ "debug_mode": self.app.debug
69
+ }
70
+ }
71
+
72
+ def _generate_enhanced_api_paths(self) -> Dict[str, Any]:
73
+ """Generate OpenAPI paths from registered routes"""
74
+ paths = {}
75
+ for path in self.app._internal_routes.keys():
76
+ route_info = self.app._internal_routes[path]
77
+ method = route_info.get('method', 'GET').lower()
78
+
79
+ if path not in paths:
80
+ paths[path] = {}
81
+
82
+ paths[path][method] = {
83
+ "summary": self._get_route_description(path, method),
84
+ "tags": self._get_route_tags(path),
85
+ "parameters": self._get_route_parameters(path),
86
+ "responses": {
87
+ "200": {
88
+ "description": "Success",
89
+ "content": {
90
+ "application/json": {
91
+ "example": self._get_example_response(path, method)
92
+ }
93
+ }
94
+ }
95
+ },
96
+ "x-code-samples": self._generate_code_samples(path, method)
97
+ }
98
+
99
+ return paths
100
+
101
+ def _get_route_description(self, path: str, method: str) -> str:
102
+ """Get route description"""
103
+ if 'admin' in path:
104
+ return "Admin interface"
105
+ elif 'health' in path:
106
+ return "Health check endpoint"
107
+ elif 'framework' in path:
108
+ return "Framework information"
109
+ return f"{method.upper()} {path}"
110
+
111
+ def _get_route_tags(self, path: str) -> list:
112
+ """Get route tags"""
113
+ tags = []
114
+ if 'admin' in path:
115
+ tags.append('Admin')
116
+ if 'ai' in path:
117
+ tags.append('AI')
118
+ if not tags:
119
+ tags.append('API')
120
+ return tags
121
+
122
+ def _get_route_parameters(self, path: str) -> list:
123
+ """Extract path parameters"""
124
+ import re
125
+ params = []
126
+ pattern = r'\{([^}]+)\}'
127
+ for match in re.finditer(pattern, path):
128
+ param_name = match.group(1)
129
+ params.append({
130
+ "name": param_name,
131
+ "in": "path",
132
+ "required": True,
133
+ "schema": {"type": "string"},
134
+ "description": f"The {param_name} parameter"
135
+ })
136
+ return params
137
+
138
+ def _get_example_response(self, path: str, method: str) -> Dict[str, Any]:
139
+ """Generate example response"""
140
+ return {
141
+ "status": "success",
142
+ "data": f"Response from {method.upper()} {path}",
143
+ "timestamp": datetime.utcnow().isoformat()
144
+ }
145
+
146
+ def _generate_api_schemas(self) -> Dict[str, Any]:
147
+ """Generate OpenAPI schemas"""
148
+ return {
149
+ "Error": {
150
+ "type": "object",
151
+ "properties": {
152
+ "status": {"type": "string"},
153
+ "message": {"type": "string"},
154
+ "code": {"type": "integer"}
155
+ }
156
+ },
157
+ "Success": {
158
+ "type": "object",
159
+ "properties": {
160
+ "status": {"type": "string"},
161
+ "data": {"type": "object"},
162
+ "timestamp": {"type": "string"}
163
+ }
164
+ }
165
+ }
166
+
167
+ def _generate_code_samples(self, path: str, method: str) -> list:
168
+ """Generate code samples for endpoint"""
169
+ base_url = "http://localhost:8000"
170
+ return [
171
+ {
172
+ "lang": "curl",
173
+ "source": f'curl -X {method.upper()} "{base_url}{path}" \\\n -H "Accept: application/json"'
174
+ },
175
+ {
176
+ "lang": "javascript",
177
+ "source": f'fetch("{base_url}{path}", {{\n method: "{method.upper()}",\n headers: {{"Accept": "application/json"}}\n}})'
178
+ },
179
+ {
180
+ "lang": "python",
181
+ "source": f'import requests\nresponse = requests.{method.lower()}("{base_url}{path}")\nprint(response.json())'
182
+ }
183
+ ]
184
+
185
+ def _render_html_template(self, spec: Dict[str, Any]) -> str:
186
+ """Render HTML documentation template"""
187
+ return f"""
188
+ <!DOCTYPE html>
189
+ <html lang="en">
190
+ <head>
191
+ <meta charset="UTF-8">
192
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
193
+ <title>{self.app.title} - API Documentation</title>
194
+ <style>
195
+ * {{ margin: 0; padding: 0; box-sizing: border-box; }}
196
+
197
+ body {{
198
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
199
+ background: linear-gradient(135deg, #000000 0%, #ffffff 100%);
200
+ color: #333;
201
+ min-height: 100vh;
202
+ }}
203
+
204
+ .header {{
205
+ background: linear-gradient(135deg, #000000 0%, #1a1a1a 100%);
206
+ color: white;
207
+ padding: 4rem 2rem;
208
+ text-align: center;
209
+ border-bottom: 1px solid #333;
210
+ }}
211
+
212
+ .header h1 {{
213
+ font-size: 2.5rem;
214
+ margin-bottom: 1rem;
215
+ }}
216
+
217
+ .container {{
218
+ max-width: 1200px;
219
+ margin: 0 auto;
220
+ padding: 2rem;
221
+ }}
222
+
223
+ .api-section {{
224
+ background: white;
225
+ border-radius: 8px;
226
+ padding: 2rem;
227
+ margin-bottom: 2rem;
228
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
229
+ border: 1px solid #e5e5e5;
230
+ }}
231
+
232
+ .section-title {{
233
+ font-size: 1.5rem;
234
+ margin-bottom: 1rem;
235
+ color: #000;
236
+ border-bottom: 2px solid #6366f1;
237
+ padding-bottom: 0.5rem;
238
+ }}
239
+
240
+ .endpoint {{
241
+ border: 1px solid #e5e5e5;
242
+ border-radius: 6px;
243
+ padding: 1.5rem;
244
+ margin-bottom: 1rem;
245
+ background: #f9f9f9;
246
+ }}
247
+
248
+ .endpoint-method {{
249
+ display: inline-block;
250
+ padding: 0.25rem 0.75rem;
251
+ border-radius: 4px;
252
+ font-weight: bold;
253
+ margin-right: 1rem;
254
+ font-size: 0.875rem;
255
+ }}
256
+
257
+ .method-get {{ background: #10b981; color: white; }}
258
+ .method-post {{ background: #3b82f6; color: white; }}
259
+ .method-put {{ background: #f59e0b; color: white; }}
260
+ .method-delete {{ background: #ef4444; color: white; }}
261
+
262
+ .endpoint-path {{
263
+ font-family: 'Monaco', 'Courier New', monospace;
264
+ font-weight: 600;
265
+ color: #333;
266
+ }}
267
+
268
+ .code-block {{
269
+ background: #1e1e1e;
270
+ color: #d4d4d4;
271
+ padding: 1rem;
272
+ border-radius: 6px;
273
+ overflow-x: auto;
274
+ font-family: 'Monaco', monospace;
275
+ font-size: 0.875rem;
276
+ line-height: 1.5;
277
+ margin-top: 1rem;
278
+ }}
279
+
280
+ .badge {{
281
+ display: inline-block;
282
+ padding: 0.25rem 0.75rem;
283
+ background: #6366f1;
284
+ color: white;
285
+ border-radius: 20px;
286
+ font-size: 0.75rem;
287
+ margin-right: 0.5rem;
288
+ margin-bottom: 0.5rem;
289
+ }}
290
+
291
+ .system-info {{
292
+ display: grid;
293
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
294
+ gap: 1rem;
295
+ margin-top: 2rem;
296
+ }}
297
+
298
+ .info-card {{
299
+ background: linear-gradient(135deg, #6366f1, #4f46e5);
300
+ color: white;
301
+ padding: 1.5rem;
302
+ border-radius: 8px;
303
+ text-align: center;
304
+ }}
305
+
306
+ .info-value {{
307
+ font-size: 2rem;
308
+ font-weight: bold;
309
+ margin-bottom: 0.5rem;
310
+ }}
311
+
312
+ .info-label {{
313
+ font-size: 0.875rem;
314
+ opacity: 0.9;
315
+ }}
316
+ </style>
317
+ </head>
318
+ <body>
319
+ <div class="header">
320
+ <h1>{self.app.title}</h1>
321
+ <p>{self.app.description}</p>
322
+ </div>
323
+
324
+ <div class="container">
325
+ <div class="api-section">
326
+ <h2 class="section-title">📊 System Information</h2>
327
+ <div class="system-info">
328
+ <div class="info-card">
329
+ <div class="info-value">{spec['x-system-info']['total_routes']}</div>
330
+ <div class="info-label">Total Endpoints</div>
331
+ </div>
332
+ <div class="info-card">
333
+ <div class="info-value">{spec['x-system-info']['ai_routes']}</div>
334
+ <div class="info-label">AI-Powered Routes</div>
335
+ </div>
336
+ <div class="info-card">
337
+ <div class="info-value">v{self.app.version}</div>
338
+ <div class="info-label">Version</div>
339
+ </div>
340
+ <div class="info-card">
341
+ <div class="info-value">{sys.version_info.major}.{sys.version_info.minor}</div>
342
+ <div class="info-label">Python</div>
343
+ </div>
344
+ </div>
345
+ </div>
346
+
347
+ <div class="api-section">
348
+ <h2 class="section-title">🔌 API Endpoints</h2>
349
+ {self._render_endpoints_html(spec)}
350
+ </div>
351
+ </div>
352
+ </body>
353
+ </html>
354
+ """
355
+
356
+ def _render_endpoints_html(self, spec: Dict[str, Any]) -> str:
357
+ """Render endpoints in HTML"""
358
+ html = ""
359
+ for path, methods in spec.get('paths', {}).items():
360
+ for method, details in methods.items():
361
+ if method.startswith('x-'):
362
+ continue
363
+ method_class = f"method-{method.lower()}"
364
+ html += f"""
365
+ <div class="endpoint">
366
+ <div>
367
+ <span class="endpoint-method {method_class}">{method.upper()}</span>
368
+ <span class="endpoint-path">{path}</span>
369
+ </div>
370
+ <p>{details.get('summary', 'No description')}</p>
371
+ {f'<div class="code-block">curl -X {method.upper()} "{path}"</div>' if path else ''}
372
+ </div>
373
+ """
374
+ return html
375
+
376
+ def _create_html_response(self, content: str):
377
+ """Create HTML response object"""
378
+ class HTMLResponse:
379
+ def __init__(self, content, status_code=200, headers=None):
380
+ self.content = content
381
+ self.status_code = status_code
382
+ self.headers = headers or {'content-type': 'text/html'}
383
+
384
+ return HTMLResponse(content)