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.
- createsonline/__init__.py +46 -0
- createsonline/admin/__init__.py +7 -0
- createsonline/admin/content.py +526 -0
- createsonline/admin/crud.py +805 -0
- createsonline/admin/field_builder.py +559 -0
- createsonline/admin/integration.py +482 -0
- createsonline/admin/interface.py +2562 -0
- createsonline/admin/model_creator.py +513 -0
- createsonline/admin/model_manager.py +388 -0
- createsonline/admin/modern_dashboard.py +498 -0
- createsonline/admin/permissions.py +264 -0
- createsonline/admin/user_forms.py +594 -0
- createsonline/ai/__init__.py +202 -0
- createsonline/ai/fields.py +1226 -0
- createsonline/ai/orm.py +325 -0
- createsonline/ai/services.py +1244 -0
- createsonline/app.py +506 -0
- createsonline/auth/__init__.py +8 -0
- createsonline/auth/management.py +228 -0
- createsonline/auth/models.py +552 -0
- createsonline/cli/__init__.py +5 -0
- createsonline/cli/commands/__init__.py +122 -0
- createsonline/cli/commands/database.py +416 -0
- createsonline/cli/commands/info.py +173 -0
- createsonline/cli/commands/initdb.py +218 -0
- createsonline/cli/commands/project.py +545 -0
- createsonline/cli/commands/serve.py +173 -0
- createsonline/cli/commands/shell.py +93 -0
- createsonline/cli/commands/users.py +148 -0
- createsonline/cli/main.py +2041 -0
- createsonline/cli/manage.py +274 -0
- createsonline/config/__init__.py +9 -0
- createsonline/config/app.py +2577 -0
- createsonline/config/database.py +179 -0
- createsonline/config/docs.py +384 -0
- createsonline/config/errors.py +160 -0
- createsonline/config/orm.py +43 -0
- createsonline/config/request.py +93 -0
- createsonline/config/settings.py +176 -0
- createsonline/data/__init__.py +23 -0
- createsonline/data/dataframe.py +925 -0
- createsonline/data/io.py +453 -0
- createsonline/data/series.py +557 -0
- createsonline/database/__init__.py +60 -0
- createsonline/database/abstraction.py +440 -0
- createsonline/database/assistant.py +585 -0
- createsonline/database/fields.py +442 -0
- createsonline/database/migrations.py +132 -0
- createsonline/database/models.py +604 -0
- createsonline/database.py +438 -0
- createsonline/http/__init__.py +28 -0
- createsonline/http/client.py +535 -0
- createsonline/ml/__init__.py +55 -0
- createsonline/ml/classification.py +552 -0
- createsonline/ml/clustering.py +680 -0
- createsonline/ml/metrics.py +542 -0
- createsonline/ml/neural.py +560 -0
- createsonline/ml/preprocessing.py +784 -0
- createsonline/ml/regression.py +501 -0
- createsonline/performance/__init__.py +19 -0
- createsonline/performance/cache.py +444 -0
- createsonline/performance/compression.py +335 -0
- createsonline/performance/core.py +419 -0
- createsonline/project_init.py +789 -0
- createsonline/routing.py +528 -0
- createsonline/security/__init__.py +34 -0
- createsonline/security/core.py +811 -0
- createsonline/security/encryption.py +349 -0
- createsonline/server.py +295 -0
- createsonline/static/css/admin.css +263 -0
- createsonline/static/css/common.css +358 -0
- createsonline/static/css/dashboard.css +89 -0
- createsonline/static/favicon.ico +0 -0
- createsonline/static/icons/icon-128x128.png +0 -0
- createsonline/static/icons/icon-128x128.webp +0 -0
- createsonline/static/icons/icon-16x16.png +0 -0
- createsonline/static/icons/icon-16x16.webp +0 -0
- createsonline/static/icons/icon-180x180.png +0 -0
- createsonline/static/icons/icon-180x180.webp +0 -0
- createsonline/static/icons/icon-192x192.png +0 -0
- createsonline/static/icons/icon-192x192.webp +0 -0
- createsonline/static/icons/icon-256x256.png +0 -0
- createsonline/static/icons/icon-256x256.webp +0 -0
- createsonline/static/icons/icon-32x32.png +0 -0
- createsonline/static/icons/icon-32x32.webp +0 -0
- createsonline/static/icons/icon-384x384.png +0 -0
- createsonline/static/icons/icon-384x384.webp +0 -0
- createsonline/static/icons/icon-48x48.png +0 -0
- createsonline/static/icons/icon-48x48.webp +0 -0
- createsonline/static/icons/icon-512x512.png +0 -0
- createsonline/static/icons/icon-512x512.webp +0 -0
- createsonline/static/icons/icon-64x64.png +0 -0
- createsonline/static/icons/icon-64x64.webp +0 -0
- createsonline/static/image/android-chrome-192x192.png +0 -0
- createsonline/static/image/android-chrome-512x512.png +0 -0
- createsonline/static/image/apple-touch-icon.png +0 -0
- createsonline/static/image/favicon-16x16.png +0 -0
- createsonline/static/image/favicon-32x32.png +0 -0
- createsonline/static/image/favicon.ico +0 -0
- createsonline/static/image/favicon.svg +17 -0
- createsonline/static/image/icon-128x128.png +0 -0
- createsonline/static/image/icon-128x128.webp +0 -0
- createsonline/static/image/icon-16x16.png +0 -0
- createsonline/static/image/icon-16x16.webp +0 -0
- createsonline/static/image/icon-180x180.png +0 -0
- createsonline/static/image/icon-180x180.webp +0 -0
- createsonline/static/image/icon-192x192.png +0 -0
- createsonline/static/image/icon-192x192.webp +0 -0
- createsonline/static/image/icon-256x256.png +0 -0
- createsonline/static/image/icon-256x256.webp +0 -0
- createsonline/static/image/icon-32x32.png +0 -0
- createsonline/static/image/icon-32x32.webp +0 -0
- createsonline/static/image/icon-384x384.png +0 -0
- createsonline/static/image/icon-384x384.webp +0 -0
- createsonline/static/image/icon-48x48.png +0 -0
- createsonline/static/image/icon-48x48.webp +0 -0
- createsonline/static/image/icon-512x512.png +0 -0
- createsonline/static/image/icon-512x512.webp +0 -0
- createsonline/static/image/icon-64x64.png +0 -0
- createsonline/static/image/icon-64x64.webp +0 -0
- createsonline/static/image/logo-header-h100.png +0 -0
- createsonline/static/image/logo-header-h100.webp +0 -0
- createsonline/static/image/logo-header-h200@2x.png +0 -0
- createsonline/static/image/logo-header-h200@2x.webp +0 -0
- createsonline/static/image/logo.png +0 -0
- createsonline/static/js/admin.js +274 -0
- createsonline/static/site.webmanifest +35 -0
- createsonline/static/templates/admin/base.html +87 -0
- createsonline/static/templates/admin/dashboard.html +217 -0
- createsonline/static/templates/admin/model_form.html +270 -0
- createsonline/static/templates/admin/model_list.html +202 -0
- createsonline/static/test_script.js +15 -0
- createsonline/static/test_styles.css +59 -0
- createsonline/static_files.py +365 -0
- createsonline/templates/404.html +100 -0
- createsonline/templates/admin_login.html +169 -0
- createsonline/templates/base.html +102 -0
- createsonline/templates/index.html +151 -0
- createsonline/templates.py +205 -0
- createsonline/testing.py +322 -0
- createsonline/utils.py +448 -0
- createsonline/validation/__init__.py +49 -0
- createsonline/validation/fields.py +598 -0
- createsonline/validation/models.py +504 -0
- createsonline/validation/validators.py +561 -0
- createsonline/views.py +184 -0
- createsonline-0.1.26.dist-info/METADATA +46 -0
- createsonline-0.1.26.dist-info/RECORD +152 -0
- createsonline-0.1.26.dist-info/WHEEL +5 -0
- createsonline-0.1.26.dist-info/entry_points.txt +2 -0
- createsonline-0.1.26.dist-info/licenses/LICENSE +21 -0
- createsonline-0.1.26.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,2577 @@
|
|
|
1
|
+
# createsonline/config/app.py
|
|
2
|
+
"""
|
|
3
|
+
CREATESONLINE Application Factory
|
|
4
|
+
|
|
5
|
+
The main application factory for the CREATESONLINE framework.
|
|
6
|
+
Built for AI-native web development.
|
|
7
|
+
|
|
8
|
+
Supports Python 3.9 through 3.13 with zero external dependencies.
|
|
9
|
+
"""
|
|
10
|
+
from typing import Dict, Any, Optional, List, Callable
|
|
11
|
+
import sys
|
|
12
|
+
import json
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from urllib.parse import parse_qs
|
|
15
|
+
|
|
16
|
+
# Python version check (3.9-3.13 support)
|
|
17
|
+
if sys.version_info < (3, 9) or sys.version_info >= (3, 14):
|
|
18
|
+
raise RuntimeError(f"CREATESONLINE supports Python 3.9-3.13. Current: {sys.version}")
|
|
19
|
+
|
|
20
|
+
# Import from extracted modules
|
|
21
|
+
from .errors import HTTPException, ErrorPageGenerator
|
|
22
|
+
from .request import CreatesonlineInternalRequest
|
|
23
|
+
from .docs import APIDocumentationGenerator
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class CreatesonlineApp:
|
|
27
|
+
"""
|
|
28
|
+
CREATESONLINE Framework Application
|
|
29
|
+
|
|
30
|
+
The main application class for building AI-native web applications.
|
|
31
|
+
Pure internal implementation with zero external dependencies.
|
|
32
|
+
|
|
33
|
+
Features:
|
|
34
|
+
- Pure AI-native routing and middleware
|
|
35
|
+
- Built-in admin interface
|
|
36
|
+
- User management system
|
|
37
|
+
- Intelligent request/response handling
|
|
38
|
+
- Automatic API documentation
|
|
39
|
+
- Vector search capabilities
|
|
40
|
+
- LLM integration ready
|
|
41
|
+
- Complete internal implementation
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
title: str = "CREATESONLINE Application",
|
|
47
|
+
description: str = "Built with CREATESONLINE - The AI-Native Framework",
|
|
48
|
+
version: str = "1.0.0",
|
|
49
|
+
ai_config: Optional[Dict[str, Any]] = None,
|
|
50
|
+
debug: bool = False,
|
|
51
|
+
cors_origins: Optional[List[str]] = None,
|
|
52
|
+
**kwargs
|
|
53
|
+
):
|
|
54
|
+
"""
|
|
55
|
+
Initialize CREATESONLINE application
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
title: Application title
|
|
59
|
+
description: Application description
|
|
60
|
+
version: Application version
|
|
61
|
+
ai_config: AI configuration dictionary
|
|
62
|
+
debug: Enable debug mode
|
|
63
|
+
cors_origins: Allowed CORS origins
|
|
64
|
+
**kwargs: Additional configuration options
|
|
65
|
+
"""
|
|
66
|
+
# Core application metadata
|
|
67
|
+
self.title = title
|
|
68
|
+
self.description = description
|
|
69
|
+
self.version = version
|
|
70
|
+
self.debug = debug
|
|
71
|
+
self.ai_config = ai_config or {}
|
|
72
|
+
|
|
73
|
+
# Internal application state
|
|
74
|
+
self._routes: List[Dict[str, Any]] = []
|
|
75
|
+
self._middleware: List[Dict[str, Any]] = []
|
|
76
|
+
self._startup_handlers: List[Callable] = []
|
|
77
|
+
self._shutdown_handlers: List[Callable] = []
|
|
78
|
+
|
|
79
|
+
# AI features registry
|
|
80
|
+
self._ai_features: List[str] = []
|
|
81
|
+
|
|
82
|
+
# Internal routing system
|
|
83
|
+
self._internal_routes = {}
|
|
84
|
+
|
|
85
|
+
# Initialize pure internal application
|
|
86
|
+
self._setup_internal_app(cors_origins)
|
|
87
|
+
|
|
88
|
+
# Setup framework routes (works with both implementations)
|
|
89
|
+
self._setup_framework_routes()
|
|
90
|
+
self._setup_error_handlers()
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def routes(self):
|
|
94
|
+
"""Backward compatibility property for _internal_routes"""
|
|
95
|
+
return self._internal_routes
|
|
96
|
+
|
|
97
|
+
def _setup_internal_app(self, cors_origins: Optional[List[str]] = None):
|
|
98
|
+
"""Setup internal ASGI application"""
|
|
99
|
+
|
|
100
|
+
# Store CORS configuration for internal implementation
|
|
101
|
+
self._cors_origins = cors_origins or (["*"] if self.debug else [])
|
|
102
|
+
self._enable_gzip = True
|
|
103
|
+
|
|
104
|
+
def _setup_framework_routes(self):
|
|
105
|
+
"""Setup built-in CREATESONLINE routes"""
|
|
106
|
+
|
|
107
|
+
# Framework root endpoint
|
|
108
|
+
@self.get("/")
|
|
109
|
+
async def root_endpoint(request):
|
|
110
|
+
return await self._root_endpoint(request)
|
|
111
|
+
|
|
112
|
+
# Health check
|
|
113
|
+
@self.get("/health")
|
|
114
|
+
async def health_endpoint(request):
|
|
115
|
+
return await self._health_endpoint(request)
|
|
116
|
+
|
|
117
|
+
# Framework info
|
|
118
|
+
@self.get("/framework/info")
|
|
119
|
+
async def framework_info_endpoint(request):
|
|
120
|
+
return await self._framework_info_endpoint(request)
|
|
121
|
+
|
|
122
|
+
def _setup_error_handlers(self):
|
|
123
|
+
"""Setup CREATESONLINE error handling - pure internal implementation"""
|
|
124
|
+
# Error handling is now done in the internal ASGI handler
|
|
125
|
+
pass
|
|
126
|
+
|
|
127
|
+
async def _root_endpoint(self, request) -> Dict[str, Any]:
|
|
128
|
+
"""CREATESONLINE framework root endpoint"""
|
|
129
|
+
return {
|
|
130
|
+
"framework": "CREATESONLINE",
|
|
131
|
+
"application": self.title,
|
|
132
|
+
"version": self.version,
|
|
133
|
+
"tagline": "Build Intelligence Into Everything",
|
|
134
|
+
"status": "operational",
|
|
135
|
+
"mode": "internal",
|
|
136
|
+
"timestamp": datetime.utcnow().isoformat(),
|
|
137
|
+
"admin_interface": "/admin",
|
|
138
|
+
"api_documentation": "/docs",
|
|
139
|
+
"health_check": "/health",
|
|
140
|
+
"ai_enabled": len(self._ai_features) > 0,
|
|
141
|
+
"ai_features": self._ai_features,
|
|
142
|
+
"python_version": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
|
|
143
|
+
"supported_python": "3.9-3.13"
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async def _health_endpoint(self, request) -> Dict[str, Any]:
|
|
147
|
+
"""CREATESONLINE health check endpoint"""
|
|
148
|
+
return {
|
|
149
|
+
"status": "healthy",
|
|
150
|
+
"framework": "CREATESONLINE",
|
|
151
|
+
"version": self.version,
|
|
152
|
+
"mode": "internal",
|
|
153
|
+
"timestamp": datetime.utcnow().isoformat(),
|
|
154
|
+
"ai_enabled": len(self._ai_features) > 0,
|
|
155
|
+
"python_version": f"{sys.version_info.major}.{sys.version_info.minor}",
|
|
156
|
+
"system_checks": {
|
|
157
|
+
"database": "ready",
|
|
158
|
+
"ai_services": "operational",
|
|
159
|
+
"memory": "optimal",
|
|
160
|
+
"internal_asgi": True
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async def _framework_info_endpoint(self, request) -> Dict[str, Any]:
|
|
165
|
+
"""CREATESONLINE framework information endpoint"""
|
|
166
|
+
return {
|
|
167
|
+
"framework": {
|
|
168
|
+
"name": "CREATESONLINE",
|
|
169
|
+
"version": self.version,
|
|
170
|
+
"description": self.description,
|
|
171
|
+
"tagline": "Build Intelligence Into Everything",
|
|
172
|
+
"architecture": "AI-Native",
|
|
173
|
+
"foundation": "Internal ASGI",
|
|
174
|
+
"python_support": "3.9-3.13"
|
|
175
|
+
},
|
|
176
|
+
"application": {
|
|
177
|
+
"title": self.title,
|
|
178
|
+
"version": self.version,
|
|
179
|
+
"debug": self.debug
|
|
180
|
+
},
|
|
181
|
+
"ai_configuration": self.ai_config,
|
|
182
|
+
"enabled_features": self._ai_features,
|
|
183
|
+
"dependencies": {
|
|
184
|
+
"pure_internal": True,
|
|
185
|
+
"external_ai": self._check_external_ai(),
|
|
186
|
+
"mode": "core"
|
|
187
|
+
},
|
|
188
|
+
"capabilities": [
|
|
189
|
+
"AI-Enhanced Fields",
|
|
190
|
+
"Built-in Admin Interface",
|
|
191
|
+
"User Management System",
|
|
192
|
+
"Natural Language Queries",
|
|
193
|
+
"Vector Similarity Search",
|
|
194
|
+
"LLM Integration",
|
|
195
|
+
"Smart Routing",
|
|
196
|
+
"Intelligent Middleware",
|
|
197
|
+
"Pure Python Core"
|
|
198
|
+
],
|
|
199
|
+
"endpoints": {
|
|
200
|
+
"admin": "/admin",
|
|
201
|
+
"health": "/health",
|
|
202
|
+
"api_spec": "/docs"
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
def _generate_api_paths(self) -> Dict[str, Any]:
|
|
207
|
+
"""Generate OpenAPI paths from routes"""
|
|
208
|
+
paths = {}
|
|
209
|
+
for route_key in self._internal_routes.keys():
|
|
210
|
+
method, path = route_key.split(':', 1)
|
|
211
|
+
if path not in paths:
|
|
212
|
+
paths[path] = {}
|
|
213
|
+
paths[path][method.lower()] = {
|
|
214
|
+
"summary": f"{method} {path}",
|
|
215
|
+
"responses": {
|
|
216
|
+
"200": {"description": "Success"}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return paths
|
|
220
|
+
|
|
221
|
+
def _check_external_ai(self) -> bool:
|
|
222
|
+
"""Check if external AI services are available"""
|
|
223
|
+
try:
|
|
224
|
+
import openai
|
|
225
|
+
return True
|
|
226
|
+
except ImportError:
|
|
227
|
+
return False
|
|
228
|
+
|
|
229
|
+
# ========================================
|
|
230
|
+
# CREATESONLINE ROUTING API
|
|
231
|
+
# ========================================
|
|
232
|
+
|
|
233
|
+
def get(self, path: str, **kwargs) -> Callable:
|
|
234
|
+
"""CREATESONLINE GET route decorator"""
|
|
235
|
+
return self._add_route("GET", path, **kwargs)
|
|
236
|
+
|
|
237
|
+
def post(self, path: str, **kwargs) -> Callable:
|
|
238
|
+
"""CREATESONLINE POST route decorator"""
|
|
239
|
+
return self._add_route("POST", path, **kwargs)
|
|
240
|
+
|
|
241
|
+
def put(self, path: str, **kwargs) -> Callable:
|
|
242
|
+
"""CREATESONLINE PUT route decorator"""
|
|
243
|
+
return self._add_route("PUT", path, **kwargs)
|
|
244
|
+
|
|
245
|
+
def delete(self, path: str, **kwargs) -> Callable:
|
|
246
|
+
"""CREATESONLINE DELETE route decorator"""
|
|
247
|
+
return self._add_route("DELETE", path, **kwargs)
|
|
248
|
+
|
|
249
|
+
def route(
|
|
250
|
+
self,
|
|
251
|
+
path: str,
|
|
252
|
+
methods: Optional[List[str]] = None,
|
|
253
|
+
**kwargs
|
|
254
|
+
) -> Callable:
|
|
255
|
+
"""CREATESONLINE multi-method route decorator"""
|
|
256
|
+
if methods is None:
|
|
257
|
+
methods = ["GET"]
|
|
258
|
+
|
|
259
|
+
def decorator(func: Callable) -> Callable:
|
|
260
|
+
for method in methods:
|
|
261
|
+
self._add_route_internal(method.upper(), path, func, **kwargs)
|
|
262
|
+
return func
|
|
263
|
+
return decorator
|
|
264
|
+
|
|
265
|
+
def _add_route(self, method: str, path: str, **kwargs) -> Callable:
|
|
266
|
+
"""Add route to router"""
|
|
267
|
+
def decorator(func: Callable) -> Callable:
|
|
268
|
+
self._add_route_internal(method, path, func, **kwargs)
|
|
269
|
+
return func
|
|
270
|
+
return decorator
|
|
271
|
+
|
|
272
|
+
def _add_route_internal(self, method: str, path: str, func: Callable, **kwargs):
|
|
273
|
+
"""Internal route registration"""
|
|
274
|
+
route_key = f"{method}:{path}"
|
|
275
|
+
|
|
276
|
+
# Store in internal router only
|
|
277
|
+
self._internal_routes[route_key] = func
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
# ========================================
|
|
281
|
+
# CREATESONLINE MIDDLEWARE & EVENTS
|
|
282
|
+
# ========================================
|
|
283
|
+
|
|
284
|
+
def middleware(self, middleware_type: str = "http") -> Callable:
|
|
285
|
+
"""Add custom middleware to CREATESONLINE application"""
|
|
286
|
+
def decorator(func: Callable) -> Callable:
|
|
287
|
+
self._middleware.append({
|
|
288
|
+
"type": middleware_type,
|
|
289
|
+
"func": func
|
|
290
|
+
})
|
|
291
|
+
return func
|
|
292
|
+
return decorator
|
|
293
|
+
|
|
294
|
+
def on_startup(self, func: Callable) -> Callable:
|
|
295
|
+
"""Register CREATESONLINE startup event handler"""
|
|
296
|
+
self._startup_handlers.append(func)
|
|
297
|
+
return func
|
|
298
|
+
|
|
299
|
+
def on_shutdown(self, func: Callable) -> Callable:
|
|
300
|
+
"""Register CREATESONLINE shutdown event handler"""
|
|
301
|
+
self._shutdown_handlers.append(func)
|
|
302
|
+
return func
|
|
303
|
+
|
|
304
|
+
def get_routes(self) -> List[str]:
|
|
305
|
+
"""Get list of all registered routes"""
|
|
306
|
+
routes = set()
|
|
307
|
+
for route_key in self._internal_routes.keys():
|
|
308
|
+
# Extract path from "METHOD:path" format
|
|
309
|
+
if ':' in route_key:
|
|
310
|
+
path = route_key.split(':', 1)[1]
|
|
311
|
+
routes.add(path)
|
|
312
|
+
return sorted(list(routes))
|
|
313
|
+
|
|
314
|
+
# ========================================
|
|
315
|
+
# CREATESONLINE AI FEATURES
|
|
316
|
+
# ========================================
|
|
317
|
+
|
|
318
|
+
def enable_ai_features(self, features: List[str]) -> 'CreatesonlineApp':
|
|
319
|
+
"""Enable AI features in CREATESONLINE application"""
|
|
320
|
+
for feature in features:
|
|
321
|
+
if feature not in self._ai_features:
|
|
322
|
+
self._ai_features.append(feature)
|
|
323
|
+
|
|
324
|
+
# Setup feature-specific routes
|
|
325
|
+
self._setup_ai_feature_routes(feature)
|
|
326
|
+
|
|
327
|
+
return self
|
|
328
|
+
|
|
329
|
+
def _setup_ai_feature_routes(self, feature: str):
|
|
330
|
+
"""Setup AI feature-specific routes"""
|
|
331
|
+
if feature == "smart_query":
|
|
332
|
+
@self.get("/ai/query/{model:str}")
|
|
333
|
+
async def smart_query_endpoint(request):
|
|
334
|
+
return await self._smart_query_handler(request)
|
|
335
|
+
|
|
336
|
+
elif feature == "content_generation":
|
|
337
|
+
@self.post("/ai/generate")
|
|
338
|
+
async def content_generation_endpoint(request):
|
|
339
|
+
return await self._content_generation_handler(request)
|
|
340
|
+
|
|
341
|
+
elif feature == "vector_search":
|
|
342
|
+
@self.get("/ai/search")
|
|
343
|
+
async def vector_search_endpoint(request):
|
|
344
|
+
return await self._vector_search_handler(request)
|
|
345
|
+
|
|
346
|
+
elif feature == "model_serving":
|
|
347
|
+
@self.post("/ai/predict")
|
|
348
|
+
async def model_prediction_endpoint(request):
|
|
349
|
+
return await self._model_prediction_handler(request)
|
|
350
|
+
|
|
351
|
+
elif feature == "admin_ai":
|
|
352
|
+
@self.get("/admin/ai")
|
|
353
|
+
async def admin_ai_dashboard(request):
|
|
354
|
+
return await self._admin_ai_handler(request)
|
|
355
|
+
|
|
356
|
+
async def _smart_query_handler(self, request):
|
|
357
|
+
"""Handle smart query requests"""
|
|
358
|
+
model = request.path_params.get('model', 'unknown')
|
|
359
|
+
return {
|
|
360
|
+
"framework": "CREATESONLINE",
|
|
361
|
+
"feature": "smart_query",
|
|
362
|
+
"mode": "internal",
|
|
363
|
+
"model": model,
|
|
364
|
+
"result": f"Smart query processed with CREATESONLINE AI using model: {model}"
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async def _content_generation_handler(self, request):
|
|
368
|
+
"""Handle content generation requests"""
|
|
369
|
+
return {
|
|
370
|
+
"framework": "CREATESONLINE",
|
|
371
|
+
"feature": "content_generation",
|
|
372
|
+
"mode": "internal",
|
|
373
|
+
"generated_content": "CREATESONLINE AI generated content"
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
async def _vector_search_handler(self, request):
|
|
377
|
+
"""Handle vector search requests"""
|
|
378
|
+
return {
|
|
379
|
+
"framework": "CREATESONLINE",
|
|
380
|
+
"feature": "vector_search",
|
|
381
|
+
"mode": "internal",
|
|
382
|
+
"results": []
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
async def _model_prediction_handler(self, request):
|
|
386
|
+
"""Handle model prediction requests"""
|
|
387
|
+
return {
|
|
388
|
+
"framework": "CREATESONLINE",
|
|
389
|
+
"feature": "model_serving",
|
|
390
|
+
"mode": "internal",
|
|
391
|
+
"prediction": 0.87
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async def _admin_ai_handler(self, request):
|
|
395
|
+
"""Handle admin AI dashboard requests"""
|
|
396
|
+
return {
|
|
397
|
+
"framework": "CREATESONLINE",
|
|
398
|
+
"feature": "admin_ai",
|
|
399
|
+
"mode": "internal",
|
|
400
|
+
"ai_dashboard": "AI-enhanced admin interface"
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
# ========================================
|
|
404
|
+
# ASGI INTERFACE
|
|
405
|
+
# ========================================
|
|
406
|
+
|
|
407
|
+
async def __call__(self, scope, receive, send):
|
|
408
|
+
"""
|
|
409
|
+
CREATESONLINE ASGI callable interface
|
|
410
|
+
|
|
411
|
+
Pure internal ASGI implementation
|
|
412
|
+
"""
|
|
413
|
+
# Execute startup handlers if this is the first request
|
|
414
|
+
if not getattr(self, '_startup_executed', False):
|
|
415
|
+
for handler in self._startup_handlers:
|
|
416
|
+
await handler()
|
|
417
|
+
self._startup_executed = True
|
|
418
|
+
|
|
419
|
+
# Use internal ASGI implementation
|
|
420
|
+
await self._internal_asgi_handler(scope, receive, send)
|
|
421
|
+
|
|
422
|
+
async def _internal_asgi_handler(self, scope, receive, send):
|
|
423
|
+
"""Internal ASGI handler for zero-dependency mode"""
|
|
424
|
+
|
|
425
|
+
if scope['type'] == 'http':
|
|
426
|
+
await self._handle_internal_http(scope, receive, send)
|
|
427
|
+
elif scope['type'] == 'websocket':
|
|
428
|
+
await self._handle_internal_websocket(scope, receive, send)
|
|
429
|
+
|
|
430
|
+
async def _handle_internal_http(self, scope, receive, send):
|
|
431
|
+
"""Handle HTTP requests with internal router"""
|
|
432
|
+
|
|
433
|
+
# On first request, discover routes from routes.py to override decorators
|
|
434
|
+
if getattr(self, '_needs_route_discovery', False):
|
|
435
|
+
try:
|
|
436
|
+
from createsonline.project_init import auto_discover_routes
|
|
437
|
+
auto_discover_routes(self)
|
|
438
|
+
self._needs_route_discovery = False
|
|
439
|
+
except Exception:
|
|
440
|
+
self._needs_route_discovery = False
|
|
441
|
+
|
|
442
|
+
path = scope['path']
|
|
443
|
+
method = scope['method']
|
|
444
|
+
|
|
445
|
+
# Serve static files first (favicon, logo, icons, CSS, JS, images)
|
|
446
|
+
if method == 'GET' and (
|
|
447
|
+
path.startswith('/icons/') or
|
|
448
|
+
path.startswith('/static/') or
|
|
449
|
+
path in ['/favicon.ico', '/site.webmanifest'] or
|
|
450
|
+
path.endswith(('.png', '.webp', '.jpg', '.jpeg', '.css', '.js', '.svg', '.ico'))
|
|
451
|
+
):
|
|
452
|
+
await self._serve_static_file(path, scope, receive, send)
|
|
453
|
+
return
|
|
454
|
+
|
|
455
|
+
route_key = f"{method}:{path}"
|
|
456
|
+
|
|
457
|
+
# Create internal request object
|
|
458
|
+
request = CreatesonlineInternalRequest(scope, receive)
|
|
459
|
+
|
|
460
|
+
try:
|
|
461
|
+
# Find matching route - first try exact match
|
|
462
|
+
handler = None
|
|
463
|
+
path_params = {}
|
|
464
|
+
|
|
465
|
+
if route_key in self._internal_routes:
|
|
466
|
+
handler = self._internal_routes[route_key]
|
|
467
|
+
else:
|
|
468
|
+
# Try pattern matching for path parameters
|
|
469
|
+
handler, path_params = self._match_parametric_route(method, path)
|
|
470
|
+
|
|
471
|
+
if handler:
|
|
472
|
+
# Add path parameters to the request
|
|
473
|
+
request.path_params.update(path_params)
|
|
474
|
+
response_data = await handler(request)
|
|
475
|
+
else:
|
|
476
|
+
# 404 handler
|
|
477
|
+
accept_header = getattr(request, 'headers', {}).get('accept', '')
|
|
478
|
+
user_agent = getattr(request, 'headers', {}).get('user-agent', '')
|
|
479
|
+
|
|
480
|
+
# If browser request, return beautiful HTML error page
|
|
481
|
+
if ('text/html' in accept_header or 'Mozilla' in user_agent):
|
|
482
|
+
error_html = self._generate_error_page(
|
|
483
|
+
status_code=404,
|
|
484
|
+
error_message="The requested page could not be found.",
|
|
485
|
+
path=path,
|
|
486
|
+
method=method,
|
|
487
|
+
details="This endpoint is not available or may have been moved."
|
|
488
|
+
)
|
|
489
|
+
await send({
|
|
490
|
+
'type': 'http.response.start',
|
|
491
|
+
'status': 404,
|
|
492
|
+
'headers': [
|
|
493
|
+
[b'content-type', b'text/html; charset=utf-8'],
|
|
494
|
+
[b'content-length', str(len(error_html.encode())).encode()],
|
|
495
|
+
]
|
|
496
|
+
})
|
|
497
|
+
await send({
|
|
498
|
+
'type': 'http.response.body',
|
|
499
|
+
'body': error_html.encode()
|
|
500
|
+
})
|
|
501
|
+
return
|
|
502
|
+
|
|
503
|
+
# Otherwise return JSON error
|
|
504
|
+
response_data = {
|
|
505
|
+
"error": "Not Found",
|
|
506
|
+
"path": path,
|
|
507
|
+
"method": method,
|
|
508
|
+
"framework": "CREATESONLINE",
|
|
509
|
+
"mode": "internal",
|
|
510
|
+
"available_routes": list(self._internal_routes.keys())
|
|
511
|
+
}
|
|
512
|
+
status = 404
|
|
513
|
+
|
|
514
|
+
# Send response
|
|
515
|
+
await self._send_internal_response(send, response_data, status=locals().get('status', 200))
|
|
516
|
+
|
|
517
|
+
except Exception as e:
|
|
518
|
+
# Error handling
|
|
519
|
+
accept_header = getattr(request, 'headers', {}).get('accept', '')
|
|
520
|
+
user_agent = getattr(request, 'headers', {}).get('user-agent', '')
|
|
521
|
+
|
|
522
|
+
# If browser request, return beautiful HTML error page
|
|
523
|
+
if ('text/html' in accept_header or 'Mozilla' in user_agent):
|
|
524
|
+
error_html = self._generate_error_page(
|
|
525
|
+
status_code=500,
|
|
526
|
+
error_message="An internal server error occurred.",
|
|
527
|
+
path=getattr(request, 'path', ''),
|
|
528
|
+
method=getattr(request, 'method', 'GET'),
|
|
529
|
+
details=str(e) if self.debug else "Please try again later or contact support."
|
|
530
|
+
)
|
|
531
|
+
await send({
|
|
532
|
+
'type': 'http.response.start',
|
|
533
|
+
'status': 500,
|
|
534
|
+
'headers': [
|
|
535
|
+
[b'content-type', b'text/html; charset=utf-8'],
|
|
536
|
+
[b'content-length', str(len(error_html.encode())).encode()],
|
|
537
|
+
]
|
|
538
|
+
})
|
|
539
|
+
await send({
|
|
540
|
+
'type': 'http.response.body',
|
|
541
|
+
'body': error_html.encode()
|
|
542
|
+
})
|
|
543
|
+
return
|
|
544
|
+
|
|
545
|
+
# Otherwise return JSON error
|
|
546
|
+
error_data = {
|
|
547
|
+
"error": "Internal Server Error",
|
|
548
|
+
"message": str(e) if self.debug else "Something went wrong",
|
|
549
|
+
"framework": "CREATESONLINE",
|
|
550
|
+
"mode": "internal"
|
|
551
|
+
}
|
|
552
|
+
await self._send_internal_response(send, error_data, status=500)
|
|
553
|
+
|
|
554
|
+
async def _serve_static_file(self, path: str, scope, receive, send):
|
|
555
|
+
"""Serve static files using Django-style STATICFILES_DIRS"""
|
|
556
|
+
from createsonline.static_files import static_handler
|
|
557
|
+
|
|
558
|
+
# Use the static handler which respects STATICFILES_DIRS
|
|
559
|
+
content, status, headers = static_handler.serve_file(path)
|
|
560
|
+
|
|
561
|
+
# Convert headers dict to ASGI format
|
|
562
|
+
asgi_headers = []
|
|
563
|
+
for key, value in headers.items():
|
|
564
|
+
asgi_headers.append([key.lower().encode(), str(value).encode()])
|
|
565
|
+
|
|
566
|
+
# Send response
|
|
567
|
+
await send({
|
|
568
|
+
'type': 'http.response.start',
|
|
569
|
+
'status': status,
|
|
570
|
+
'headers': asgi_headers,
|
|
571
|
+
})
|
|
572
|
+
await send({
|
|
573
|
+
'type': 'http.response.body',
|
|
574
|
+
'body': content,
|
|
575
|
+
})
|
|
576
|
+
|
|
577
|
+
def _match_parametric_route(self, method: str, path: str):
|
|
578
|
+
"""Match path against parametric routes and extract parameters"""
|
|
579
|
+
import re
|
|
580
|
+
|
|
581
|
+
for route_key, handler in self._internal_routes.items():
|
|
582
|
+
stored_method, route_pattern = route_key.split(':', 1)
|
|
583
|
+
|
|
584
|
+
if stored_method != method:
|
|
585
|
+
continue
|
|
586
|
+
|
|
587
|
+
# Check if this route has path parameters
|
|
588
|
+
if '{' not in route_pattern or '}' not in route_pattern:
|
|
589
|
+
continue
|
|
590
|
+
|
|
591
|
+
# Convert path pattern to regex
|
|
592
|
+
# Handle {param} and {param:path} patterns
|
|
593
|
+
regex_pattern = route_pattern
|
|
594
|
+
path_params = {}
|
|
595
|
+
|
|
596
|
+
# Find all parameter patterns
|
|
597
|
+
param_matches = re.findall(r'\{([^}]+)\}', route_pattern)
|
|
598
|
+
|
|
599
|
+
for param_match in param_matches:
|
|
600
|
+
if ':' in param_match:
|
|
601
|
+
# Handle {param_name:path} or {param_name:type} patterns
|
|
602
|
+
param_name, param_type = param_match.split(':', 1)
|
|
603
|
+
if param_type == 'path':
|
|
604
|
+
# Match any path including slashes
|
|
605
|
+
regex_pattern = regex_pattern.replace(f'{{{param_match}}}', f'(?P<{param_name}>.+)')
|
|
606
|
+
else:
|
|
607
|
+
# Match segments without slashes
|
|
608
|
+
regex_pattern = regex_pattern.replace(f'{{{param_match}}}', f'(?P<{param_name}>[^/]+)')
|
|
609
|
+
else:
|
|
610
|
+
# Handle simple {param} patterns - match segments without slashes
|
|
611
|
+
param_name = param_match
|
|
612
|
+
regex_pattern = regex_pattern.replace(f'{{{param_match}}}', f'(?P<{param_name}>[^/]+)')
|
|
613
|
+
|
|
614
|
+
# Try to match the path
|
|
615
|
+
match = re.match(f'^{regex_pattern}$', path)
|
|
616
|
+
if match:
|
|
617
|
+
path_params = match.groupdict()
|
|
618
|
+
return handler, path_params
|
|
619
|
+
|
|
620
|
+
return None, {}
|
|
621
|
+
|
|
622
|
+
async def _handle_internal_websocket(self, scope, receive, send):
|
|
623
|
+
"""Handle WebSocket connections (internal implementation)"""
|
|
624
|
+
await send({'type': 'websocket.accept'})
|
|
625
|
+
|
|
626
|
+
while True:
|
|
627
|
+
message = await receive()
|
|
628
|
+
if message['type'] == 'websocket.disconnect':
|
|
629
|
+
break
|
|
630
|
+
elif message['type'] == 'websocket.receive':
|
|
631
|
+
await send({
|
|
632
|
+
'type': 'websocket.send',
|
|
633
|
+
'text': json.dumps({
|
|
634
|
+
"framework": "CREATESONLINE",
|
|
635
|
+
"mode": "internal",
|
|
636
|
+
"message": "WebSocket connected",
|
|
637
|
+
"received": message.get('text', '')
|
|
638
|
+
})
|
|
639
|
+
})
|
|
640
|
+
|
|
641
|
+
async def _send_internal_response(self, send, data, status=200):
|
|
642
|
+
"""Send response using internal ASGI implementation"""
|
|
643
|
+
|
|
644
|
+
response_headers = {}
|
|
645
|
+
|
|
646
|
+
# Handle different response types
|
|
647
|
+
if hasattr(data, 'content') and hasattr(data, 'status_code'):
|
|
648
|
+
# Handle Response objects (like FileResponse, HTMLResponse, etc.)
|
|
649
|
+
content = data.content
|
|
650
|
+
status = getattr(data, 'status_code', status)
|
|
651
|
+
response_headers = getattr(data, 'headers', {})
|
|
652
|
+
|
|
653
|
+
if isinstance(content, bytes):
|
|
654
|
+
response_body = content
|
|
655
|
+
elif isinstance(content, str):
|
|
656
|
+
response_body = content.encode('utf-8')
|
|
657
|
+
elif isinstance(content, (dict, list)):
|
|
658
|
+
response_body = json.dumps(content, indent=2 if self.debug else None).encode('utf-8')
|
|
659
|
+
else:
|
|
660
|
+
response_body = str(content).encode('utf-8')
|
|
661
|
+
elif isinstance(data, dict) or isinstance(data, list):
|
|
662
|
+
# JSON response
|
|
663
|
+
response_body = json.dumps(data, indent=2 if self.debug else None).encode('utf-8')
|
|
664
|
+
elif isinstance(data, str):
|
|
665
|
+
# Text/HTML response
|
|
666
|
+
response_body = data.encode('utf-8')
|
|
667
|
+
else:
|
|
668
|
+
# Fallback
|
|
669
|
+
response_body = str(data).encode('utf-8')
|
|
670
|
+
|
|
671
|
+
# Build headers list
|
|
672
|
+
headers = [
|
|
673
|
+
[b'content-length', str(len(response_body)).encode()],
|
|
674
|
+
[b'x-framework', b'CREATESONLINE'],
|
|
675
|
+
[b'x-version', self.version.encode()],
|
|
676
|
+
[b'x-mode', b'internal'],
|
|
677
|
+
]
|
|
678
|
+
|
|
679
|
+
# Add response-specific headers first (from Response object)
|
|
680
|
+
for key, value in response_headers.items():
|
|
681
|
+
if isinstance(key, str):
|
|
682
|
+
key = key.lower().encode()
|
|
683
|
+
if isinstance(value, str):
|
|
684
|
+
value = value.encode()
|
|
685
|
+
headers.append([key, value])
|
|
686
|
+
|
|
687
|
+
# Add default content-type if not set by Response object
|
|
688
|
+
content_type_set = any(header[0] == b'content-type' for header in headers)
|
|
689
|
+
if not content_type_set:
|
|
690
|
+
if isinstance(data, dict) or isinstance(data, list):
|
|
691
|
+
headers.append([b'content-type', b'application/json'])
|
|
692
|
+
elif isinstance(data, str) and data.strip().startswith('<'):
|
|
693
|
+
headers.append([b'content-type', b'text/html'])
|
|
694
|
+
else:
|
|
695
|
+
headers.append([b'content-type', b'text/plain'])
|
|
696
|
+
|
|
697
|
+
# Add CORS headers
|
|
698
|
+
if self._cors_origins and "*" in self._cors_origins:
|
|
699
|
+
headers.extend([
|
|
700
|
+
[b'access-control-allow-origin', b'*'],
|
|
701
|
+
[b'access-control-allow-methods', b'GET, POST, PUT, DELETE, OPTIONS'],
|
|
702
|
+
[b'access-control-allow-headers', b'*'],
|
|
703
|
+
])
|
|
704
|
+
|
|
705
|
+
await send({
|
|
706
|
+
'type': 'http.response.start',
|
|
707
|
+
'status': status,
|
|
708
|
+
'headers': headers,
|
|
709
|
+
})
|
|
710
|
+
|
|
711
|
+
await send({
|
|
712
|
+
'type': 'http.response.body',
|
|
713
|
+
'body': response_body,
|
|
714
|
+
})
|
|
715
|
+
|
|
716
|
+
# ========================================
|
|
717
|
+
# CREATESONLINE UTILITIES
|
|
718
|
+
# ========================================
|
|
719
|
+
|
|
720
|
+
def get_routes_info(self) -> List[Dict[str, Any]]:
|
|
721
|
+
"""Get information about CREATESONLINE routes"""
|
|
722
|
+
routes_info = []
|
|
723
|
+
for route_key in self._internal_routes.keys():
|
|
724
|
+
method, path = route_key.split(':', 1)
|
|
725
|
+
routes_info.append({
|
|
726
|
+
"path": path,
|
|
727
|
+
"method": method,
|
|
728
|
+
"framework": "CREATESONLINE",
|
|
729
|
+
"mode": "internal"
|
|
730
|
+
})
|
|
731
|
+
return routes_info
|
|
732
|
+
|
|
733
|
+
def get_ai_config(self) -> Dict[str, Any]:
|
|
734
|
+
"""Get current CREATESONLINE AI configuration"""
|
|
735
|
+
return {
|
|
736
|
+
"framework": "CREATESONLINE",
|
|
737
|
+
"mode": "internal",
|
|
738
|
+
"config": self.ai_config,
|
|
739
|
+
"enabled_features": self._ai_features,
|
|
740
|
+
"feature_count": len(self._ai_features),
|
|
741
|
+
"external_ai_available": self._check_external_ai()
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
def _generate_beautiful_api_docs(self):
|
|
745
|
+
"""Generate beautiful HTML API documentation with dynamic backend data"""
|
|
746
|
+
import json
|
|
747
|
+
from datetime import datetime
|
|
748
|
+
import platform
|
|
749
|
+
import sys
|
|
750
|
+
|
|
751
|
+
# Get comprehensive API spec data with real backend info
|
|
752
|
+
spec = {
|
|
753
|
+
"openapi": "3.0.0",
|
|
754
|
+
"info": {
|
|
755
|
+
"title": self.title,
|
|
756
|
+
"description": self.description,
|
|
757
|
+
"version": self.version,
|
|
758
|
+
"x-framework": "CREATESONLINE",
|
|
759
|
+
"x-ai-enabled": len(self._ai_features) > 0,
|
|
760
|
+
"x-mode": "internal",
|
|
761
|
+
"x-timestamp": datetime.utcnow().isoformat(),
|
|
762
|
+
"x-python-version": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
|
|
763
|
+
"x-platform": platform.system(),
|
|
764
|
+
"x-architecture": platform.machine()
|
|
765
|
+
},
|
|
766
|
+
"servers": [
|
|
767
|
+
{
|
|
768
|
+
"url": "/",
|
|
769
|
+
"description": "CREATESONLINE Development Server",
|
|
770
|
+
"variables": {
|
|
771
|
+
"protocol": {"default": "http", "enum": ["http", "https"]},
|
|
772
|
+
"host": {"default": "127.0.0.1:8000"}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
],
|
|
776
|
+
"paths": self._generate_enhanced_api_paths(),
|
|
777
|
+
"components": {
|
|
778
|
+
"schemas": self._generate_api_schemas(),
|
|
779
|
+
"securitySchemes": {
|
|
780
|
+
"ApiKeyAuth": {
|
|
781
|
+
"type": "apiKey",
|
|
782
|
+
"in": "header",
|
|
783
|
+
"name": "X-API-Key"
|
|
784
|
+
},
|
|
785
|
+
"BearerAuth": {
|
|
786
|
+
"type": "http",
|
|
787
|
+
"scheme": "bearer"
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
},
|
|
791
|
+
"x-system-info": {
|
|
792
|
+
"framework": "CREATESONLINE",
|
|
793
|
+
"mode": "AI-Native",
|
|
794
|
+
"features": self._ai_features,
|
|
795
|
+
"total_routes": len(self._internal_routes),
|
|
796
|
+
"ai_routes": len([r for r in self._internal_routes.keys() if 'ai' in r.lower()]),
|
|
797
|
+
"admin_routes": len([r for r in self._internal_routes.keys() if 'admin' in r.lower()]),
|
|
798
|
+
"startup_time": datetime.utcnow().isoformat(),
|
|
799
|
+
"health_status": "operational",
|
|
800
|
+
"debug_mode": self.debug
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
# Advanced HTML template with dynamic backend data
|
|
805
|
+
html_content = f"""
|
|
806
|
+
<!DOCTYPE html>
|
|
807
|
+
<html lang="en">
|
|
808
|
+
<head>
|
|
809
|
+
<meta charset="UTF-8">
|
|
810
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
811
|
+
<title>{self.title} - API Explorer</title>
|
|
812
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
813
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
814
|
+
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
|
|
815
|
+
<style>
|
|
816
|
+
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
|
|
817
|
+
|
|
818
|
+
:root {{
|
|
819
|
+
/* CREATESONLINE Brand Colors */
|
|
820
|
+
--primary: #000000;
|
|
821
|
+
--secondary: #ffffff;
|
|
822
|
+
--accent: #6366f1;
|
|
823
|
+
--accent-hover: #4f46e5;
|
|
824
|
+
|
|
825
|
+
/* Gray Scale */
|
|
826
|
+
--gray-50: #fafafa;
|
|
827
|
+
--gray-100: #f5f5f5;
|
|
828
|
+
--gray-200: #e5e5e5;
|
|
829
|
+
--gray-300: #d4d4d4;
|
|
830
|
+
--gray-400: #a3a3a3;
|
|
831
|
+
--gray-500: #737373;
|
|
832
|
+
--gray-600: #525252;
|
|
833
|
+
--gray-700: #404040;
|
|
834
|
+
--gray-800: #262626;
|
|
835
|
+
--gray-900: #171717;
|
|
836
|
+
|
|
837
|
+
/* Status Colors */
|
|
838
|
+
--success: #10b981;
|
|
839
|
+
--success-bg: #ecfdf5;
|
|
840
|
+
--warning: #f59e0b;
|
|
841
|
+
--warning-bg: #fffbeb;
|
|
842
|
+
--error: #ef4444;
|
|
843
|
+
--error-bg: #fef2f2;
|
|
844
|
+
--info: #3b82f6;
|
|
845
|
+
--info-bg: #eff6ff;
|
|
846
|
+
|
|
847
|
+
/* Shadows */
|
|
848
|
+
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
|
849
|
+
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
|
850
|
+
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
|
851
|
+
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
|
|
852
|
+
|
|
853
|
+
/* Transitions */
|
|
854
|
+
--transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
855
|
+
--transition-slow: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
856
|
+
}}
|
|
857
|
+
|
|
858
|
+
html {{ scroll-behavior: smooth; }}
|
|
859
|
+
|
|
860
|
+
@keyframes rotate-pulse {{
|
|
861
|
+
0% {{ transform: rotate(0deg) scale(1); }}
|
|
862
|
+
25% {{ transform: rotate(10deg) scale(1.1); }}
|
|
863
|
+
50% {{ transform: rotate(0deg) scale(1); }}
|
|
864
|
+
75% {{ transform: rotate(-10deg) scale(1.1); }}
|
|
865
|
+
100% {{ transform: rotate(0deg) scale(1); }}
|
|
866
|
+
}}
|
|
867
|
+
|
|
868
|
+
body {{
|
|
869
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
870
|
+
background: linear-gradient(135deg, #000000 0%, #ffffff 100%);
|
|
871
|
+
color: var(--gray-900);
|
|
872
|
+
line-height: 1.6;
|
|
873
|
+
min-height: 100vh;
|
|
874
|
+
}}
|
|
875
|
+
|
|
876
|
+
/* Header Section */
|
|
877
|
+
.header {{
|
|
878
|
+
background: linear-gradient(135deg, #000000 0%, #ffffff 100%);
|
|
879
|
+
color: var(--primary);
|
|
880
|
+
padding: 4rem 0 3rem;
|
|
881
|
+
position: relative;
|
|
882
|
+
overflow: hidden;
|
|
883
|
+
}}
|
|
884
|
+
|
|
885
|
+
.header::before {{
|
|
886
|
+
content: '';
|
|
887
|
+
position: absolute;
|
|
888
|
+
top: 0; left: 0; right: 0; bottom: 0;
|
|
889
|
+
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse"><path d="M 20 0 L 0 0 0 20" fill="none" stroke="rgba(255,255,255,0.05)" stroke-width="1"/></pattern></defs><rect width="100" height="100" fill="url(%23grid)"/></svg>');
|
|
890
|
+
opacity: 0.7;
|
|
891
|
+
}}
|
|
892
|
+
|
|
893
|
+
.header-content {{
|
|
894
|
+
position: relative;
|
|
895
|
+
z-index: 2;
|
|
896
|
+
text-align: center;
|
|
897
|
+
}}
|
|
898
|
+
|
|
899
|
+
.header h1 {{
|
|
900
|
+
font-size: clamp(2.5rem, 5vw, 4rem);
|
|
901
|
+
font-weight: 900;
|
|
902
|
+
margin-bottom: 0.5rem;
|
|
903
|
+
background: linear-gradient(45deg, #ffffff, #f8fafc);
|
|
904
|
+
-webkit-background-clip: text;
|
|
905
|
+
-webkit-text-fill-color: transparent;
|
|
906
|
+
background-clip: text;
|
|
907
|
+
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
|
908
|
+
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
|
|
909
|
+
}}
|
|
910
|
+
|
|
911
|
+
.header-description {{
|
|
912
|
+
font-size: 1.25rem;
|
|
913
|
+
color: #ffffff;
|
|
914
|
+
opacity: 0.95;
|
|
915
|
+
margin-bottom: 2rem;
|
|
916
|
+
max-width: 600px;
|
|
917
|
+
margin-left: auto;
|
|
918
|
+
margin-right: auto;
|
|
919
|
+
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
|
|
920
|
+
}}
|
|
921
|
+
|
|
922
|
+
.header-badges {{
|
|
923
|
+
display: flex;
|
|
924
|
+
justify-content: center;
|
|
925
|
+
gap: 1rem;
|
|
926
|
+
flex-wrap: wrap;
|
|
927
|
+
margin-bottom: 2rem;
|
|
928
|
+
}}
|
|
929
|
+
|
|
930
|
+
.badge {{
|
|
931
|
+
padding: 0.5rem 1rem;
|
|
932
|
+
border-radius: 50px;
|
|
933
|
+
font-size: 0.875rem;
|
|
934
|
+
font-weight: 600;
|
|
935
|
+
backdrop-filter: blur(10px);
|
|
936
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
937
|
+
}}
|
|
938
|
+
|
|
939
|
+
.badge-version {{ background: rgba(16, 185, 129, 0.9); }}
|
|
940
|
+
.badge-ai {{ background: rgba(59, 130, 246, 0.9); }}
|
|
941
|
+
.badge-routes {{ background: rgba(168, 85, 247, 0.9); }}
|
|
942
|
+
|
|
943
|
+
/* Navigation Hover Effects */
|
|
944
|
+
.nav-link:hover {{
|
|
945
|
+
transform: translateY(-2px);
|
|
946
|
+
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3) !important;
|
|
947
|
+
background: rgba(255, 255, 255, 0.15) !important;
|
|
948
|
+
border-color: rgba(255, 255, 255, 0.4) !important;
|
|
949
|
+
}}
|
|
950
|
+
|
|
951
|
+
.nav-active:hover {{
|
|
952
|
+
transform: translateY(-2px);
|
|
953
|
+
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.4) !important;
|
|
954
|
+
background: #f8fafc !important;
|
|
955
|
+
}}
|
|
956
|
+
|
|
957
|
+
.nav-link:active {{
|
|
958
|
+
transform: translateY(0px);
|
|
959
|
+
}}
|
|
960
|
+
|
|
961
|
+
.system-stats {{
|
|
962
|
+
display: grid;
|
|
963
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
964
|
+
gap: 1rem;
|
|
965
|
+
max-width: 800px;
|
|
966
|
+
margin: 0 auto;
|
|
967
|
+
}}
|
|
968
|
+
|
|
969
|
+
.stat-card {{
|
|
970
|
+
background: rgba(255, 255, 255, 0.1);
|
|
971
|
+
backdrop-filter: blur(10px);
|
|
972
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
973
|
+
border-radius: 1rem;
|
|
974
|
+
padding: 1.5rem;
|
|
975
|
+
text-align: center;
|
|
976
|
+
}}
|
|
977
|
+
|
|
978
|
+
.stat-value {{
|
|
979
|
+
font-size: 2rem;
|
|
980
|
+
font-weight: 800;
|
|
981
|
+
color: #ffffff;
|
|
982
|
+
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
|
|
983
|
+
}}
|
|
984
|
+
|
|
985
|
+
.stat-label {{
|
|
986
|
+
font-size: 0.875rem;
|
|
987
|
+
color: #ffffff;
|
|
988
|
+
opacity: 0.9;
|
|
989
|
+
margin-top: 0.5rem;
|
|
990
|
+
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
|
991
|
+
}}
|
|
992
|
+
|
|
993
|
+
/* Main Content */
|
|
994
|
+
.container {{
|
|
995
|
+
max-width: 1400px;
|
|
996
|
+
margin: 0 auto;
|
|
997
|
+
padding: 0 2rem;
|
|
998
|
+
}}
|
|
999
|
+
|
|
1000
|
+
.api-layout {{
|
|
1001
|
+
display: grid;
|
|
1002
|
+
grid-template-columns: 350px 1fr;
|
|
1003
|
+
gap: 2rem;
|
|
1004
|
+
margin: 2rem 0;
|
|
1005
|
+
min-height: calc(100vh - 400px);
|
|
1006
|
+
}}
|
|
1007
|
+
|
|
1008
|
+
/* Sidebar */
|
|
1009
|
+
.sidebar {{
|
|
1010
|
+
background: var(--secondary);
|
|
1011
|
+
border-radius: 1rem;
|
|
1012
|
+
box-shadow: var(--shadow-lg);
|
|
1013
|
+
position: sticky;
|
|
1014
|
+
top: 2rem;
|
|
1015
|
+
height: fit-content;
|
|
1016
|
+
max-height: calc(100vh - 4rem);
|
|
1017
|
+
overflow: hidden;
|
|
1018
|
+
border: 1px solid var(--gray-200);
|
|
1019
|
+
}}
|
|
1020
|
+
|
|
1021
|
+
.sidebar-header {{
|
|
1022
|
+
background: linear-gradient(135deg, var(--primary), var(--gray-800));
|
|
1023
|
+
color: var(--secondary);
|
|
1024
|
+
padding: 1.5rem;
|
|
1025
|
+
font-weight: 700;
|
|
1026
|
+
font-size: 1.125rem;
|
|
1027
|
+
display: flex;
|
|
1028
|
+
align-items: center;
|
|
1029
|
+
gap: 0.75rem;
|
|
1030
|
+
}}
|
|
1031
|
+
|
|
1032
|
+
.sidebar-content {{
|
|
1033
|
+
max-height: calc(100vh - 8rem);
|
|
1034
|
+
overflow-y: auto;
|
|
1035
|
+
}}
|
|
1036
|
+
|
|
1037
|
+
.endpoint-filters {{
|
|
1038
|
+
padding: 1rem;
|
|
1039
|
+
border-bottom: 1px solid var(--gray-200);
|
|
1040
|
+
}}
|
|
1041
|
+
|
|
1042
|
+
.filter-tabs {{
|
|
1043
|
+
display: flex;
|
|
1044
|
+
gap: 0.5rem;
|
|
1045
|
+
flex-wrap: wrap;
|
|
1046
|
+
}}
|
|
1047
|
+
|
|
1048
|
+
.filter-tab {{
|
|
1049
|
+
padding: 0.5rem 1rem;
|
|
1050
|
+
border: none;
|
|
1051
|
+
background: var(--gray-100);
|
|
1052
|
+
color: var(--gray-600);
|
|
1053
|
+
border-radius: 0.5rem;
|
|
1054
|
+
cursor: pointer;
|
|
1055
|
+
font-size: 0.875rem;
|
|
1056
|
+
font-weight: 500;
|
|
1057
|
+
transition: var(--transition);
|
|
1058
|
+
}}
|
|
1059
|
+
|
|
1060
|
+
.filter-tab.active {{
|
|
1061
|
+
background: var(--primary);
|
|
1062
|
+
color: var(--secondary);
|
|
1063
|
+
}}
|
|
1064
|
+
|
|
1065
|
+
.endpoint-list {{
|
|
1066
|
+
list-style: none;
|
|
1067
|
+
padding: 0;
|
|
1068
|
+
}}
|
|
1069
|
+
|
|
1070
|
+
.endpoint-group {{
|
|
1071
|
+
border-bottom: 1px solid var(--gray-100);
|
|
1072
|
+
}}
|
|
1073
|
+
|
|
1074
|
+
.endpoint-group-title {{
|
|
1075
|
+
padding: 1rem;
|
|
1076
|
+
font-weight: 600;
|
|
1077
|
+
color: var(--gray-700);
|
|
1078
|
+
background: var(--gray-50);
|
|
1079
|
+
font-size: 0.875rem;
|
|
1080
|
+
text-transform: uppercase;
|
|
1081
|
+
letter-spacing: 0.05em;
|
|
1082
|
+
}}
|
|
1083
|
+
|
|
1084
|
+
.endpoint-item {{
|
|
1085
|
+
border-bottom: 1px solid var(--gray-100);
|
|
1086
|
+
cursor: pointer;
|
|
1087
|
+
transition: var(--transition);
|
|
1088
|
+
position: relative;
|
|
1089
|
+
}}
|
|
1090
|
+
|
|
1091
|
+
.endpoint-item:hover {{
|
|
1092
|
+
background: var(--gray-50);
|
|
1093
|
+
}}
|
|
1094
|
+
|
|
1095
|
+
.endpoint-item.active {{
|
|
1096
|
+
background: var(--primary);
|
|
1097
|
+
color: var(--secondary);
|
|
1098
|
+
}}
|
|
1099
|
+
|
|
1100
|
+
.endpoint-item.active::before {{
|
|
1101
|
+
content: '';
|
|
1102
|
+
position: absolute;
|
|
1103
|
+
left: 0;
|
|
1104
|
+
top: 0;
|
|
1105
|
+
bottom: 0;
|
|
1106
|
+
width: 4px;
|
|
1107
|
+
background: var(--accent);
|
|
1108
|
+
}}
|
|
1109
|
+
|
|
1110
|
+
.endpoint-link {{
|
|
1111
|
+
display: flex;
|
|
1112
|
+
align-items: center;
|
|
1113
|
+
padding: 1rem;
|
|
1114
|
+
text-decoration: none;
|
|
1115
|
+
color: inherit;
|
|
1116
|
+
gap: 1rem;
|
|
1117
|
+
}}
|
|
1118
|
+
|
|
1119
|
+
.method-badge {{
|
|
1120
|
+
display: inline-flex;
|
|
1121
|
+
align-items: center;
|
|
1122
|
+
justify-content: center;
|
|
1123
|
+
padding: 0.25rem 0.75rem;
|
|
1124
|
+
font-size: 0.75rem;
|
|
1125
|
+
font-weight: 700;
|
|
1126
|
+
font-family: 'JetBrains Mono', monospace;
|
|
1127
|
+
border-radius: 0.375rem;
|
|
1128
|
+
min-width: 70px;
|
|
1129
|
+
text-align: center;
|
|
1130
|
+
border: 2px solid;
|
|
1131
|
+
}}
|
|
1132
|
+
|
|
1133
|
+
.method-get {{ background: var(--success); color: white; border-color: var(--success); }}
|
|
1134
|
+
.method-post {{ background: var(--info); color: white; border-color: var(--info); }}
|
|
1135
|
+
.method-put {{ background: var(--warning); color: white; border-color: var(--warning); }}
|
|
1136
|
+
.method-delete {{ background: var(--error); color: white; border-color: var(--error); }}
|
|
1137
|
+
.method-patch {{ background: var(--accent); color: white; border-color: var(--accent); }}
|
|
1138
|
+
|
|
1139
|
+
.endpoint-path {{
|
|
1140
|
+
font-family: 'JetBrains Mono', monospace;
|
|
1141
|
+
flex: 1;
|
|
1142
|
+
overflow: hidden;
|
|
1143
|
+
text-overflow: ellipsis;
|
|
1144
|
+
white-space: nowrap;
|
|
1145
|
+
}}
|
|
1146
|
+
|
|
1147
|
+
/* Main Content Area */
|
|
1148
|
+
.main-content {{
|
|
1149
|
+
background: var(--secondary);
|
|
1150
|
+
border-radius: 1rem;
|
|
1151
|
+
box-shadow: var(--shadow-lg);
|
|
1152
|
+
overflow: hidden;
|
|
1153
|
+
border: 1px solid var(--gray-200);
|
|
1154
|
+
}}
|
|
1155
|
+
|
|
1156
|
+
.content-header {{
|
|
1157
|
+
background: linear-gradient(135deg, var(--gray-50), var(--gray-100));
|
|
1158
|
+
padding: 2rem;
|
|
1159
|
+
border-bottom: 1px solid var(--gray-200);
|
|
1160
|
+
}}
|
|
1161
|
+
|
|
1162
|
+
.endpoint-title {{
|
|
1163
|
+
font-size: 2.5rem;
|
|
1164
|
+
font-weight: 900;
|
|
1165
|
+
margin-bottom: 1rem;
|
|
1166
|
+
display: flex;
|
|
1167
|
+
align-items: center;
|
|
1168
|
+
gap: 1rem;
|
|
1169
|
+
flex-wrap: wrap;
|
|
1170
|
+
}}
|
|
1171
|
+
|
|
1172
|
+
.endpoint-description {{
|
|
1173
|
+
font-size: 1.125rem;
|
|
1174
|
+
color: var(--gray-600);
|
|
1175
|
+
margin-bottom: 1.5rem;
|
|
1176
|
+
padding: 1.5rem;
|
|
1177
|
+
background: var(--secondary);
|
|
1178
|
+
border-radius: 0.75rem;
|
|
1179
|
+
border-left: 4px solid var(--accent);
|
|
1180
|
+
box-shadow: var(--shadow-sm);
|
|
1181
|
+
}}
|
|
1182
|
+
|
|
1183
|
+
.endpoint-tags {{
|
|
1184
|
+
display: flex;
|
|
1185
|
+
gap: 0.5rem;
|
|
1186
|
+
flex-wrap: wrap;
|
|
1187
|
+
}}
|
|
1188
|
+
|
|
1189
|
+
.tag {{
|
|
1190
|
+
padding: 0.25rem 0.75rem;
|
|
1191
|
+
background: var(--accent);
|
|
1192
|
+
color: var(--secondary);
|
|
1193
|
+
border-radius: 0.375rem;
|
|
1194
|
+
font-size: 0.875rem;
|
|
1195
|
+
font-weight: 500;
|
|
1196
|
+
}}
|
|
1197
|
+
|
|
1198
|
+
.content-body {{
|
|
1199
|
+
padding: 2rem;
|
|
1200
|
+
}}
|
|
1201
|
+
|
|
1202
|
+
.section {{
|
|
1203
|
+
margin-bottom: 3rem;
|
|
1204
|
+
}}
|
|
1205
|
+
|
|
1206
|
+
.section-title {{
|
|
1207
|
+
font-size: 1.5rem;
|
|
1208
|
+
font-weight: 700;
|
|
1209
|
+
margin-bottom: 1rem;
|
|
1210
|
+
display: flex;
|
|
1211
|
+
align-items: center;
|
|
1212
|
+
gap: 0.5rem;
|
|
1213
|
+
padding-bottom: 0.5rem;
|
|
1214
|
+
border-bottom: 2px solid var(--gray-200);
|
|
1215
|
+
}}
|
|
1216
|
+
|
|
1217
|
+
/* Code Examples */
|
|
1218
|
+
.code-examples {{
|
|
1219
|
+
background: var(--gray-50);
|
|
1220
|
+
border-radius: 1rem;
|
|
1221
|
+
overflow: hidden;
|
|
1222
|
+
border: 1px solid var(--gray-200);
|
|
1223
|
+
}}
|
|
1224
|
+
|
|
1225
|
+
.code-tabs {{
|
|
1226
|
+
display: flex;
|
|
1227
|
+
background: var(--gray-100);
|
|
1228
|
+
border-bottom: 1px solid var(--gray-200);
|
|
1229
|
+
}}
|
|
1230
|
+
|
|
1231
|
+
.code-tab {{
|
|
1232
|
+
padding: 1rem 1.5rem;
|
|
1233
|
+
background: none;
|
|
1234
|
+
border: none;
|
|
1235
|
+
cursor: pointer;
|
|
1236
|
+
font-weight: 600;
|
|
1237
|
+
color: var(--gray-600);
|
|
1238
|
+
transition: var(--transition);
|
|
1239
|
+
border-bottom: 3px solid transparent;
|
|
1240
|
+
}}
|
|
1241
|
+
|
|
1242
|
+
.code-tab.active {{
|
|
1243
|
+
background: var(--secondary);
|
|
1244
|
+
color: var(--primary);
|
|
1245
|
+
border-bottom-color: var(--accent);
|
|
1246
|
+
}}
|
|
1247
|
+
|
|
1248
|
+
.code-content {{
|
|
1249
|
+
display: none;
|
|
1250
|
+
position: relative;
|
|
1251
|
+
}}
|
|
1252
|
+
|
|
1253
|
+
.code-content.active {{
|
|
1254
|
+
display: block;
|
|
1255
|
+
}}
|
|
1256
|
+
|
|
1257
|
+
.code-block {{
|
|
1258
|
+
background: var(--gray-900);
|
|
1259
|
+
color: var(--gray-100);
|
|
1260
|
+
padding: 2rem;
|
|
1261
|
+
font-family: 'JetBrains Mono', monospace;
|
|
1262
|
+
font-size: 0.875rem;
|
|
1263
|
+
line-height: 1.6;
|
|
1264
|
+
overflow-x: auto;
|
|
1265
|
+
position: relative;
|
|
1266
|
+
}}
|
|
1267
|
+
|
|
1268
|
+
.copy-btn {{
|
|
1269
|
+
position: absolute;
|
|
1270
|
+
top: 1rem;
|
|
1271
|
+
right: 1rem;
|
|
1272
|
+
background: var(--gray-700);
|
|
1273
|
+
color: var(--gray-100);
|
|
1274
|
+
border: none;
|
|
1275
|
+
padding: 0.5rem 1rem;
|
|
1276
|
+
border-radius: 0.375rem;
|
|
1277
|
+
cursor: pointer;
|
|
1278
|
+
font-size: 0.75rem;
|
|
1279
|
+
font-weight: 600;
|
|
1280
|
+
transition: var(--transition);
|
|
1281
|
+
}}
|
|
1282
|
+
|
|
1283
|
+
.copy-btn:hover {{
|
|
1284
|
+
background: var(--gray-600);
|
|
1285
|
+
}}
|
|
1286
|
+
|
|
1287
|
+
/* Try It Out Section */
|
|
1288
|
+
.try-it-section {{
|
|
1289
|
+
background: linear-gradient(135deg, var(--info-bg), var(--success-bg));
|
|
1290
|
+
border-radius: 1rem;
|
|
1291
|
+
padding: 2rem;
|
|
1292
|
+
border: 1px solid var(--info);
|
|
1293
|
+
margin: 2rem 0;
|
|
1294
|
+
}}
|
|
1295
|
+
|
|
1296
|
+
.try-it-header {{
|
|
1297
|
+
display: flex;
|
|
1298
|
+
align-items: center;
|
|
1299
|
+
justify-content: space-between;
|
|
1300
|
+
margin-bottom: 2rem;
|
|
1301
|
+
flex-wrap: wrap;
|
|
1302
|
+
gap: 1rem;
|
|
1303
|
+
}}
|
|
1304
|
+
|
|
1305
|
+
.try-it-title {{
|
|
1306
|
+
font-size: 1.5rem;
|
|
1307
|
+
font-weight: 700;
|
|
1308
|
+
color: var(--gray-900);
|
|
1309
|
+
display: flex;
|
|
1310
|
+
align-items: center;
|
|
1311
|
+
gap: 0.5rem;
|
|
1312
|
+
}}
|
|
1313
|
+
|
|
1314
|
+
.execute-btn {{
|
|
1315
|
+
background: linear-gradient(135deg, var(--accent), var(--accent-hover));
|
|
1316
|
+
color: var(--secondary);
|
|
1317
|
+
border: none;
|
|
1318
|
+
padding: 1rem 2rem;
|
|
1319
|
+
border-radius: 0.75rem;
|
|
1320
|
+
font-weight: 700;
|
|
1321
|
+
cursor: pointer;
|
|
1322
|
+
transition: var(--transition);
|
|
1323
|
+
font-family: inherit;
|
|
1324
|
+
font-size: 1rem;
|
|
1325
|
+
box-shadow: var(--shadow-md);
|
|
1326
|
+
}}
|
|
1327
|
+
|
|
1328
|
+
.execute-btn:hover {{
|
|
1329
|
+
transform: translateY(-2px);
|
|
1330
|
+
box-shadow: var(--shadow-lg);
|
|
1331
|
+
}}
|
|
1332
|
+
|
|
1333
|
+
.execute-btn:active {{
|
|
1334
|
+
transform: translateY(0);
|
|
1335
|
+
}}
|
|
1336
|
+
|
|
1337
|
+
.execute-btn.loading {{
|
|
1338
|
+
opacity: 0.7;
|
|
1339
|
+
pointer-events: none;
|
|
1340
|
+
}}
|
|
1341
|
+
|
|
1342
|
+
/* Response Section */
|
|
1343
|
+
.response-section {{
|
|
1344
|
+
margin-top: 2rem;
|
|
1345
|
+
border-radius: 1rem;
|
|
1346
|
+
overflow: hidden;
|
|
1347
|
+
border: 1px solid var(--gray-200);
|
|
1348
|
+
box-shadow: var(--shadow-md);
|
|
1349
|
+
}}
|
|
1350
|
+
|
|
1351
|
+
.response-header {{
|
|
1352
|
+
background: var(--gray-100);
|
|
1353
|
+
padding: 1rem 1.5rem;
|
|
1354
|
+
border-bottom: 1px solid var(--gray-200);
|
|
1355
|
+
display: flex;
|
|
1356
|
+
align-items: center;
|
|
1357
|
+
justify-content: space-between;
|
|
1358
|
+
}}
|
|
1359
|
+
|
|
1360
|
+
.status-indicator {{
|
|
1361
|
+
display: inline-flex;
|
|
1362
|
+
align-items: center;
|
|
1363
|
+
gap: 0.5rem;
|
|
1364
|
+
padding: 0.5rem 1rem;
|
|
1365
|
+
border-radius: 50px;
|
|
1366
|
+
font-weight: 600;
|
|
1367
|
+
font-size: 0.875rem;
|
|
1368
|
+
}}
|
|
1369
|
+
|
|
1370
|
+
.status-200 {{ background: var(--success-bg); color: var(--success); border: 1px solid var(--success); }}
|
|
1371
|
+
.status-400 {{ background: var(--error-bg); color: var(--error); border: 1px solid var(--error); }}
|
|
1372
|
+
.status-500 {{ background: var(--error-bg); color: var(--error); border: 1px solid var(--error); }}
|
|
1373
|
+
|
|
1374
|
+
.response-time {{
|
|
1375
|
+
font-family: 'JetBrains Mono', monospace;
|
|
1376
|
+
color: var(--gray-600);
|
|
1377
|
+
font-size: 0.875rem;
|
|
1378
|
+
}}
|
|
1379
|
+
|
|
1380
|
+
/* Welcome Message */
|
|
1381
|
+
.welcome-message {{
|
|
1382
|
+
text-align: center;
|
|
1383
|
+
padding: 4rem 2rem;
|
|
1384
|
+
color: var(--gray-600);
|
|
1385
|
+
}}
|
|
1386
|
+
|
|
1387
|
+
.welcome-message h2 {{
|
|
1388
|
+
font-size: 2.5rem;
|
|
1389
|
+
font-weight: 800;
|
|
1390
|
+
margin-bottom: 1rem;
|
|
1391
|
+
color: var(--gray-800);
|
|
1392
|
+
}}
|
|
1393
|
+
|
|
1394
|
+
.welcome-message p {{
|
|
1395
|
+
font-size: 1.125rem;
|
|
1396
|
+
margin-bottom: 2rem;
|
|
1397
|
+
max-width: 600px;
|
|
1398
|
+
margin-left: auto;
|
|
1399
|
+
margin-right: auto;
|
|
1400
|
+
}}
|
|
1401
|
+
|
|
1402
|
+
.quick-start {{
|
|
1403
|
+
background: var(--gray-50);
|
|
1404
|
+
border-radius: 1rem;
|
|
1405
|
+
padding: 2rem;
|
|
1406
|
+
margin-top: 2rem;
|
|
1407
|
+
border: 1px solid var(--gray-200);
|
|
1408
|
+
}}
|
|
1409
|
+
|
|
1410
|
+
.quick-start h3 {{
|
|
1411
|
+
font-size: 1.25rem;
|
|
1412
|
+
font-weight: 700;
|
|
1413
|
+
margin-bottom: 1rem;
|
|
1414
|
+
color: var(--gray-800);
|
|
1415
|
+
}}
|
|
1416
|
+
|
|
1417
|
+
/* Loading States */
|
|
1418
|
+
.loading-spinner {{
|
|
1419
|
+
display: inline-block;
|
|
1420
|
+
width: 20px;
|
|
1421
|
+
height: 20px;
|
|
1422
|
+
border: 3px solid rgba(255, 255, 255, 0.3);
|
|
1423
|
+
border-radius: 50%;
|
|
1424
|
+
border-top-color: var(--secondary);
|
|
1425
|
+
animation: spin 1s ease-in-out infinite;
|
|
1426
|
+
}}
|
|
1427
|
+
|
|
1428
|
+
@keyframes spin {{
|
|
1429
|
+
to {{ transform: rotate(360deg); }}
|
|
1430
|
+
}}
|
|
1431
|
+
|
|
1432
|
+
/* Responsive Design */
|
|
1433
|
+
@media (max-width: 1024px) {{
|
|
1434
|
+
.api-layout {{
|
|
1435
|
+
grid-template-columns: 1fr;
|
|
1436
|
+
gap: 1rem;
|
|
1437
|
+
}}
|
|
1438
|
+
|
|
1439
|
+
.sidebar {{
|
|
1440
|
+
position: static;
|
|
1441
|
+
max-height: none;
|
|
1442
|
+
}}
|
|
1443
|
+
|
|
1444
|
+
.header h1 {{
|
|
1445
|
+
font-size: 3rem;
|
|
1446
|
+
}}
|
|
1447
|
+
|
|
1448
|
+
.system-stats {{
|
|
1449
|
+
grid-template-columns: repeat(2, 1fr);
|
|
1450
|
+
}}
|
|
1451
|
+
}}
|
|
1452
|
+
|
|
1453
|
+
@media (max-width: 640px) {{
|
|
1454
|
+
.container {{
|
|
1455
|
+
padding: 0 1rem;
|
|
1456
|
+
}}
|
|
1457
|
+
|
|
1458
|
+
.header {{
|
|
1459
|
+
padding: 2rem 0;
|
|
1460
|
+
}}
|
|
1461
|
+
|
|
1462
|
+
.header h1 {{
|
|
1463
|
+
font-size: 2rem;
|
|
1464
|
+
}}
|
|
1465
|
+
|
|
1466
|
+
.header-badges {{
|
|
1467
|
+
flex-direction: column;
|
|
1468
|
+
align-items: center;
|
|
1469
|
+
}}
|
|
1470
|
+
|
|
1471
|
+
.system-stats {{
|
|
1472
|
+
grid-template-columns: 1fr;
|
|
1473
|
+
}}
|
|
1474
|
+
|
|
1475
|
+
.endpoint-title {{
|
|
1476
|
+
font-size: 1.75rem;
|
|
1477
|
+
flex-direction: column;
|
|
1478
|
+
align-items: flex-start;
|
|
1479
|
+
}}
|
|
1480
|
+
|
|
1481
|
+
.try-it-header {{
|
|
1482
|
+
flex-direction: column;
|
|
1483
|
+
align-items: stretch;
|
|
1484
|
+
}}
|
|
1485
|
+
|
|
1486
|
+
.execute-btn {{
|
|
1487
|
+
width: 100%;
|
|
1488
|
+
text-align: center;
|
|
1489
|
+
}}
|
|
1490
|
+
}}
|
|
1491
|
+
</style>
|
|
1492
|
+
</head>
|
|
1493
|
+
<body>
|
|
1494
|
+
<div class="header">
|
|
1495
|
+
<div class="header-nav" style="position: absolute; top: 1rem; right: 2rem; display: flex; gap: 1.5rem; z-index: 3;">
|
|
1496
|
+
<a href="/" class="nav-link" style="color: #ffffff; text-decoration: none; font-weight: 600; padding: 0.5rem 1rem; border-radius: 0.5rem; transition: all 0.3s ease; border: 2px solid rgba(255,255,255,0.2); backdrop-filter: blur(10px); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);">Home</a>
|
|
1497
|
+
<a href="/docs" class="nav-link nav-active" style="color: #000000; text-decoration: none; font-weight: 600; padding: 0.5rem 1rem; border-radius: 0.5rem; transition: all 0.3s ease; background: #ffffff; border: 2px solid #ffffff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);">API Docs</a>
|
|
1498
|
+
<a href="/admin" class="nav-link" style="color: #ffffff; text-decoration: none; font-weight: 600; padding: 0.5rem 1rem; border-radius: 0.5rem; transition: all 0.3s ease; border: 2px solid rgba(255,255,255,0.2); backdrop-filter: blur(10px); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);">Admin</a>
|
|
1499
|
+
<a href="/health" class="nav-link" style="color: #ffffff; text-decoration: none; font-weight: 600; padding: 0.5rem 1rem; border-radius: 0.5rem; transition: all 0.3s ease; border: 2px solid rgba(255,255,255,0.2); backdrop-filter: blur(10px); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);">Health</a>
|
|
1500
|
+
</div>
|
|
1501
|
+
<div class="container">
|
|
1502
|
+
<div class="header-content">
|
|
1503
|
+
<h1><img src="/static/image/favicon.ico" alt="CREATESONLINE" style="width: 48px; height: 48px; margin-right: 1rem; animation: rotate-pulse 3s ease-in-out infinite; vertical-align: middle;" />{self.title}</h1>
|
|
1504
|
+
<p class="header-description">{self.description}</p>
|
|
1505
|
+
|
|
1506
|
+
<div class="header-badges">
|
|
1507
|
+
<div class="badge badge-version">v{self.version}</div>
|
|
1508
|
+
<div class="badge badge-ai"><img src="/static/image/favicon.ico" alt="CREATESONLINE" style="width: 16px; height: 16px; margin-right: 0.5rem; vertical-align: middle;" />AI-Native</div>
|
|
1509
|
+
<div class="badge badge-routes">{spec['x-system-info']['total_routes']} Routes</div>
|
|
1510
|
+
</div>
|
|
1511
|
+
|
|
1512
|
+
<div class="system-stats">
|
|
1513
|
+
<div class="stat-card">
|
|
1514
|
+
<div class="stat-value">{spec['x-system-info']['total_routes']}</div>
|
|
1515
|
+
<div class="stat-label">Total Endpoints</div>
|
|
1516
|
+
</div>
|
|
1517
|
+
<div class="stat-card">
|
|
1518
|
+
<div class="stat-value">{spec['x-system-info']['ai_routes']}</div>
|
|
1519
|
+
<div class="stat-label">AI-Powered</div>
|
|
1520
|
+
</div>
|
|
1521
|
+
<div class="stat-card">
|
|
1522
|
+
<div class="stat-value">{len(spec['x-system-info']['features'])}</div>
|
|
1523
|
+
<div class="stat-label">AI Features</div>
|
|
1524
|
+
</div>
|
|
1525
|
+
<div class="stat-card">
|
|
1526
|
+
<div class="stat-value">{spec['info']['x-python-version']}</div>
|
|
1527
|
+
<div class="stat-label">Python Version</div>
|
|
1528
|
+
</div>
|
|
1529
|
+
</div>
|
|
1530
|
+
</div>
|
|
1531
|
+
</div>
|
|
1532
|
+
</div>
|
|
1533
|
+
|
|
1534
|
+
<div class="container">
|
|
1535
|
+
<div class="api-layout">
|
|
1536
|
+
<aside class="sidebar">
|
|
1537
|
+
<div class="sidebar-header">
|
|
1538
|
+
📋 API Endpoints
|
|
1539
|
+
</div>
|
|
1540
|
+
<div class="sidebar-content">
|
|
1541
|
+
<div class="endpoint-filters">
|
|
1542
|
+
<div class="filter-tabs">
|
|
1543
|
+
<button class="filter-tab active" data-filter="all">All</button>
|
|
1544
|
+
<button class="filter-tab" data-filter="ai">AI</button>
|
|
1545
|
+
<button class="filter-tab" data-filter="admin">Admin</button>
|
|
1546
|
+
<button class="filter-tab" data-filter="system">System</button>
|
|
1547
|
+
</div>
|
|
1548
|
+
</div>
|
|
1549
|
+
<ul class="endpoint-list" id="endpointList">
|
|
1550
|
+
<!-- Dynamic endpoints will be loaded here -->
|
|
1551
|
+
</ul>
|
|
1552
|
+
</div>
|
|
1553
|
+
</aside>
|
|
1554
|
+
|
|
1555
|
+
<main class="main-content">
|
|
1556
|
+
<div id="endpointDetails">
|
|
1557
|
+
<div class="welcome-message">
|
|
1558
|
+
<h2>Welcome to {self.title} API</h2>
|
|
1559
|
+
<p>Explore our AI-native API with intelligent endpoints and real-time testing capabilities. Select an endpoint from the sidebar to get started.</p>
|
|
1560
|
+
|
|
1561
|
+
<div class="quick-start">
|
|
1562
|
+
<h3>🚀 Quick Start</h3>
|
|
1563
|
+
<div class="code-block">
|
|
1564
|
+
curl -X GET "{self._get_base_url()}/" \\
|
|
1565
|
+
-H "Accept: application/json" \\
|
|
1566
|
+
-H "User-Agent: MyApp/1.0"
|
|
1567
|
+
</div>
|
|
1568
|
+
</div>
|
|
1569
|
+
</div>
|
|
1570
|
+
</div>
|
|
1571
|
+
</main>
|
|
1572
|
+
</div>
|
|
1573
|
+
</div>
|
|
1574
|
+
|
|
1575
|
+
<script>
|
|
1576
|
+
// API Specification with all dynamic backend data
|
|
1577
|
+
const apiSpec = {json.dumps(spec, indent=2)};
|
|
1578
|
+
|
|
1579
|
+
class AdvancedAPIExplorer {{
|
|
1580
|
+
constructor() {{
|
|
1581
|
+
this.currentEndpoint = null;
|
|
1582
|
+
this.baseUrl = window.location.origin;
|
|
1583
|
+
this.currentFilter = 'all';
|
|
1584
|
+
this.currentCodeTab = 'curl';
|
|
1585
|
+
this.init();
|
|
1586
|
+
}}
|
|
1587
|
+
|
|
1588
|
+
init() {{
|
|
1589
|
+
this.renderEndpoints();
|
|
1590
|
+
this.setupEventListeners();
|
|
1591
|
+
this.setupFilters();
|
|
1592
|
+
this.displaySystemInfo();
|
|
1593
|
+
}}
|
|
1594
|
+
|
|
1595
|
+
setupEventListeners() {{
|
|
1596
|
+
// Filter tabs
|
|
1597
|
+
document.querySelectorAll('.filter-tab').forEach(tab => {{
|
|
1598
|
+
tab.addEventListener('click', (e) => {{
|
|
1599
|
+
this.setFilter(e.target.dataset.filter);
|
|
1600
|
+
}});
|
|
1601
|
+
}});
|
|
1602
|
+
}}
|
|
1603
|
+
|
|
1604
|
+
setupFilters() {{
|
|
1605
|
+
// Initialize filter functionality
|
|
1606
|
+
this.filterEndpoints(this.currentFilter);
|
|
1607
|
+
}}
|
|
1608
|
+
|
|
1609
|
+
setFilter(filter) {{
|
|
1610
|
+
this.currentFilter = filter;
|
|
1611
|
+
|
|
1612
|
+
// Update active tab
|
|
1613
|
+
document.querySelectorAll('.filter-tab').forEach(tab => {{
|
|
1614
|
+
tab.classList.remove('active');
|
|
1615
|
+
}});
|
|
1616
|
+
document.querySelector(`[data-filter="${{filter}}"]`).classList.add('active');
|
|
1617
|
+
|
|
1618
|
+
// Filter endpoints
|
|
1619
|
+
this.filterEndpoints(filter);
|
|
1620
|
+
}}
|
|
1621
|
+
|
|
1622
|
+
filterEndpoints(filter) {{
|
|
1623
|
+
const items = document.querySelectorAll('.endpoint-item');
|
|
1624
|
+
items.forEach(item => {{
|
|
1625
|
+
const tags = item.dataset.tags || '';
|
|
1626
|
+
const shouldShow = filter === 'all' ||
|
|
1627
|
+
tags.toLowerCase().includes(filter.toLowerCase());
|
|
1628
|
+
item.style.display = shouldShow ? 'block' : 'none';
|
|
1629
|
+
}});
|
|
1630
|
+
}}
|
|
1631
|
+
|
|
1632
|
+
displaySystemInfo() {{
|
|
1633
|
+
// console.log('🚀 CREATESONLINE API Explorer Loaded');
|
|
1634
|
+
// console.log('System Info:', apiSpec['x-system-info']);
|
|
1635
|
+
// console.log('Available Features:', apiSpec['x-system-info']['features']);
|
|
1636
|
+
}}
|
|
1637
|
+
|
|
1638
|
+
renderEndpoints() {{
|
|
1639
|
+
const endpointList = document.getElementById('endpointList');
|
|
1640
|
+
const paths = apiSpec.paths || {{}};
|
|
1641
|
+
|
|
1642
|
+
// Group endpoints by tags
|
|
1643
|
+
const groupedEndpoints = {{}};
|
|
1644
|
+
|
|
1645
|
+
Object.entries(paths).forEach(([path, methods]) => {{
|
|
1646
|
+
Object.entries(methods).forEach(([method, details]) => {{
|
|
1647
|
+
const tags = details.tags || ['API'];
|
|
1648
|
+
const primaryTag = tags[0];
|
|
1649
|
+
|
|
1650
|
+
if (!groupedEndpoints[primaryTag]) {{
|
|
1651
|
+
groupedEndpoints[primaryTag] = [];
|
|
1652
|
+
}}
|
|
1653
|
+
|
|
1654
|
+
groupedEndpoints[primaryTag].push({{
|
|
1655
|
+
path, method, details, tags: tags.join(' ')
|
|
1656
|
+
}});
|
|
1657
|
+
}});
|
|
1658
|
+
}});
|
|
1659
|
+
|
|
1660
|
+
// Render grouped endpoints
|
|
1661
|
+
Object.entries(groupedEndpoints).forEach(([group, endpoints]) => {{
|
|
1662
|
+
// Group header
|
|
1663
|
+
const groupDiv = document.createElement('div');
|
|
1664
|
+
groupDiv.className = 'endpoint-group';
|
|
1665
|
+
|
|
1666
|
+
const groupTitle = document.createElement('div');
|
|
1667
|
+
groupTitle.className = 'endpoint-group-title';
|
|
1668
|
+
groupTitle.textContent = group;
|
|
1669
|
+
groupDiv.appendChild(groupTitle);
|
|
1670
|
+
|
|
1671
|
+
// Group endpoints
|
|
1672
|
+
endpoints.forEach({{path, method, details, tags}}) => {{
|
|
1673
|
+
const li = document.createElement('li');
|
|
1674
|
+
li.className = 'endpoint-item';
|
|
1675
|
+
li.dataset.tags = tags;
|
|
1676
|
+
li.innerHTML = `
|
|
1677
|
+
<a href="#" class="endpoint-link" data-path="${{path}}" data-method="${{method}}">
|
|
1678
|
+
<span class="method-badge method-${{method}}">${{method.toUpperCase()}}</span>
|
|
1679
|
+
<span class="endpoint-path">${{path}}</span>
|
|
1680
|
+
</a>
|
|
1681
|
+
`;
|
|
1682
|
+
|
|
1683
|
+
li.addEventListener('click', (e) => {{
|
|
1684
|
+
e.preventDefault();
|
|
1685
|
+
this.showEndpointDetails(path, method, details);
|
|
1686
|
+
this.setActiveEndpoint(li);
|
|
1687
|
+
}});
|
|
1688
|
+
|
|
1689
|
+
groupDiv.appendChild(li);
|
|
1690
|
+
}});
|
|
1691
|
+
|
|
1692
|
+
endpointList.appendChild(groupDiv);
|
|
1693
|
+
}});
|
|
1694
|
+
}}
|
|
1695
|
+
|
|
1696
|
+
setActiveEndpoint(activeElement) {{
|
|
1697
|
+
document.querySelectorAll('.endpoint-item').forEach(item => {{
|
|
1698
|
+
item.classList.remove('active');
|
|
1699
|
+
}});
|
|
1700
|
+
activeElement.classList.add('active');
|
|
1701
|
+
}}
|
|
1702
|
+
|
|
1703
|
+
showEndpointDetails(path, method, details) {{
|
|
1704
|
+
this.currentEndpoint = {{ path, method, details }};
|
|
1705
|
+
const detailsContainer = document.getElementById('endpointDetails');
|
|
1706
|
+
|
|
1707
|
+
const codeSamples = details['x-code-samples'] || [];
|
|
1708
|
+
const parameters = details.parameters || [];
|
|
1709
|
+
const responses = details.responses || {{}};
|
|
1710
|
+
|
|
1711
|
+
detailsContainer.innerHTML = `
|
|
1712
|
+
<div class="content-header">
|
|
1713
|
+
<div class="endpoint-title">
|
|
1714
|
+
<span class="method-badge method-${{method}}">${{method.toUpperCase()}}</span>
|
|
1715
|
+
<span>${{path}}</span>
|
|
1716
|
+
</div>
|
|
1717
|
+
|
|
1718
|
+
<div class="endpoint-description">
|
|
1719
|
+
${{details.description || details.summary || 'No description available'}}
|
|
1720
|
+
</div>
|
|
1721
|
+
|
|
1722
|
+
<div class="endpoint-tags">
|
|
1723
|
+
${{(details.tags || []).map(tag => `<span class="tag">${{tag}}</span>`).join('')}}
|
|
1724
|
+
</div>
|
|
1725
|
+
</div>
|
|
1726
|
+
|
|
1727
|
+
<div class="content-body">
|
|
1728
|
+
${{parameters.length > 0 ? `
|
|
1729
|
+
<div class="section">
|
|
1730
|
+
<h3 class="section-title">📝 Parameters</h3>
|
|
1731
|
+
<div class="parameters-grid">
|
|
1732
|
+
${{parameters.map(param => `
|
|
1733
|
+
<div class="parameter-item">
|
|
1734
|
+
<div class="parameter-name">${{param.name}}</div>
|
|
1735
|
+
<div class="parameter-type">${{param.schema?.type || 'string'}}</div>
|
|
1736
|
+
<div class="parameter-description">${{param.description || 'No description'}}</div>
|
|
1737
|
+
<div class="parameter-required">${{param.required ? 'Required' : 'Optional'}}</div>
|
|
1738
|
+
</div>
|
|
1739
|
+
`).join('')}}
|
|
1740
|
+
</div>
|
|
1741
|
+
</div>
|
|
1742
|
+
` : ''}}
|
|
1743
|
+
|
|
1744
|
+
<div class="section">
|
|
1745
|
+
<h3 class="section-title">💻 Code Examples</h3>
|
|
1746
|
+
<div class="code-examples">
|
|
1747
|
+
<div class="code-tabs">
|
|
1748
|
+
${{codeSamples.map(sample => `
|
|
1749
|
+
<button class="code-tab ${{sample.lang === 'curl' ? 'active' : ''}}"
|
|
1750
|
+
data-lang="${{sample.lang}}">${{sample.lang.toUpperCase()}}</button>
|
|
1751
|
+
`).join('')}}
|
|
1752
|
+
</div>
|
|
1753
|
+
${{codeSamples.map(sample => `
|
|
1754
|
+
<div class="code-content ${{sample.lang === 'curl' ? 'active' : ''}}"
|
|
1755
|
+
data-lang="${{sample.lang}}">
|
|
1756
|
+
<div class="code-block">
|
|
1757
|
+
<button class="copy-btn" onclick="this.copyCode(this)">📋 Copy</button>
|
|
1758
|
+
<pre>${{sample.source}}</pre>
|
|
1759
|
+
</div>
|
|
1760
|
+
</div>
|
|
1761
|
+
`).join('')}}
|
|
1762
|
+
</div>
|
|
1763
|
+
</div>
|
|
1764
|
+
|
|
1765
|
+
<div class="try-it-section">
|
|
1766
|
+
<div class="try-it-header">
|
|
1767
|
+
<h3 class="try-it-title">
|
|
1768
|
+
🚀 Try It Out
|
|
1769
|
+
</h3>
|
|
1770
|
+
<button class="execute-btn" onclick="apiExplorer.executeRequest()">
|
|
1771
|
+
Execute Request
|
|
1772
|
+
</button>
|
|
1773
|
+
</div>
|
|
1774
|
+
|
|
1775
|
+
<div id="responseContainer" class="response-section" style="display: none;">
|
|
1776
|
+
<div class="response-header">
|
|
1777
|
+
<div id="responseStatus"></div>
|
|
1778
|
+
<div id="responseTime" class="response-time"></div>
|
|
1779
|
+
</div>
|
|
1780
|
+
<div id="responseContent"></div>
|
|
1781
|
+
</div>
|
|
1782
|
+
</div>
|
|
1783
|
+
|
|
1784
|
+
<div class="section">
|
|
1785
|
+
<h3 class="section-title">📊 Response Schema</h3>
|
|
1786
|
+
${{this.renderResponseSchema(responses)}}
|
|
1787
|
+
</div>
|
|
1788
|
+
</div>
|
|
1789
|
+
`;
|
|
1790
|
+
|
|
1791
|
+
// Setup code tab switching
|
|
1792
|
+
this.setupCodeTabs();
|
|
1793
|
+
}}
|
|
1794
|
+
|
|
1795
|
+
setupCodeTabs() {{
|
|
1796
|
+
document.querySelectorAll('.code-tab').forEach(tab => {{
|
|
1797
|
+
tab.addEventListener('click', (e) => {{
|
|
1798
|
+
const lang = e.target.dataset.lang;
|
|
1799
|
+
|
|
1800
|
+
// Update active tab
|
|
1801
|
+
document.querySelectorAll('.code-tab').forEach(t => t.classList.remove('active'));
|
|
1802
|
+
e.target.classList.add('active');
|
|
1803
|
+
|
|
1804
|
+
// Show corresponding content
|
|
1805
|
+
document.querySelectorAll('.code-content').forEach(content => {{
|
|
1806
|
+
content.classList.remove('active');
|
|
1807
|
+
}});
|
|
1808
|
+
document.querySelector(`[data-lang="${{lang}}"]`).classList.add('active');
|
|
1809
|
+
}});
|
|
1810
|
+
}});
|
|
1811
|
+
}}
|
|
1812
|
+
|
|
1813
|
+
copyCode(button) {{
|
|
1814
|
+
const code = button.nextElementSibling.textContent;
|
|
1815
|
+
navigator.clipboard.writeText(code).then(() => {{
|
|
1816
|
+
button.textContent = '✅ Copied!';
|
|
1817
|
+
setTimeout(() => {{
|
|
1818
|
+
button.textContent = '📋 Copy';
|
|
1819
|
+
}}, 2000);
|
|
1820
|
+
}});
|
|
1821
|
+
}}
|
|
1822
|
+
|
|
1823
|
+
renderResponseSchema(responses) {{
|
|
1824
|
+
let html = '';
|
|
1825
|
+
Object.entries(responses).forEach(([status, response]) => {{
|
|
1826
|
+
const statusClass = status.startsWith('2') ? 'status-200' :
|
|
1827
|
+
status.startsWith('4') ? 'status-400' : 'status-500';
|
|
1828
|
+
html += `
|
|
1829
|
+
<div style="margin-bottom: 1.5rem;">
|
|
1830
|
+
<div class="status-indicator ${{statusClass}}">
|
|
1831
|
+
<span>${{status}}</span>
|
|
1832
|
+
<span>${{response.description}}</span>
|
|
1833
|
+
</div>
|
|
1834
|
+
<div class="code-block" style="margin-top: 1rem;">
|
|
1835
|
+
<button class="copy-btn" onclick="apiExplorer.copyCode(this)">📋 Copy</button>
|
|
1836
|
+
<pre>${{JSON.stringify(response.content?.['application/json']?.example || {{}}, null, 2)}}</pre>
|
|
1837
|
+
</div>
|
|
1838
|
+
</div>
|
|
1839
|
+
`;
|
|
1840
|
+
}});
|
|
1841
|
+
return html || '<p>No response schema available</p>';
|
|
1842
|
+
}}
|
|
1843
|
+
|
|
1844
|
+
async executeRequest() {{
|
|
1845
|
+
if (!this.currentEndpoint) return;
|
|
1846
|
+
|
|
1847
|
+
const {{ path, method }} = this.currentEndpoint;
|
|
1848
|
+
const executeBtn = document.querySelector('.execute-btn');
|
|
1849
|
+
const responseContainer = document.getElementById('responseContainer');
|
|
1850
|
+
const responseStatus = document.getElementById('responseStatus');
|
|
1851
|
+
const responseTime = document.getElementById('responseTime');
|
|
1852
|
+
const responseContent = document.getElementById('responseContent');
|
|
1853
|
+
|
|
1854
|
+
// Show loading state
|
|
1855
|
+
executeBtn.classList.add('loading');
|
|
1856
|
+
executeBtn.innerHTML = '<span class="loading-spinner"></span> Executing...';
|
|
1857
|
+
responseContainer.style.display = 'block';
|
|
1858
|
+
|
|
1859
|
+
try {{
|
|
1860
|
+
const startTime = performance.now();
|
|
1861
|
+
const response = await fetch(path, {{
|
|
1862
|
+
method: method.toUpperCase(),
|
|
1863
|
+
headers: {{
|
|
1864
|
+
'Accept': 'application/json',
|
|
1865
|
+
'User-Agent': 'CREATESONLINE-API-Explorer/1.0'
|
|
1866
|
+
}}
|
|
1867
|
+
}});
|
|
1868
|
+
const endTime = performance.now();
|
|
1869
|
+
const responseTimeMs = Math.round(endTime - startTime);
|
|
1870
|
+
|
|
1871
|
+
let responseData;
|
|
1872
|
+
const contentType = response.headers.get('content-type') || '';
|
|
1873
|
+
|
|
1874
|
+
if (contentType.includes('application/json')) {{
|
|
1875
|
+
responseData = await response.json();
|
|
1876
|
+
}} else {{
|
|
1877
|
+
responseData = await response.text();
|
|
1878
|
+
}}
|
|
1879
|
+
|
|
1880
|
+
const statusClass = response.ok ? 'status-200' :
|
|
1881
|
+
response.status >= 400 && response.status < 500 ? 'status-400' : 'status-500';
|
|
1882
|
+
|
|
1883
|
+
responseStatus.innerHTML = `
|
|
1884
|
+
<div class="status-indicator ${{statusClass}}">
|
|
1885
|
+
<span>${{response.status}} ${{response.statusText}}</span>
|
|
1886
|
+
</div>
|
|
1887
|
+
`;
|
|
1888
|
+
|
|
1889
|
+
responseTime.textContent = `⏱️ ${{responseTimeMs}}ms`;
|
|
1890
|
+
|
|
1891
|
+
responseContent.innerHTML = `
|
|
1892
|
+
<div class="code-block">
|
|
1893
|
+
<button class="copy-btn" onclick="apiExplorer.copyCode(this)">📋 Copy</button>
|
|
1894
|
+
<pre>${{typeof responseData === 'object' ?
|
|
1895
|
+
JSON.stringify(responseData, null, 2) : responseData}}</pre>
|
|
1896
|
+
</div>
|
|
1897
|
+
`;
|
|
1898
|
+
|
|
1899
|
+
}} catch (error) {{
|
|
1900
|
+
responseStatus.innerHTML = `
|
|
1901
|
+
<div class="status-indicator status-400">
|
|
1902
|
+
<span>❌ Network Error</span>
|
|
1903
|
+
</div>
|
|
1904
|
+
`;
|
|
1905
|
+
responseTime.textContent = '';
|
|
1906
|
+
responseContent.innerHTML = `
|
|
1907
|
+
<div class="code-block">
|
|
1908
|
+
<pre>Error: ${{error.message}}</pre>
|
|
1909
|
+
</div>
|
|
1910
|
+
`;
|
|
1911
|
+
}} finally {{
|
|
1912
|
+
executeBtn.classList.remove('loading');
|
|
1913
|
+
executeBtn.textContent = 'Execute Request';
|
|
1914
|
+
}}
|
|
1915
|
+
}}
|
|
1916
|
+
}}
|
|
1917
|
+
|
|
1918
|
+
// Initialize the advanced API explorer
|
|
1919
|
+
const apiExplorer = new AdvancedAPIExplorer();
|
|
1920
|
+
|
|
1921
|
+
// Global functions for inline event handlers
|
|
1922
|
+
window.apiExplorer = apiExplorer;
|
|
1923
|
+
</script>
|
|
1924
|
+
</body>
|
|
1925
|
+
</html>
|
|
1926
|
+
"""
|
|
1927
|
+
|
|
1928
|
+
# Use internal HTML response class
|
|
1929
|
+
class HTMLResponse:
|
|
1930
|
+
def __init__(self, content, status_code=200, headers=None):
|
|
1931
|
+
self.content = content
|
|
1932
|
+
self.status_code = status_code
|
|
1933
|
+
self.headers = headers or {'content-type': 'text/html'}
|
|
1934
|
+
|
|
1935
|
+
return HTMLResponse(html_content)
|
|
1936
|
+
|
|
1937
|
+
def _get_base_url(self):
|
|
1938
|
+
"""Get base URL for API documentation"""
|
|
1939
|
+
return "http://127.0.0.1:8000" # Default for development
|
|
1940
|
+
|
|
1941
|
+
def _generate_enhanced_api_paths(self) -> dict:
|
|
1942
|
+
"""Generate enhanced OpenAPI paths from routes with detailed info"""
|
|
1943
|
+
paths = {}
|
|
1944
|
+
for route_key in self._internal_routes.keys():
|
|
1945
|
+
method, path = route_key.split(':', 1)
|
|
1946
|
+
if path not in paths:
|
|
1947
|
+
paths[path] = {}
|
|
1948
|
+
|
|
1949
|
+
# Enhanced path information
|
|
1950
|
+
paths[path][method.lower()] = {
|
|
1951
|
+
"summary": f"{method.upper()} {path}",
|
|
1952
|
+
"description": self._get_route_description(path, method),
|
|
1953
|
+
"operationId": f"{method.lower()}_{path.replace('/', '_').replace('{', '').replace('}', '').strip('_')}",
|
|
1954
|
+
"tags": self._get_route_tags(path),
|
|
1955
|
+
"parameters": self._get_route_parameters(path),
|
|
1956
|
+
"responses": {
|
|
1957
|
+
"200": {
|
|
1958
|
+
"description": "Successful response",
|
|
1959
|
+
"content": {
|
|
1960
|
+
"application/json": {
|
|
1961
|
+
"schema": {"type": "object"},
|
|
1962
|
+
"example": self._get_example_response(path, method)
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
},
|
|
1966
|
+
"400": {"description": "Bad request"},
|
|
1967
|
+
"404": {"description": "Not found"},
|
|
1968
|
+
"500": {"description": "Internal server error"}
|
|
1969
|
+
},
|
|
1970
|
+
"x-code-samples": [
|
|
1971
|
+
{
|
|
1972
|
+
"lang": "curl",
|
|
1973
|
+
"source": f"curl -X {method.upper()} '{self._get_base_url()}{path}' -H 'Accept: application/json'"
|
|
1974
|
+
},
|
|
1975
|
+
{
|
|
1976
|
+
"lang": "python",
|
|
1977
|
+
"source": f"import requests\nresponse = requests.{method.lower()}('{self._get_base_url()}{path}')\nprint(response.json())"
|
|
1978
|
+
},
|
|
1979
|
+
{
|
|
1980
|
+
"lang": "javascript",
|
|
1981
|
+
"source": f"fetch('{self._get_base_url()}{path}', {{\n method: '{method.upper()}',\n headers: {{\n 'Accept': 'application/json'\n }}\n}})\n.then(response => response.json())\n.then(data => console.log(data));"
|
|
1982
|
+
}
|
|
1983
|
+
]
|
|
1984
|
+
}
|
|
1985
|
+
return paths
|
|
1986
|
+
|
|
1987
|
+
def _get_route_description(self, path: str, method: str) -> str:
|
|
1988
|
+
"""Get description for a route based on its path"""
|
|
1989
|
+
descriptions = {
|
|
1990
|
+
"/": "Get framework information and status",
|
|
1991
|
+
"/health": "Health check endpoint with system metrics",
|
|
1992
|
+
"/examples": "Example endpoint showcasing framework capabilities",
|
|
1993
|
+
"/admin": "Admin interface for managing the application",
|
|
1994
|
+
"/api/status": "API status and operational information",
|
|
1995
|
+
"/docs": "Interactive API documentation and specification",
|
|
1996
|
+
"/docs": "Interactive API documentation",
|
|
1997
|
+
"/framework/info": "Detailed framework information and configuration"
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
# Check for AI routes
|
|
2001
|
+
if '/ai/' in path:
|
|
2002
|
+
return "AI-powered endpoint with intelligent processing capabilities"
|
|
2003
|
+
|
|
2004
|
+
# Check for admin routes
|
|
2005
|
+
if '/admin/' in path:
|
|
2006
|
+
return "Administrative endpoint for system management"
|
|
2007
|
+
|
|
2008
|
+
return descriptions.get(path, f"API endpoint: {method.upper()} {path}")
|
|
2009
|
+
|
|
2010
|
+
def _get_route_tags(self, path: str) -> list:
|
|
2011
|
+
"""Get tags for route categorization"""
|
|
2012
|
+
if path.startswith('/ai/'):
|
|
2013
|
+
return ["AI", "Machine Learning"]
|
|
2014
|
+
elif path.startswith('/admin/'):
|
|
2015
|
+
return ["Admin", "Management"]
|
|
2016
|
+
elif path in ['/health', '/api/status']:
|
|
2017
|
+
return ["System", "Health"]
|
|
2018
|
+
elif path in ['/', '/examples', '/framework/info']:
|
|
2019
|
+
return ["Framework", "Info"]
|
|
2020
|
+
else:
|
|
2021
|
+
return ["API"]
|
|
2022
|
+
|
|
2023
|
+
def _get_route_parameters(self, path: str) -> list:
|
|
2024
|
+
"""Extract parameters from route path"""
|
|
2025
|
+
import re
|
|
2026
|
+
params = []
|
|
2027
|
+
param_matches = re.findall(r'\{([^}]+)\}', path)
|
|
2028
|
+
|
|
2029
|
+
for param in param_matches:
|
|
2030
|
+
param_info = {
|
|
2031
|
+
"name": param,
|
|
2032
|
+
"in": "path",
|
|
2033
|
+
"required": True,
|
|
2034
|
+
"schema": {"type": "string"},
|
|
2035
|
+
"description": f"Path parameter: {param}"
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
# Type hints from parameter names
|
|
2039
|
+
if 'id' in param.lower():
|
|
2040
|
+
param_info["schema"] = {"type": "integer", "minimum": 1}
|
|
2041
|
+
param_info["description"] = f"Unique identifier for {param.replace('_id', '').replace('id', 'resource')}"
|
|
2042
|
+
elif 'model' in param.lower():
|
|
2043
|
+
param_info["schema"] = {"type": "string", "enum": ["gpt-3.5-turbo", "gpt-4", "claude-3"]}
|
|
2044
|
+
param_info["description"] = "AI model name"
|
|
2045
|
+
|
|
2046
|
+
params.append(param_info)
|
|
2047
|
+
|
|
2048
|
+
return params
|
|
2049
|
+
|
|
2050
|
+
def _get_example_response(self, path: str, method: str) -> dict:
|
|
2051
|
+
"""Generate example responses based on route"""
|
|
2052
|
+
examples = {
|
|
2053
|
+
"/": {
|
|
2054
|
+
"framework": "CREATESONLINE",
|
|
2055
|
+
"version": self.version,
|
|
2056
|
+
"status": "operational",
|
|
2057
|
+
"ai_enabled": len(self._ai_features) > 0,
|
|
2058
|
+
"features": self._ai_features[:3] if self._ai_features else []
|
|
2059
|
+
},
|
|
2060
|
+
"/health": {
|
|
2061
|
+
"status": "healthy",
|
|
2062
|
+
"timestamp": "2024-01-15T10:30:00Z",
|
|
2063
|
+
"uptime": 86400,
|
|
2064
|
+
"system": {"memory": "45%", "cpu": "12%", "disk": "67%"}
|
|
2065
|
+
},
|
|
2066
|
+
"/examples": {
|
|
2067
|
+
"message": "CREATESONLINE Example Response",
|
|
2068
|
+
"capabilities": ["AI Integration", "Admin Interface", "Smart Routing"],
|
|
2069
|
+
"timestamp": "2024-01-15T10:30:00Z"
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
if '/ai/' in path:
|
|
2074
|
+
return {
|
|
2075
|
+
"model": "gpt-3.5-turbo",
|
|
2076
|
+
"response": "AI-generated response",
|
|
2077
|
+
"confidence": 0.95,
|
|
2078
|
+
"processing_time_ms": 250
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
return examples.get(path, {"message": "Success", "data": {}})
|
|
2082
|
+
|
|
2083
|
+
def _generate_api_schemas(self) -> dict:
|
|
2084
|
+
"""Generate API schemas for common data types"""
|
|
2085
|
+
return {
|
|
2086
|
+
"FrameworkInfo": {
|
|
2087
|
+
"type": "object",
|
|
2088
|
+
"properties": {
|
|
2089
|
+
"framework": {"type": "string", "example": "CREATESONLINE"},
|
|
2090
|
+
"version": {"type": "string", "example": self.version},
|
|
2091
|
+
"status": {"type": "string", "enum": ["operational", "maintenance", "error"]},
|
|
2092
|
+
"ai_enabled": {"type": "boolean"},
|
|
2093
|
+
"features": {"type": "array", "items": {"type": "string"}}
|
|
2094
|
+
}
|
|
2095
|
+
},
|
|
2096
|
+
"HealthStatus": {
|
|
2097
|
+
"type": "object",
|
|
2098
|
+
"properties": {
|
|
2099
|
+
"status": {"type": "string", "enum": ["healthy", "degraded", "unhealthy"]},
|
|
2100
|
+
"timestamp": {"type": "string", "format": "date-time"},
|
|
2101
|
+
"uptime": {"type": "integer", "description": "Uptime in seconds"},
|
|
2102
|
+
"system": {
|
|
2103
|
+
"type": "object",
|
|
2104
|
+
"properties": {
|
|
2105
|
+
"memory": {"type": "string"},
|
|
2106
|
+
"cpu": {"type": "string"},
|
|
2107
|
+
"disk": {"type": "string"}
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
},
|
|
2112
|
+
"Error": {
|
|
2113
|
+
"type": "object",
|
|
2114
|
+
"properties": {
|
|
2115
|
+
"error": {"type": "string"},
|
|
2116
|
+
"message": {"type": "string"},
|
|
2117
|
+
"code": {"type": "integer"},
|
|
2118
|
+
"timestamp": {"type": "string", "format": "date-time"}
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
def _generate_error_page(self, status_code: int, error_message: str, path: str = "", method: str = "GET", details: str = "") -> str:
|
|
2124
|
+
"""Generate beautiful HTML error pages matching homepage UI"""
|
|
2125
|
+
|
|
2126
|
+
error_titles = {
|
|
2127
|
+
404: "Page Not Found",
|
|
2128
|
+
500: "Internal Server Error",
|
|
2129
|
+
403: "Access Forbidden",
|
|
2130
|
+
400: "Bad Request"
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
title = error_titles.get(status_code, f"Error {status_code}")
|
|
2134
|
+
|
|
2135
|
+
return f"""<!DOCTYPE html>
|
|
2136
|
+
<html lang="en">
|
|
2137
|
+
<head>
|
|
2138
|
+
<meta charset="UTF-8">
|
|
2139
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2140
|
+
<title>{title} - CREATESONLINE</title>
|
|
2141
|
+
<style>
|
|
2142
|
+
* {{
|
|
2143
|
+
margin: 0;
|
|
2144
|
+
padding: 0;
|
|
2145
|
+
box-sizing: border-box;
|
|
2146
|
+
}}
|
|
2147
|
+
|
|
2148
|
+
body {{
|
|
2149
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
|
|
2150
|
+
background: #0a0a0a;
|
|
2151
|
+
color: #ffffff;
|
|
2152
|
+
padding: 40px 20px;
|
|
2153
|
+
min-height: 100vh;
|
|
2154
|
+
display: flex;
|
|
2155
|
+
align-items: center;
|
|
2156
|
+
justify-content: center;
|
|
2157
|
+
}}
|
|
2158
|
+
|
|
2159
|
+
.error-container {{
|
|
2160
|
+
max-width: 700px;
|
|
2161
|
+
width: 100%;
|
|
2162
|
+
background: #1a1a1a;
|
|
2163
|
+
padding: 60px 50px;
|
|
2164
|
+
border-radius: 12px;
|
|
2165
|
+
border: 1px solid #2a2a2a;
|
|
2166
|
+
text-align: center;
|
|
2167
|
+
}}
|
|
2168
|
+
|
|
2169
|
+
.error-code {{
|
|
2170
|
+
font-size: 8rem;
|
|
2171
|
+
font-weight: 700;
|
|
2172
|
+
background: linear-gradient(135deg, #ffffff 0%, #a0a0a0 100%);
|
|
2173
|
+
-webkit-background-clip: text;
|
|
2174
|
+
-webkit-text-fill-color: transparent;
|
|
2175
|
+
background-clip: text;
|
|
2176
|
+
margin-bottom: 20px;
|
|
2177
|
+
line-height: 1;
|
|
2178
|
+
}}
|
|
2179
|
+
|
|
2180
|
+
h1 {{
|
|
2181
|
+
font-size: 2em;
|
|
2182
|
+
font-weight: 600;
|
|
2183
|
+
margin-bottom: 15px;
|
|
2184
|
+
color: #fff;
|
|
2185
|
+
}}
|
|
2186
|
+
|
|
2187
|
+
p {{
|
|
2188
|
+
color: #888;
|
|
2189
|
+
font-size: 1.1em;
|
|
2190
|
+
margin-bottom: 40px;
|
|
2191
|
+
line-height: 1.6;
|
|
2192
|
+
}}
|
|
2193
|
+
|
|
2194
|
+
.btn {{
|
|
2195
|
+
padding: 14px 28px;
|
|
2196
|
+
background: #ffffff;
|
|
2197
|
+
color: #000;
|
|
2198
|
+
border-radius: 8px;
|
|
2199
|
+
text-decoration: none;
|
|
2200
|
+
font-weight: 600;
|
|
2201
|
+
transition: all 0.3s ease;
|
|
2202
|
+
display: inline-block;
|
|
2203
|
+
}}
|
|
2204
|
+
|
|
2205
|
+
.btn:hover {{
|
|
2206
|
+
background: #f0f0f0;
|
|
2207
|
+
transform: translateY(-2px);
|
|
2208
|
+
box-shadow: 0 8px 20px rgba(255, 255, 255, 0.15);
|
|
2209
|
+
}}
|
|
2210
|
+
|
|
2211
|
+
.footer {{
|
|
2212
|
+
margin-top: 50px;
|
|
2213
|
+
padding-top: 30px;
|
|
2214
|
+
border-top: 1px solid #2a2a2a;
|
|
2215
|
+
color: #666;
|
|
2216
|
+
font-size: 0.9em;
|
|
2217
|
+
}}
|
|
2218
|
+
|
|
2219
|
+
@media (max-width: 600px) {{
|
|
2220
|
+
.error-container {{
|
|
2221
|
+
padding: 40px 30px;
|
|
2222
|
+
}}
|
|
2223
|
+
.error-code {{
|
|
2224
|
+
font-size: 5em;
|
|
2225
|
+
}}
|
|
2226
|
+
h1 {{
|
|
2227
|
+
font-size: 1.5em;
|
|
2228
|
+
}}
|
|
2229
|
+
}}
|
|
2230
|
+
</style>
|
|
2231
|
+
</head>
|
|
2232
|
+
<body>
|
|
2233
|
+
<div class="error-container">
|
|
2234
|
+
<div class="error-code">{status_code}</div>
|
|
2235
|
+
<h1>{title}</h1>
|
|
2236
|
+
<p>{error_message}</p>
|
|
2237
|
+
|
|
2238
|
+
<a href="/" class="btn">Go Home</a>
|
|
2239
|
+
|
|
2240
|
+
<div class="footer">
|
|
2241
|
+
<p>CREATESONLINE v0.1.6</p>
|
|
2242
|
+
</div>
|
|
2243
|
+
</div>
|
|
2244
|
+
</body>
|
|
2245
|
+
</html>"""
|
|
2246
|
+
|
|
2247
|
+
|
|
2248
|
+
class CreatesonlineInternalRequest:
|
|
2249
|
+
"""Internal request object for zero-dependency mode"""
|
|
2250
|
+
|
|
2251
|
+
def __init__(self, scope, receive):
|
|
2252
|
+
self.scope = scope
|
|
2253
|
+
self.receive = receive
|
|
2254
|
+
self.method = scope['method']
|
|
2255
|
+
self.url = self._build_url(scope)
|
|
2256
|
+
self.path = scope['path']
|
|
2257
|
+
self.query_params = self._parse_query_string(scope.get('query_string', b''))
|
|
2258
|
+
self.path_params = scope.get('path_params', {})
|
|
2259
|
+
self.headers = self._parse_headers(scope.get('headers', []))
|
|
2260
|
+
|
|
2261
|
+
def _build_url(self, scope):
|
|
2262
|
+
"""Build URL object from scope"""
|
|
2263
|
+
scheme = scope.get('scheme', 'http')
|
|
2264
|
+
server = scope.get('server', ('localhost', 80))
|
|
2265
|
+
path = scope.get('path', '/')
|
|
2266
|
+
query_string = scope.get('query_string', b'').decode()
|
|
2267
|
+
|
|
2268
|
+
url = f"{scheme}://{server[0]}:{server[1]}{path}"
|
|
2269
|
+
if query_string:
|
|
2270
|
+
url += f"?{query_string}"
|
|
2271
|
+
|
|
2272
|
+
return url
|
|
2273
|
+
|
|
2274
|
+
def _parse_query_string(self, query_string):
|
|
2275
|
+
"""Parse query string into dict"""
|
|
2276
|
+
if not query_string:
|
|
2277
|
+
return {}
|
|
2278
|
+
|
|
2279
|
+
return parse_qs(query_string.decode(), keep_blank_values=True)
|
|
2280
|
+
|
|
2281
|
+
def _parse_headers(self, headers):
|
|
2282
|
+
"""Parse headers into dict"""
|
|
2283
|
+
header_dict = {}
|
|
2284
|
+
for name, value in headers:
|
|
2285
|
+
# Handle both bytes and string headers
|
|
2286
|
+
if isinstance(name, bytes):
|
|
2287
|
+
name = name.decode()
|
|
2288
|
+
if isinstance(value, bytes):
|
|
2289
|
+
value = value.decode()
|
|
2290
|
+
header_dict[name.lower()] = value
|
|
2291
|
+
return header_dict
|
|
2292
|
+
|
|
2293
|
+
async def json(self):
|
|
2294
|
+
"""Parse JSON body"""
|
|
2295
|
+
body = await self._get_body()
|
|
2296
|
+
return json.loads(body.decode())
|
|
2297
|
+
|
|
2298
|
+
async def body(self):
|
|
2299
|
+
"""Get raw body"""
|
|
2300
|
+
return await self._get_body()
|
|
2301
|
+
|
|
2302
|
+
async def _get_body(self):
|
|
2303
|
+
"""Get request body"""
|
|
2304
|
+
body = b''
|
|
2305
|
+
while True:
|
|
2306
|
+
message = await self.receive()
|
|
2307
|
+
if message['type'] == 'http.request':
|
|
2308
|
+
body += message.get('body', b'')
|
|
2309
|
+
if not message.get('more_body', False):
|
|
2310
|
+
break
|
|
2311
|
+
return body
|
|
2312
|
+
|
|
2313
|
+
def _generate_error_page(self, status_code: int, error_message: str, path: str = "", method: str = "GET", details: str = "") -> str:
|
|
2314
|
+
"""Generate beautiful HTML error pages with consistent CREATESONLINE theming"""
|
|
2315
|
+
|
|
2316
|
+
# Define error page content based on status code
|
|
2317
|
+
error_info = {
|
|
2318
|
+
404: {
|
|
2319
|
+
"title": "Page Not Found",
|
|
2320
|
+
"emoji": "🔍",
|
|
2321
|
+
"description": "The page you're looking for doesn't exist or has been moved.",
|
|
2322
|
+
"suggestions": [
|
|
2323
|
+
"Check the URL for typos",
|
|
2324
|
+
"Go back to the homepage",
|
|
2325
|
+
"Browse our available API endpoints",
|
|
2326
|
+
"Contact support if you believe this is an error"
|
|
2327
|
+
]
|
|
2328
|
+
},
|
|
2329
|
+
500: {
|
|
2330
|
+
"title": "Internal Server Error",
|
|
2331
|
+
"emoji": "⚠️",
|
|
2332
|
+
"description": "Something went wrong on our end. We're working to fix it.",
|
|
2333
|
+
"suggestions": [
|
|
2334
|
+
"Try refreshing the page",
|
|
2335
|
+
"Wait a moment and try again",
|
|
2336
|
+
"Check our status page",
|
|
2337
|
+
"Contact support if the problem persists"
|
|
2338
|
+
]
|
|
2339
|
+
},
|
|
2340
|
+
403: {
|
|
2341
|
+
"title": "Access Forbidden",
|
|
2342
|
+
"emoji": "🚫",
|
|
2343
|
+
"description": "You don't have permission to access this resource.",
|
|
2344
|
+
"suggestions": [
|
|
2345
|
+
"Check if you're logged in",
|
|
2346
|
+
"Verify your permissions",
|
|
2347
|
+
"Contact an administrator",
|
|
2348
|
+
"Return to the homepage"
|
|
2349
|
+
]
|
|
2350
|
+
},
|
|
2351
|
+
400: {
|
|
2352
|
+
"title": "Bad Request",
|
|
2353
|
+
"emoji": "❌",
|
|
2354
|
+
"description": "The request was invalid or could not be processed.",
|
|
2355
|
+
"suggestions": [
|
|
2356
|
+
"Check your request format",
|
|
2357
|
+
"Review the API documentation",
|
|
2358
|
+
"Verify required parameters",
|
|
2359
|
+
"Try a different approach"
|
|
2360
|
+
]
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
# Get error info or default for unknown status codes
|
|
2365
|
+
info = error_info.get(status_code, {
|
|
2366
|
+
"title": f"Error {status_code}",
|
|
2367
|
+
"emoji": "⚠️",
|
|
2368
|
+
"description": error_message or "An unexpected error occurred.",
|
|
2369
|
+
"suggestions": [
|
|
2370
|
+
"Try refreshing the page",
|
|
2371
|
+
"Go back to the previous page",
|
|
2372
|
+
"Contact support",
|
|
2373
|
+
"Return to the homepage"
|
|
2374
|
+
]
|
|
2375
|
+
})
|
|
2376
|
+
|
|
2377
|
+
# Generate navigation suggestions based on available routes
|
|
2378
|
+
nav_links = []
|
|
2379
|
+
if hasattr(self, '_internal_routes'):
|
|
2380
|
+
for route_key in self._internal_routes.keys():
|
|
2381
|
+
route_method, route_path = route_key.split(':', 1)
|
|
2382
|
+
if route_method == 'GET' and not '{' in route_path: # Only GET routes without parameters
|
|
2383
|
+
if route_path in ['/', '/health', '/admin', '/docs']:
|
|
2384
|
+
route_name = {
|
|
2385
|
+
'/': 'Home',
|
|
2386
|
+
'/health': 'System Health',
|
|
2387
|
+
'/admin': 'Admin Panel',
|
|
2388
|
+
'/docs': 'API Documentation'
|
|
2389
|
+
}.get(route_path, route_path.title())
|
|
2390
|
+
nav_links.append(f'<a href="{route_path}" style="color: #ffffff; text-decoration: none; font-weight: 600; padding: 0.75rem 1.5rem; border-radius: 0.5rem; background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2); backdrop-filter: blur(10px); transition: all 0.2s ease; display: inline-block; margin: 0.5rem;">{route_name}</a>')
|
|
2391
|
+
|
|
2392
|
+
return f"""
|
|
2393
|
+
<!DOCTYPE html>
|
|
2394
|
+
<html lang="en">
|
|
2395
|
+
<head>
|
|
2396
|
+
<meta charset="UTF-8">
|
|
2397
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2398
|
+
<title>{info['title']} - CREATESONLINE</title>
|
|
2399
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
2400
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
2401
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
|
|
2402
|
+
<link rel="icon" type="image/x-icon" href="/static/image/favicon.ico">
|
|
2403
|
+
<style>
|
|
2404
|
+
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
|
|
2405
|
+
|
|
2406
|
+
@keyframes rotate-pulse {{
|
|
2407
|
+
0% {{ transform: rotate(0deg) scale(1); }}
|
|
2408
|
+
25% {{ transform: rotate(10deg) scale(1.1); }}
|
|
2409
|
+
50% {{ transform: rotate(0deg) scale(1); }}
|
|
2410
|
+
75% {{ transform: rotate(-10deg) scale(1.1); }}
|
|
2411
|
+
100% {{ transform: rotate(0deg) scale(1); }}
|
|
2412
|
+
}}
|
|
2413
|
+
|
|
2414
|
+
@keyframes float {{
|
|
2415
|
+
0%, 100% {{ transform: translateY(0px); }}
|
|
2416
|
+
50% {{ transform: translateY(-20px); }}
|
|
2417
|
+
}}
|
|
2418
|
+
|
|
2419
|
+
body {{
|
|
2420
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
|
|
2421
|
+
background: #0a0a0a;
|
|
2422
|
+
color: #ffffff;
|
|
2423
|
+
padding: 40px 20px;
|
|
2424
|
+
min-height: 100vh;
|
|
2425
|
+
display: flex;
|
|
2426
|
+
align-items: center;
|
|
2427
|
+
justify-content: center;
|
|
2428
|
+
}}
|
|
2429
|
+
|
|
2430
|
+
.error-container {{
|
|
2431
|
+
max-width: 700px;
|
|
2432
|
+
width: 100%;
|
|
2433
|
+
background: #1a1a1a;
|
|
2434
|
+
padding: 60px 50px;
|
|
2435
|
+
border-radius: 12px;
|
|
2436
|
+
border: 1px solid #2a2a2a;
|
|
2437
|
+
text-align: center;
|
|
2438
|
+
}}
|
|
2439
|
+
|
|
2440
|
+
.error-icon {{
|
|
2441
|
+
font-size: 4rem;
|
|
2442
|
+
margin-bottom: 1.5rem;
|
|
2443
|
+
display: block;
|
|
2444
|
+
}}
|
|
2445
|
+
|
|
2446
|
+
.error-code {{
|
|
2447
|
+
font-size: 8rem;
|
|
2448
|
+
font-weight: 700;
|
|
2449
|
+
background: linear-gradient(135deg, #ffffff 0%, #a0a0a0 100%);
|
|
2450
|
+
-webkit-background-clip: text;
|
|
2451
|
+
-webkit-text-fill-color: transparent;
|
|
2452
|
+
background-clip: text;
|
|
2453
|
+
margin-bottom: 20px;
|
|
2454
|
+
line-height: 1;
|
|
2455
|
+
}}
|
|
2456
|
+
|
|
2457
|
+
.error-title {{
|
|
2458
|
+
font-size: 2em;
|
|
2459
|
+
font-weight: 600;
|
|
2460
|
+
margin-bottom: 15px;
|
|
2461
|
+
color: #fff;
|
|
2462
|
+
}}
|
|
2463
|
+
|
|
2464
|
+
.error-description {{
|
|
2465
|
+
color: #888;
|
|
2466
|
+
font-size: 1.1em;
|
|
2467
|
+
margin-bottom: 40px;
|
|
2468
|
+
line-height: 1.6;
|
|
2469
|
+
}}
|
|
2470
|
+
|
|
2471
|
+
.error-details {{
|
|
2472
|
+
background: #0a0a0a;
|
|
2473
|
+
border: 1px solid #2a2a2a;
|
|
2474
|
+
border-radius: 8px;
|
|
2475
|
+
padding: 20px;
|
|
2476
|
+
margin: 30px 0;
|
|
2477
|
+
text-align: left;
|
|
2478
|
+
}}
|
|
2479
|
+
|
|
2480
|
+
.error-details h4 {{
|
|
2481
|
+
color: #fff;
|
|
2482
|
+
margin-bottom: 15px;
|
|
2483
|
+
font-size: 1.1em;
|
|
2484
|
+
}}
|
|
2485
|
+
|
|
2486
|
+
.error-details p {{
|
|
2487
|
+
color: #888;
|
|
2488
|
+
font-size: 0.95em;
|
|
2489
|
+
margin: 8px 0;
|
|
2490
|
+
}}
|
|
2491
|
+
|
|
2492
|
+
.actions {{
|
|
2493
|
+
display: flex;
|
|
2494
|
+
gap: 15px;
|
|
2495
|
+
justify-content: center;
|
|
2496
|
+
flex-wrap: wrap;
|
|
2497
|
+
margin-top: 30px;
|
|
2498
|
+
}}
|
|
2499
|
+
|
|
2500
|
+
.btn {{
|
|
2501
|
+
padding: 14px 28px;
|
|
2502
|
+
border-radius: 8px;
|
|
2503
|
+
text-decoration: none;
|
|
2504
|
+
font-weight: 600;
|
|
2505
|
+
transition: all 0.3s ease;
|
|
2506
|
+
display: inline-block;
|
|
2507
|
+
}}
|
|
2508
|
+
|
|
2509
|
+
.btn-primary {{
|
|
2510
|
+
background: #ffffff;
|
|
2511
|
+
color: #000;
|
|
2512
|
+
}}
|
|
2513
|
+
|
|
2514
|
+
.btn-primary:hover {{
|
|
2515
|
+
background: #f0f0f0;
|
|
2516
|
+
transform: translateY(-2px);
|
|
2517
|
+
box-shadow: 0 8px 20px rgba(255, 255, 255, 0.15);
|
|
2518
|
+
}}
|
|
2519
|
+
|
|
2520
|
+
.footer {{
|
|
2521
|
+
margin-top: 50px;
|
|
2522
|
+
padding-top: 30px;
|
|
2523
|
+
border-top: 1px solid #2a2a2a;
|
|
2524
|
+
color: #666;
|
|
2525
|
+
font-size: 0.9em;
|
|
2526
|
+
}}
|
|
2527
|
+
color: #ffffff;
|
|
2528
|
+
opacity: 0.7;
|
|
2529
|
+
font-size: 0.9rem;
|
|
2530
|
+
|
|
2531
|
+
@media (max-width: 600px) {{
|
|
2532
|
+
.error-container {{
|
|
2533
|
+
padding: 40px 30px;
|
|
2534
|
+
}}
|
|
2535
|
+
.error-code {{
|
|
2536
|
+
font-size: 5em;
|
|
2537
|
+
}}
|
|
2538
|
+
.error-title {{
|
|
2539
|
+
font-size: 1.5em;
|
|
2540
|
+
}}
|
|
2541
|
+
.actions {{
|
|
2542
|
+
flex-direction: column;
|
|
2543
|
+
}}
|
|
2544
|
+
.btn {{
|
|
2545
|
+
width: 100%;
|
|
2546
|
+
}}
|
|
2547
|
+
}}
|
|
2548
|
+
</style>
|
|
2549
|
+
</head>
|
|
2550
|
+
<body>
|
|
2551
|
+
<div class="error-container">
|
|
2552
|
+
<div class="error-code">{status_code}</div>
|
|
2553
|
+
<h1 class="error-title">{info['title']}</h1>
|
|
2554
|
+
<p class="error-description">{info['description']}</p>
|
|
2555
|
+
|
|
2556
|
+
{f'''
|
|
2557
|
+
<div class="error-details">
|
|
2558
|
+
<h4>Request Details</h4>
|
|
2559
|
+
<p><strong>Path:</strong> {path}</p>
|
|
2560
|
+
<p><strong>Method:</strong> {method}</p>
|
|
2561
|
+
</div>
|
|
2562
|
+
''' if path else ''}
|
|
2563
|
+
|
|
2564
|
+
<div class="actions">
|
|
2565
|
+
<a href="/" class="btn btn-primary">Go Home</a>
|
|
2566
|
+
</div>
|
|
2567
|
+
|
|
2568
|
+
<div class="footer">
|
|
2569
|
+
<p>CREATESONLINE v0.1.6 - AI-Native Web Framework</p>
|
|
2570
|
+
</div>
|
|
2571
|
+
</div>
|
|
2572
|
+
<img src="/static/image/favicon.ico" alt="CREATESONLINE" />
|
|
2573
|
+
<span>Powered by CREATESONLINE</span>
|
|
2574
|
+
</div>
|
|
2575
|
+
</body>
|
|
2576
|
+
</html>
|
|
2577
|
+
"""
|