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,789 @@
|
|
|
1
|
+
# createsonline/project_init.py
|
|
2
|
+
"""
|
|
3
|
+
Automatic Project Initialization
|
|
4
|
+
|
|
5
|
+
This module automatically creates the project structure when a user
|
|
6
|
+
runs their application for the first time. It protects existing files
|
|
7
|
+
and only creates missing ones.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
import shutil
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ProjectInitializer:
|
|
16
|
+
"""Automatically initialize project structure without overwriting user files"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, project_root: Path = None):
|
|
19
|
+
self.project_root = project_root or Path.cwd()
|
|
20
|
+
self.framework_dir = Path(__file__).parent
|
|
21
|
+
self.user_files_created = []
|
|
22
|
+
self.user_files_skipped = []
|
|
23
|
+
self.verbose = False # Control print statements
|
|
24
|
+
|
|
25
|
+
def initialize(self, force: bool = False, verbose: bool = False) -> dict:
|
|
26
|
+
"""
|
|
27
|
+
Initialize project structure automatically.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
force: If True, overwrites existing files (dangerous!)
|
|
31
|
+
verbose: If True, print progress messages
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
dict with status of created/skipped files
|
|
35
|
+
"""
|
|
36
|
+
self.verbose = verbose
|
|
37
|
+
if verbose:
|
|
38
|
+
print(f"\nInitializing CREATESONLINE project structure...")
|
|
39
|
+
print(f"Project root: {self.project_root}")
|
|
40
|
+
|
|
41
|
+
# Create directories
|
|
42
|
+
self._create_directories()
|
|
43
|
+
|
|
44
|
+
# Create/update user files (ONLY if they don't exist)
|
|
45
|
+
self._ensure_main_py()
|
|
46
|
+
self._ensure_routes_py()
|
|
47
|
+
self._ensure_user_config_py()
|
|
48
|
+
self._ensure_templates()
|
|
49
|
+
self._ensure_static_files()
|
|
50
|
+
self._ensure_documentation()
|
|
51
|
+
|
|
52
|
+
# Summary (only if verbose)
|
|
53
|
+
if verbose:
|
|
54
|
+
print(f"\nInitialization complete!")
|
|
55
|
+
if self.user_files_created:
|
|
56
|
+
print(f"Created {len(self.user_files_created)} new files:")
|
|
57
|
+
for f in self.user_files_created:
|
|
58
|
+
print(f" * {f}")
|
|
59
|
+
if self.user_files_skipped:
|
|
60
|
+
print(f"Skipped {len(self.user_files_skipped)} existing files (protected):")
|
|
61
|
+
for f in self.user_files_skipped:
|
|
62
|
+
print(f" - {f}")
|
|
63
|
+
print(f" ⊗ {f}")
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
"created": self.user_files_created,
|
|
67
|
+
"skipped": self.user_files_skipped,
|
|
68
|
+
"success": True
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
def _create_directories(self):
|
|
72
|
+
"""Create necessary directories"""
|
|
73
|
+
dirs = [
|
|
74
|
+
self.project_root / "templates",
|
|
75
|
+
self.project_root / "static",
|
|
76
|
+
self.project_root / "static" / "css",
|
|
77
|
+
self.project_root / "static" / "js",
|
|
78
|
+
self.project_root / "static" / "images",
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
for directory in dirs:
|
|
82
|
+
if not directory.exists():
|
|
83
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
84
|
+
if self.verbose:
|
|
85
|
+
print(f" Created directory: {directory.relative_to(self.project_root)}")
|
|
86
|
+
|
|
87
|
+
def _create_file(self, filepath: Path, content: str, description: str = None):
|
|
88
|
+
"""Create a file only if it doesn't exist"""
|
|
89
|
+
rel_path = filepath.relative_to(self.project_root)
|
|
90
|
+
|
|
91
|
+
if filepath.exists():
|
|
92
|
+
self.user_files_skipped.append(str(rel_path))
|
|
93
|
+
return False
|
|
94
|
+
|
|
95
|
+
filepath.parent.mkdir(parents=True, exist_ok=True)
|
|
96
|
+
filepath.write_text(content, encoding='utf-8')
|
|
97
|
+
self.user_files_created.append(str(rel_path))
|
|
98
|
+
if description and self.verbose:
|
|
99
|
+
print(f" {description}: {rel_path}")
|
|
100
|
+
return True
|
|
101
|
+
|
|
102
|
+
def _ensure_main_py(self):
|
|
103
|
+
"""Create main.py if it doesn't exist"""
|
|
104
|
+
main_py = self.project_root / "main.py"
|
|
105
|
+
|
|
106
|
+
content = '''#!/usr/bin/env python3
|
|
107
|
+
"""
|
|
108
|
+
CREATESONLINE Application Bootstrap
|
|
109
|
+
|
|
110
|
+
⚠️ UPGRADE-SAFE FILE - Your changes persist across framework updates! ⚠️
|
|
111
|
+
|
|
112
|
+
This file is automatically generated but safe to customize.
|
|
113
|
+
"""
|
|
114
|
+
from createsonline import create_app
|
|
115
|
+
from createsonline.project_init import auto_discover_routes
|
|
116
|
+
|
|
117
|
+
# Create application
|
|
118
|
+
app = create_app(
|
|
119
|
+
title="My CREATESONLINE App",
|
|
120
|
+
description="AI-Native Web Application",
|
|
121
|
+
version="1.0.0",
|
|
122
|
+
debug=True
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Auto-discover and register routes (silent)
|
|
126
|
+
auto_discover_routes(app)
|
|
127
|
+
|
|
128
|
+
if __name__ == "__main__":
|
|
129
|
+
from createsonline.server import run_server
|
|
130
|
+
|
|
131
|
+
# Load environment variables if available
|
|
132
|
+
try:
|
|
133
|
+
from dotenv import load_dotenv
|
|
134
|
+
load_dotenv()
|
|
135
|
+
except ImportError:
|
|
136
|
+
pass
|
|
137
|
+
|
|
138
|
+
import os
|
|
139
|
+
host = os.getenv("HOST", "0.0.0.0")
|
|
140
|
+
port = int(os.getenv("PORT", "8000"))
|
|
141
|
+
|
|
142
|
+
run_server(app, host=host, port=port)
|
|
143
|
+
'''
|
|
144
|
+
|
|
145
|
+
self._create_file(main_py, content, "Created main.py")
|
|
146
|
+
|
|
147
|
+
def _ensure_routes_py(self):
|
|
148
|
+
"""Create routes.py if it doesn't exist"""
|
|
149
|
+
routes_py = self.project_root / "routes.py"
|
|
150
|
+
|
|
151
|
+
content = '''# routes.py
|
|
152
|
+
"""
|
|
153
|
+
URL Configuration - YOUR CUSTOM ROUTES
|
|
154
|
+
|
|
155
|
+
⚠️ UPGRADE-SAFE FILE - Your changes persist across framework updates! ⚠️
|
|
156
|
+
|
|
157
|
+
Define your custom URL patterns here.
|
|
158
|
+
"""
|
|
159
|
+
|
|
160
|
+
from createsonline.routing import path
|
|
161
|
+
from createsonline import render_template
|
|
162
|
+
|
|
163
|
+
# ============================================================================
|
|
164
|
+
# YOUR CUSTOM VIEW HANDLERS
|
|
165
|
+
# ============================================================================
|
|
166
|
+
|
|
167
|
+
async def home(request):
|
|
168
|
+
"""Modern homepage - uses templates/index.html"""
|
|
169
|
+
html = render_template("index.html")
|
|
170
|
+
|
|
171
|
+
from createsonline.server import InternalHTMLResponse
|
|
172
|
+
return InternalHTMLResponse(html)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
async def admin_login(request):
|
|
176
|
+
"""Admin login page - uses templates/admin_login.html"""
|
|
177
|
+
html = render_template("admin_login.html")
|
|
178
|
+
|
|
179
|
+
from createsonline.server import InternalHTMLResponse
|
|
180
|
+
return InternalHTMLResponse(html)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
async def about(request):
|
|
184
|
+
"""About page"""
|
|
185
|
+
return {"page": "about", "content": "About our application"}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
async def not_found(request):
|
|
189
|
+
"""Custom 404 error page - uses templates/404.html"""
|
|
190
|
+
html = render_template("404.html")
|
|
191
|
+
|
|
192
|
+
from createsonline.server import InternalHTMLResponse
|
|
193
|
+
return InternalHTMLResponse(html, status_code=404)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
# ============================================================================
|
|
197
|
+
# URL PATTERNS - Add your routes here!
|
|
198
|
+
# ============================================================================
|
|
199
|
+
|
|
200
|
+
urlpatterns = [
|
|
201
|
+
path('/', home, name='home'),
|
|
202
|
+
path('/admin', admin_login, name='admin-login'),
|
|
203
|
+
path('/about', about, name='about'),
|
|
204
|
+
]
|
|
205
|
+
|
|
206
|
+
# Add more routes below:
|
|
207
|
+
# path('/api/data', get_data, name='api-data'),
|
|
208
|
+
# path('/contact', contact_form, methods=['POST'], name='contact'),
|
|
209
|
+
'''
|
|
210
|
+
|
|
211
|
+
self._create_file(routes_py, content, "Created routes.py")
|
|
212
|
+
|
|
213
|
+
def _ensure_user_config_py(self):
|
|
214
|
+
"""Create user_config.py if it doesn't exist"""
|
|
215
|
+
config_py = self.project_root / "user_config.py"
|
|
216
|
+
|
|
217
|
+
content = '''# user_config.py
|
|
218
|
+
"""
|
|
219
|
+
User Configuration for CREATESONLINE
|
|
220
|
+
|
|
221
|
+
⚠️ UPGRADE-SAFE FILE - Your changes persist across framework updates! ⚠️
|
|
222
|
+
|
|
223
|
+
Add your custom settings here.
|
|
224
|
+
"""
|
|
225
|
+
|
|
226
|
+
# Application settings
|
|
227
|
+
DEBUG = True
|
|
228
|
+
SECRET_KEY = "change-this-in-production"
|
|
229
|
+
|
|
230
|
+
# Database settings (if needed)
|
|
231
|
+
# DATABASE_URL = "sqlite:///app.db"
|
|
232
|
+
# DATABASE_TIMEOUT = 30
|
|
233
|
+
|
|
234
|
+
# Custom middleware (optional)
|
|
235
|
+
# MIDDLEWARE = [
|
|
236
|
+
# 'myapp.middleware.CustomMiddleware',
|
|
237
|
+
# ]
|
|
238
|
+
|
|
239
|
+
# API Keys (use environment variables in production!)
|
|
240
|
+
# API_KEY = "your-api-key"
|
|
241
|
+
|
|
242
|
+
# Add any custom settings you need below:
|
|
243
|
+
'''
|
|
244
|
+
|
|
245
|
+
self._create_file(config_py, content, "Created user_config.py")
|
|
246
|
+
|
|
247
|
+
def _ensure_templates(self):
|
|
248
|
+
"""Copy modern templates from framework if they don't exist"""
|
|
249
|
+
# Try to copy from framework's templates directory first
|
|
250
|
+
framework_templates = self.framework_dir / "templates"
|
|
251
|
+
|
|
252
|
+
template_files = ["base.html", "index.html", "admin_login.html", "404.html"]
|
|
253
|
+
|
|
254
|
+
for template_name in template_files:
|
|
255
|
+
source = framework_templates / template_name
|
|
256
|
+
dest = self.project_root / "templates" / template_name
|
|
257
|
+
|
|
258
|
+
# Skip if template already exists
|
|
259
|
+
if dest.exists():
|
|
260
|
+
self.user_files_skipped.append(f"templates/{template_name}")
|
|
261
|
+
continue
|
|
262
|
+
|
|
263
|
+
# Copy from framework or use fallback
|
|
264
|
+
if source.exists():
|
|
265
|
+
shutil.copy2(source, dest)
|
|
266
|
+
self.user_files_created.append(f"templates/{template_name}")
|
|
267
|
+
if self.verbose:
|
|
268
|
+
print(f" Created template: templates/{template_name}")
|
|
269
|
+
else:
|
|
270
|
+
# Fallback to hardcoded templates
|
|
271
|
+
content = self._get_fallback_template(template_name)
|
|
272
|
+
if content:
|
|
273
|
+
self._create_file(dest, content, f"Created template")
|
|
274
|
+
|
|
275
|
+
def _ensure_static_files(self):
|
|
276
|
+
"""Copy default static files if they don't exist"""
|
|
277
|
+
# Copy from framework's static directory if needed
|
|
278
|
+
framework_static = self.framework_dir / "static"
|
|
279
|
+
project_static = self.project_root / "static"
|
|
280
|
+
|
|
281
|
+
# Create favicon files
|
|
282
|
+
self._create_favicon_files()
|
|
283
|
+
|
|
284
|
+
# Create logo image
|
|
285
|
+
self._create_logo_file()
|
|
286
|
+
|
|
287
|
+
# Create site.webmanifest
|
|
288
|
+
self._create_webmanifest()
|
|
289
|
+
|
|
290
|
+
# Create guide.html and examples.html
|
|
291
|
+
self._create_file(
|
|
292
|
+
project_static / "guide.html",
|
|
293
|
+
self._get_guide_html(),
|
|
294
|
+
"Created guide page"
|
|
295
|
+
)
|
|
296
|
+
self._create_file(
|
|
297
|
+
project_static / "examples.html",
|
|
298
|
+
self._get_examples_html(),
|
|
299
|
+
"Created examples page"
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
def _ensure_documentation(self):
|
|
303
|
+
"""Create documentation files"""
|
|
304
|
+
self._create_file(
|
|
305
|
+
self.project_root / "README.md",
|
|
306
|
+
self._get_readme_content(),
|
|
307
|
+
"Created README.md"
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
def _create_favicon_files(self):
|
|
311
|
+
"""Generate default favicon files (SVG and ICO)"""
|
|
312
|
+
# Create favicon.svg (modern browsers)
|
|
313
|
+
svg_content = '''<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
|
314
|
+
<rect width="100" height="100" fill="#000000"/>
|
|
315
|
+
<text x="50" y="70" font-family="Arial, sans-serif" font-size="70" font-weight="bold" text-anchor="middle" fill="#ffffff">C</text>
|
|
316
|
+
</svg>'''
|
|
317
|
+
self._create_file(
|
|
318
|
+
self.project_root / "favicon.svg",
|
|
319
|
+
svg_content,
|
|
320
|
+
"Created favicon.svg"
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
# Create basic favicon.ico (16x16 transparent PNG in ICO format)
|
|
324
|
+
# Using minimal valid ICO file structure
|
|
325
|
+
import base64
|
|
326
|
+
# This is a valid 16x16 transparent favicon.ico
|
|
327
|
+
ico_base64 = "AAABAAEAEBAAAAAAAABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEA"
|
|
328
|
+
|
|
329
|
+
try:
|
|
330
|
+
ico_bytes = base64.b64decode(ico_base64)
|
|
331
|
+
ico_path = self.project_root / "favicon.ico"
|
|
332
|
+
if not ico_path.exists():
|
|
333
|
+
ico_path.write_bytes(ico_bytes)
|
|
334
|
+
self.user_files_created.append("favicon.ico")
|
|
335
|
+
if self.verbose:
|
|
336
|
+
print(f" Created favicon.ico")
|
|
337
|
+
except Exception as e:
|
|
338
|
+
if self.verbose:
|
|
339
|
+
print(f" Skipped favicon.ico (creation failed: {e})")
|
|
340
|
+
|
|
341
|
+
def _create_logo_file(self):
|
|
342
|
+
"""Generate default logo SVG"""
|
|
343
|
+
logo_content = '''<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 100">
|
|
344
|
+
<rect width="400" height="100" fill="transparent"/>
|
|
345
|
+
<text x="10" y="70" font-family="Arial, sans-serif" font-size="60" font-weight="bold" fill="#000000">CREATESONLINE</text>
|
|
346
|
+
</svg>'''
|
|
347
|
+
self._create_file(
|
|
348
|
+
self.project_root / "logo.png",
|
|
349
|
+
logo_content,
|
|
350
|
+
"Created logo.svg"
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
# Also create logo.png as SVG (browsers will handle it)
|
|
354
|
+
self._create_file(
|
|
355
|
+
self.project_root / "logo-header-h200@2x.png",
|
|
356
|
+
logo_content,
|
|
357
|
+
"Created logo-header"
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
def _create_webmanifest(self):
|
|
361
|
+
"""Create site.webmanifest for PWA support"""
|
|
362
|
+
manifest_content = '''{
|
|
363
|
+
"name": "CREATESONLINE App",
|
|
364
|
+
"short_name": "CREATESONLINE",
|
|
365
|
+
"icons": [
|
|
366
|
+
{
|
|
367
|
+
"src": "/favicon.svg",
|
|
368
|
+
"sizes": "any",
|
|
369
|
+
"type": "image/svg+xml"
|
|
370
|
+
}
|
|
371
|
+
],
|
|
372
|
+
"theme_color": "#000000",
|
|
373
|
+
"background_color": "#ffffff",
|
|
374
|
+
"display": "standalone"
|
|
375
|
+
}'''
|
|
376
|
+
self._create_file(
|
|
377
|
+
self.project_root / "site.webmanifest",
|
|
378
|
+
manifest_content,
|
|
379
|
+
"Created site.webmanifest"
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
def _get_fallback_template(self, template_name: str) -> str:
|
|
383
|
+
"""Get fallback template content when framework templates aren't available"""
|
|
384
|
+
if template_name == "base.html":
|
|
385
|
+
return self._get_base_template()
|
|
386
|
+
elif template_name == "index.html":
|
|
387
|
+
return self._get_index_template()
|
|
388
|
+
elif template_name == "admin_login.html":
|
|
389
|
+
return self._get_admin_login_template()
|
|
390
|
+
elif template_name == "404.html":
|
|
391
|
+
return self._get_404_template()
|
|
392
|
+
return None
|
|
393
|
+
|
|
394
|
+
def _get_base_template(self) -> str:
|
|
395
|
+
return '''<!DOCTYPE html>
|
|
396
|
+
<html lang="en">
|
|
397
|
+
<head>
|
|
398
|
+
<meta charset="UTF-8">
|
|
399
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
400
|
+
<title>{% block title %}CREATESONLINE App{% endblock %}</title>
|
|
401
|
+
<link rel="stylesheet" href="/static/css/base.css">
|
|
402
|
+
{% block extra_css %}{% endblock %}
|
|
403
|
+
</head>
|
|
404
|
+
<body>
|
|
405
|
+
<nav>
|
|
406
|
+
<h1>CREATESONLINE</h1>
|
|
407
|
+
<ul>
|
|
408
|
+
<li><a href="/">Home</a></li>
|
|
409
|
+
<li><a href="/about">About</a></li>
|
|
410
|
+
</ul>
|
|
411
|
+
</nav>
|
|
412
|
+
|
|
413
|
+
<main>
|
|
414
|
+
{% block content %}{% endblock %}
|
|
415
|
+
</main>
|
|
416
|
+
|
|
417
|
+
<footer>
|
|
418
|
+
<p>Powered by CREATESONLINE</p>
|
|
419
|
+
</footer>
|
|
420
|
+
|
|
421
|
+
<script src="/static/js/base.js"></script>
|
|
422
|
+
{% block extra_js %}{% endblock %}
|
|
423
|
+
</body>
|
|
424
|
+
</html>
|
|
425
|
+
'''
|
|
426
|
+
|
|
427
|
+
def _get_index_template(self) -> str:
|
|
428
|
+
return '''{% extends "base.html" %}
|
|
429
|
+
|
|
430
|
+
{% block title %}Home - CREATESONLINE{% endblock %}
|
|
431
|
+
|
|
432
|
+
{% block content %}
|
|
433
|
+
<div class="container">
|
|
434
|
+
<h1>Welcome to CREATESONLINE</h1>
|
|
435
|
+
<p>Your AI-Native Web Framework is ready!</p>
|
|
436
|
+
|
|
437
|
+
<div class="features">
|
|
438
|
+
<div class="feature">
|
|
439
|
+
<h3>🚀 Zero Setup</h3>
|
|
440
|
+
<p>Everything works out of the box</p>
|
|
441
|
+
</div>
|
|
442
|
+
<div class="feature">
|
|
443
|
+
<h3>🤖 AI-First</h3>
|
|
444
|
+
<p>Built for intelligent applications</p>
|
|
445
|
+
</div>
|
|
446
|
+
<div class="feature">
|
|
447
|
+
<h3>⚡ Fast</h3>
|
|
448
|
+
<p>Pure Python async/await</p>
|
|
449
|
+
</div>
|
|
450
|
+
</div>
|
|
451
|
+
</div>
|
|
452
|
+
{% endblock %}
|
|
453
|
+
'''
|
|
454
|
+
|
|
455
|
+
def _get_admin_login_template(self) -> str:
|
|
456
|
+
return '''{% extends "base.html" %}
|
|
457
|
+
|
|
458
|
+
{% block title %}Admin Login - CREATESONLINE{% endblock %}
|
|
459
|
+
|
|
460
|
+
{% block content %}
|
|
461
|
+
<div class="login-container">
|
|
462
|
+
<h1>Admin Login</h1>
|
|
463
|
+
<p class="subtitle">Enter your credentials to access the admin panel</p>
|
|
464
|
+
|
|
465
|
+
<form method="POST" action="/admin/login">
|
|
466
|
+
<div class="form-group">
|
|
467
|
+
<label for="username">Username</label>
|
|
468
|
+
<input type="text" id="username" name="username" required>
|
|
469
|
+
</div>
|
|
470
|
+
|
|
471
|
+
<div class="form-group">
|
|
472
|
+
<label for="password">Password</label>
|
|
473
|
+
<input type="password" id="password" name="password" required>
|
|
474
|
+
</div>
|
|
475
|
+
|
|
476
|
+
<button type="submit">Sign In</button>
|
|
477
|
+
</form>
|
|
478
|
+
|
|
479
|
+
<div class="back-link">
|
|
480
|
+
<a href="/">Back to Home</a>
|
|
481
|
+
</div>
|
|
482
|
+
</div>
|
|
483
|
+
{% endblock %}
|
|
484
|
+
'''
|
|
485
|
+
|
|
486
|
+
def _get_404_template(self) -> str:
|
|
487
|
+
return '''<!DOCTYPE html>
|
|
488
|
+
<html lang="en">
|
|
489
|
+
<head>
|
|
490
|
+
<meta charset="UTF-8">
|
|
491
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
492
|
+
<title>404 - Page Not Found</title>
|
|
493
|
+
|
|
494
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
|
495
|
+
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
|
496
|
+
|
|
497
|
+
<style>
|
|
498
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
499
|
+
body {
|
|
500
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
501
|
+
background: #0a0a0a; color: #ffffff; min-height: 100vh;
|
|
502
|
+
display: flex; align-items: center; justify-content: center; padding: 20px;
|
|
503
|
+
}
|
|
504
|
+
.container {
|
|
505
|
+
max-width: 600px; width: 100%; text-align: center;
|
|
506
|
+
background: #1a1a1a; padding: 60px 40px; border-radius: 16px; border: 1px solid #2a2a2a;
|
|
507
|
+
}
|
|
508
|
+
.error-code {
|
|
509
|
+
font-size: 8em; font-weight: 700;
|
|
510
|
+
background: linear-gradient(135deg, #ffffff 0%, #666666 100%);
|
|
511
|
+
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
|
512
|
+
margin-bottom: 20px; line-height: 1;
|
|
513
|
+
}
|
|
514
|
+
h1 { font-size: 2em; margin-bottom: 15px; }
|
|
515
|
+
p { color: #999999; font-size: 1.1em; margin-bottom: 40px; line-height: 1.6; }
|
|
516
|
+
.btn {
|
|
517
|
+
display: inline-block; padding: 15px 35px; background: #ffffff;
|
|
518
|
+
color: #000000; text-decoration: none; border-radius: 8px;
|
|
519
|
+
font-weight: 600; transition: all 0.3s ease;
|
|
520
|
+
}
|
|
521
|
+
.btn:hover { background: #f0f0f0; transform: translateY(-2px); }
|
|
522
|
+
</style>
|
|
523
|
+
</head>
|
|
524
|
+
<body>
|
|
525
|
+
<div class="container">
|
|
526
|
+
<div class="error-code">404</div>
|
|
527
|
+
<h1>Page Not Found</h1>
|
|
528
|
+
<p>The page you're looking for doesn't exist or has been moved.</p>
|
|
529
|
+
<a href="/" class="btn">Go Home</a>
|
|
530
|
+
</div>
|
|
531
|
+
</body>
|
|
532
|
+
</html>
|
|
533
|
+
'''
|
|
534
|
+
|
|
535
|
+
def _get_guide_html(self) -> str:
|
|
536
|
+
return '''<!DOCTYPE html>
|
|
537
|
+
<html lang="en">
|
|
538
|
+
<head>
|
|
539
|
+
<meta charset="UTF-8">
|
|
540
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
541
|
+
<title>Quick Start Guide - CREATESONLINE</title>
|
|
542
|
+
<style>
|
|
543
|
+
body { font-family: system-ui; max-width: 900px; margin: 0 auto; padding: 2rem; }
|
|
544
|
+
h1 { color: #2563eb; }
|
|
545
|
+
pre { background: #1e1e1e; color: #d4d4d4; padding: 1rem; border-radius: 8px; overflow-x: auto; }
|
|
546
|
+
code { font-family: 'Courier New', monospace; }
|
|
547
|
+
</style>
|
|
548
|
+
</head>
|
|
549
|
+
<body>
|
|
550
|
+
<h1>Quick Start Guide</h1>
|
|
551
|
+
<p>Welcome to CREATESONLINE! This guide will get you started in 5 minutes.</p>
|
|
552
|
+
|
|
553
|
+
<h2>1. Installation</h2>
|
|
554
|
+
<pre><code>pip install createsonline</code></pre>
|
|
555
|
+
|
|
556
|
+
<h2>2. Create Your App</h2>
|
|
557
|
+
<p>Just run <code>python main.py</code> and everything is automatically set up!</p>
|
|
558
|
+
|
|
559
|
+
<h2>3. Add Custom Routes</h2>
|
|
560
|
+
<p>Edit <code>routes.py</code> to add your own routes:</p>
|
|
561
|
+
<pre><code>async def my_api(request):
|
|
562
|
+
return {"data": "Hello World"}
|
|
563
|
+
|
|
564
|
+
urlpatterns.append(path('/api/data', my_api))</code></pre>
|
|
565
|
+
|
|
566
|
+
<h2>4. Customize</h2>
|
|
567
|
+
<p>All your files are upgrade-safe! Edit freely:</p>
|
|
568
|
+
<ul>
|
|
569
|
+
<li><code>main.py</code> - App configuration</li>
|
|
570
|
+
<li><code>routes.py</code> - URL routes</li>
|
|
571
|
+
<li><code>user_config.py</code> - Settings</li>
|
|
572
|
+
<li><code>templates/</code> - HTML templates</li>
|
|
573
|
+
<li><code>static/</code> - CSS, JS, images</li>
|
|
574
|
+
</ul>
|
|
575
|
+
|
|
576
|
+
<p><a href="/">← Back to Home</a></p>
|
|
577
|
+
</body>
|
|
578
|
+
</html>
|
|
579
|
+
'''
|
|
580
|
+
|
|
581
|
+
def _get_examples_html(self) -> str:
|
|
582
|
+
return '''<!DOCTYPE html>
|
|
583
|
+
<html lang="en">
|
|
584
|
+
<head>
|
|
585
|
+
<meta charset="UTF-8">
|
|
586
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
587
|
+
<title>Code Examples - CREATESONLINE</title>
|
|
588
|
+
<style>
|
|
589
|
+
body { font-family: system-ui; max-width: 1000px; margin: 0 auto; padding: 2rem; }
|
|
590
|
+
h1 { color: #2563eb; }
|
|
591
|
+
.example { background: #f8f9fa; padding: 1.5rem; margin: 1rem 0; border-radius: 8px; }
|
|
592
|
+
pre { background: #1e1e1e; color: #d4d4d4; padding: 1rem; border-radius: 8px; overflow-x: auto; }
|
|
593
|
+
code { font-family: 'Courier New', monospace; }
|
|
594
|
+
</style>
|
|
595
|
+
</head>
|
|
596
|
+
<body>
|
|
597
|
+
<h1>Code Examples</h1>
|
|
598
|
+
|
|
599
|
+
<div class="example">
|
|
600
|
+
<h3>1. Simple API Endpoint</h3>
|
|
601
|
+
<pre><code>async def get_users(request):
|
|
602
|
+
return {"users": ["Alice", "Bob", "Charlie"]}
|
|
603
|
+
|
|
604
|
+
urlpatterns.append(path('/api/users', get_users))</code></pre>
|
|
605
|
+
</div>
|
|
606
|
+
|
|
607
|
+
<div class="example">
|
|
608
|
+
<h3>2. POST Request Handler</h3>
|
|
609
|
+
<pre><code>async def create_user(request):
|
|
610
|
+
data = await request.json()
|
|
611
|
+
return {"created": data, "id": 123}
|
|
612
|
+
|
|
613
|
+
urlpatterns.append(
|
|
614
|
+
path('/api/users', create_user, methods=['POST'])
|
|
615
|
+
)</code></pre>
|
|
616
|
+
</div>
|
|
617
|
+
|
|
618
|
+
<div class="example">
|
|
619
|
+
<h3>3. Serve HTML Template</h3>
|
|
620
|
+
<pre><code>async def about_page(request):
|
|
621
|
+
# Return HTML template
|
|
622
|
+
return render_template('about.html')
|
|
623
|
+
|
|
624
|
+
urlpatterns.append(path('/about', about_page))</code></pre>
|
|
625
|
+
</div>
|
|
626
|
+
|
|
627
|
+
<p><a href="/">← Back to Home</a></p>
|
|
628
|
+
</body>
|
|
629
|
+
</html>
|
|
630
|
+
'''
|
|
631
|
+
|
|
632
|
+
def _get_readme_content(self) -> str:
|
|
633
|
+
return '''# My CREATESONLINE Application
|
|
634
|
+
|
|
635
|
+
> **⚠️ UPGRADE-SAFE PROJECT** - All customizations are preserved during framework upgrades!
|
|
636
|
+
|
|
637
|
+
## Quick Start
|
|
638
|
+
|
|
639
|
+
```bash
|
|
640
|
+
# Install
|
|
641
|
+
pip install createsonline
|
|
642
|
+
|
|
643
|
+
# Run
|
|
644
|
+
python main.py
|
|
645
|
+
|
|
646
|
+
# Visit
|
|
647
|
+
http://localhost:8000
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
## Project Structure
|
|
651
|
+
|
|
652
|
+
```
|
|
653
|
+
your-project/
|
|
654
|
+
├── main.py ✅ Your app bootstrap (SAFE TO EDIT)
|
|
655
|
+
├── routes.py ✅ Your URL routes (SAFE TO EDIT)
|
|
656
|
+
├── user_config.py ✅ Your settings (SAFE TO EDIT)
|
|
657
|
+
├── templates/ ✅ Your HTML templates (SAFE TO EDIT)
|
|
658
|
+
└── static/ ✅ Your CSS/JS/images (SAFE TO EDIT)
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
## Add Custom Routes
|
|
662
|
+
|
|
663
|
+
Edit `routes.py`:
|
|
664
|
+
|
|
665
|
+
```python
|
|
666
|
+
async def my_view(request):
|
|
667
|
+
return {"message": "Hello!"}
|
|
668
|
+
|
|
669
|
+
urlpatterns.append(path('/custom', my_view))
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
## Upgrade Framework
|
|
673
|
+
|
|
674
|
+
```bash
|
|
675
|
+
pip install --upgrade createsonline
|
|
676
|
+
# Your files are automatically preserved! ✨
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
## Documentation
|
|
680
|
+
|
|
681
|
+
- Quick Start: `/static/guide.html`
|
|
682
|
+
- Examples: `/static/examples.html`
|
|
683
|
+
- GitHub: https://github.com/meahmedh/createsonline
|
|
684
|
+
'''
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
def auto_discover_routes(app):
|
|
688
|
+
"""
|
|
689
|
+
Automatically discover and register routes from routes.py
|
|
690
|
+
|
|
691
|
+
This function is called by main.py to register all user routes.
|
|
692
|
+
Routes from routes.py OVERRIDE any routes defined elsewhere.
|
|
693
|
+
It protects against errors - silent by default.
|
|
694
|
+
"""
|
|
695
|
+
import logging
|
|
696
|
+
logger = logging.getLogger("createsonline")
|
|
697
|
+
|
|
698
|
+
try:
|
|
699
|
+
from routes import urlpatterns
|
|
700
|
+
|
|
701
|
+
registered = 0
|
|
702
|
+
for route in urlpatterns:
|
|
703
|
+
for method in route.methods:
|
|
704
|
+
decorator = getattr(app, method.lower())
|
|
705
|
+
handler = route.handler
|
|
706
|
+
|
|
707
|
+
# Handle class-based views
|
|
708
|
+
if hasattr(handler, 'dispatch'):
|
|
709
|
+
instance = handler() if callable(handler) else handler
|
|
710
|
+
async def view_wrapper(request, instance=instance):
|
|
711
|
+
return await instance.dispatch(request)
|
|
712
|
+
# Force override - routes.py takes priority
|
|
713
|
+
route_key = f"{method}:{route.path}"
|
|
714
|
+
if route_key in app.routes:
|
|
715
|
+
logger.debug(f"Overriding route: {route_key}")
|
|
716
|
+
decorator(route.path)(view_wrapper)
|
|
717
|
+
else:
|
|
718
|
+
# Force override - routes.py takes priority
|
|
719
|
+
route_key = f"{method}:{route.path}"
|
|
720
|
+
if route_key in app.routes:
|
|
721
|
+
logger.debug(f"Overriding route: {route_key}")
|
|
722
|
+
decorator(route.path)(handler)
|
|
723
|
+
|
|
724
|
+
registered += 1
|
|
725
|
+
|
|
726
|
+
# Register custom 404 handler if exists
|
|
727
|
+
try:
|
|
728
|
+
from routes import not_found
|
|
729
|
+
app.routes['not_found'] = not_found
|
|
730
|
+
except ImportError:
|
|
731
|
+
pass
|
|
732
|
+
|
|
733
|
+
# Silent success - routes registered
|
|
734
|
+
logger.debug(f"Auto-discovered {registered} routes from routes.py")
|
|
735
|
+
return True
|
|
736
|
+
|
|
737
|
+
except ImportError:
|
|
738
|
+
# No routes.py found - silent, use defaults
|
|
739
|
+
return False
|
|
740
|
+
except Exception as e:
|
|
741
|
+
# Error loading routes - log but don't crash
|
|
742
|
+
logger.warning(f"Error loading routes: {e}")
|
|
743
|
+
return False
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
def init_project_if_needed(project_root: Path = None, verbose: bool = False):
|
|
747
|
+
"""
|
|
748
|
+
Initialize project structure if needed.
|
|
749
|
+
Called automatically when the app starts.
|
|
750
|
+
Silent by default - only shows messages if verbose=True.
|
|
751
|
+
|
|
752
|
+
Args:
|
|
753
|
+
project_root: Root directory for the project (default: current directory)
|
|
754
|
+
verbose: If True, print progress messages
|
|
755
|
+
"""
|
|
756
|
+
project_root = project_root or Path.cwd()
|
|
757
|
+
|
|
758
|
+
# Check if project is already initialized
|
|
759
|
+
main_py = project_root / "main.py"
|
|
760
|
+
routes_py = project_root / "routes.py"
|
|
761
|
+
|
|
762
|
+
# Always create missing static assets (favicon, logo, manifest)
|
|
763
|
+
initializer = ProjectInitializer(project_root)
|
|
764
|
+
initializer.verbose = verbose
|
|
765
|
+
initializer._create_directories()
|
|
766
|
+
initializer._ensure_static_files()
|
|
767
|
+
|
|
768
|
+
# Always ensure routes.py exists (even if main.py exists from old version)
|
|
769
|
+
if not routes_py.exists():
|
|
770
|
+
initializer._ensure_routes_py()
|
|
771
|
+
|
|
772
|
+
# If neither main.py nor routes.py exists, this is a new project
|
|
773
|
+
if not main_py.exists():
|
|
774
|
+
if verbose:
|
|
775
|
+
print("\nWelcome to CREATESONLINE!")
|
|
776
|
+
print("Setting up your project structure...")
|
|
777
|
+
|
|
778
|
+
result = initializer.initialize(verbose=verbose)
|
|
779
|
+
|
|
780
|
+
if verbose:
|
|
781
|
+
print("\nNext steps:")
|
|
782
|
+
print(" 1. Edit routes.py to add your custom routes")
|
|
783
|
+
print(" 2. Customize templates/ and static/ folders")
|
|
784
|
+
print(" 3. Run: python main.py")
|
|
785
|
+
print("\nHappy coding!\n")
|
|
786
|
+
|
|
787
|
+
return result
|
|
788
|
+
|
|
789
|
+
return {"success": True, "already_initialized": True}
|