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