WebForge 1.0.0__tar.gz

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.
@@ -0,0 +1,142 @@
1
+ Metadata-Version: 2.4
2
+ Name: WebForge
3
+ Version: 1.0.0
4
+ Summary: πŸ”₯ Create web applications easily β€” a Flask-powered micro-framework
5
+ Home-page: https://github.com/yourname/webforge
6
+ Author: Your Name
7
+ Author-email: you@example.com
8
+ License: MIT
9
+ Project-URL: Homepage, https://github.com/yourname/webforge
10
+ Project-URL: Repository, https://github.com/yourname/webforge
11
+ Project-URL: Issues, https://github.com/yourname/webforge/issues
12
+ Requires-Python: >=3.8
13
+ Description-Content-Type: text/markdown
14
+ Requires-Dist: flask>=3.0.0
15
+ Requires-Dist: python-dotenv>=1.0.0
16
+ Dynamic: author
17
+ Dynamic: author-email
18
+ Dynamic: home-page
19
+ Dynamic: requires-python
20
+
21
+ # πŸ”₯ WebForge
22
+
23
+ **Create web applications easily** β€” a minimal Flask-powered framework with clean syntax.
24
+
25
+ ```bash
26
+ pip install WebForge
27
+ webforge new mon-site
28
+ cd mon-site
29
+ python backend/server/forge.py
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Structure d'un projet WebForge
35
+
36
+ ```
37
+ mon-site/
38
+ β”œβ”€β”€ backend/
39
+ β”‚ β”œβ”€β”€ requirements.txt
40
+ β”‚ └── server/
41
+ β”‚ └── forge.py ← CΕ“ur du serveur
42
+ └── public/
43
+ β”œβ”€β”€ pages/
44
+ β”‚ └── index.html ← Templates HTML
45
+ β”œβ”€β”€ script/
46
+ β”‚ └── app.js ← JavaScript client
47
+ └── service/
48
+ └── style.css ← Fichiers statiques
49
+ ```
50
+
51
+ ---
52
+
53
+ ## Γ‰crire `forge.py`
54
+
55
+ ```python
56
+ from WebForge import webforge, security, env as web
57
+
58
+ # Variables d'environnement
59
+ password = web.env("PASSWORD")
60
+ ip = "192.168.1.123"
61
+
62
+ # ─── Routes ──────────────────────────────────────
63
+ @web.app("/")
64
+ def index():
65
+ web.route("style", "/public/service/style.css")
66
+ return web.render("index.html")
67
+
68
+ @web.app("/login")
69
+ def login():
70
+ return web.render("login.html")
71
+
72
+ # ─── SΓ©curitΓ© ────────────────────────────────────
73
+ @web.security("/")
74
+ def secure_home():
75
+ if not web.password(password): # vΓ©rif mot de passe en session
76
+ return web.redirect("/login")
77
+ if not web.ip(ip): # vΓ©rif IP
78
+ return web.abort(403)
79
+
80
+ # ─── DΓ©marrage ───────────────────────────────────
81
+ if __name__ == "__main__":
82
+ web.runapp(debug=True, port=5000, host="0.0.0.0")
83
+ ```
84
+
85
+ ---
86
+
87
+ ## API complète
88
+
89
+ ### Routes
90
+
91
+ | Fonction | Description |
92
+ |----------|-------------|
93
+ | `@web.app("/path")` | DΓ©finit une route |
94
+ | `@web.security("/path")` | Middleware de sΓ©curitΓ© pour une route |
95
+ | `web.route("name", "/path/to/file")` | Route vers un fichier statique |
96
+
97
+ ### RΓ©ponses
98
+
99
+ | Fonction | Description |
100
+ |----------|-------------|
101
+ | `web.render("page.html", **ctx)` | Rend un template HTML |
102
+ | `web.redirect("/url")` | Redirige vers une URL |
103
+ | `web.abort(403)` | Retourne une erreur HTTP |
104
+
105
+ ### SΓ©curitΓ©
106
+
107
+ | Fonction | Description |
108
+ |----------|-------------|
109
+ | `web.password("secret")` | VΓ©rifie le mot de passe en session |
110
+ | `web.ip("192.168.1.1")` | VΓ©rifie l'IP du client (accepte aussi une liste) |
111
+
112
+ ### Utilitaires
113
+
114
+ | Fonction | Description |
115
+ |----------|-------------|
116
+ | `web.env("VAR")` | Lit une variable d'environnement |
117
+ | `web.runapp(debug, port, host)` | DΓ©marre le serveur |
118
+
119
+ ---
120
+
121
+ ## CLI
122
+
123
+ ```bash
124
+ webforge new <nom> # CrΓ©e un nouveau projet
125
+ webforge run # Lance le projet courant
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Installation pour le dΓ©veloppement
131
+
132
+ ```bash
133
+ git clone https://github.com/yourname/webforge
134
+ cd webforge
135
+ pip install -e .
136
+ ```
137
+
138
+ ---
139
+
140
+ ## Licence
141
+
142
+ MIT
@@ -0,0 +1,122 @@
1
+ # πŸ”₯ WebForge
2
+
3
+ **Create web applications easily** β€” a minimal Flask-powered framework with clean syntax.
4
+
5
+ ```bash
6
+ pip install WebForge
7
+ webforge new mon-site
8
+ cd mon-site
9
+ python backend/server/forge.py
10
+ ```
11
+
12
+ ---
13
+
14
+ ## Structure d'un projet WebForge
15
+
16
+ ```
17
+ mon-site/
18
+ β”œβ”€β”€ backend/
19
+ β”‚ β”œβ”€β”€ requirements.txt
20
+ β”‚ └── server/
21
+ β”‚ └── forge.py ← CΕ“ur du serveur
22
+ └── public/
23
+ β”œβ”€β”€ pages/
24
+ β”‚ └── index.html ← Templates HTML
25
+ β”œβ”€β”€ script/
26
+ β”‚ └── app.js ← JavaScript client
27
+ └── service/
28
+ └── style.css ← Fichiers statiques
29
+ ```
30
+
31
+ ---
32
+
33
+ ## Γ‰crire `forge.py`
34
+
35
+ ```python
36
+ from WebForge import webforge, security, env as web
37
+
38
+ # Variables d'environnement
39
+ password = web.env("PASSWORD")
40
+ ip = "192.168.1.123"
41
+
42
+ # ─── Routes ──────────────────────────────────────
43
+ @web.app("/")
44
+ def index():
45
+ web.route("style", "/public/service/style.css")
46
+ return web.render("index.html")
47
+
48
+ @web.app("/login")
49
+ def login():
50
+ return web.render("login.html")
51
+
52
+ # ─── SΓ©curitΓ© ────────────────────────────────────
53
+ @web.security("/")
54
+ def secure_home():
55
+ if not web.password(password): # vΓ©rif mot de passe en session
56
+ return web.redirect("/login")
57
+ if not web.ip(ip): # vΓ©rif IP
58
+ return web.abort(403)
59
+
60
+ # ─── DΓ©marrage ───────────────────────────────────
61
+ if __name__ == "__main__":
62
+ web.runapp(debug=True, port=5000, host="0.0.0.0")
63
+ ```
64
+
65
+ ---
66
+
67
+ ## API complète
68
+
69
+ ### Routes
70
+
71
+ | Fonction | Description |
72
+ |----------|-------------|
73
+ | `@web.app("/path")` | DΓ©finit une route |
74
+ | `@web.security("/path")` | Middleware de sΓ©curitΓ© pour une route |
75
+ | `web.route("name", "/path/to/file")` | Route vers un fichier statique |
76
+
77
+ ### RΓ©ponses
78
+
79
+ | Fonction | Description |
80
+ |----------|-------------|
81
+ | `web.render("page.html", **ctx)` | Rend un template HTML |
82
+ | `web.redirect("/url")` | Redirige vers une URL |
83
+ | `web.abort(403)` | Retourne une erreur HTTP |
84
+
85
+ ### SΓ©curitΓ©
86
+
87
+ | Fonction | Description |
88
+ |----------|-------------|
89
+ | `web.password("secret")` | VΓ©rifie le mot de passe en session |
90
+ | `web.ip("192.168.1.1")` | VΓ©rifie l'IP du client (accepte aussi une liste) |
91
+
92
+ ### Utilitaires
93
+
94
+ | Fonction | Description |
95
+ |----------|-------------|
96
+ | `web.env("VAR")` | Lit une variable d'environnement |
97
+ | `web.runapp(debug, port, host)` | DΓ©marre le serveur |
98
+
99
+ ---
100
+
101
+ ## CLI
102
+
103
+ ```bash
104
+ webforge new <nom> # CrΓ©e un nouveau projet
105
+ webforge run # Lance le projet courant
106
+ ```
107
+
108
+ ---
109
+
110
+ ## Installation pour le dΓ©veloppement
111
+
112
+ ```bash
113
+ git clone https://github.com/yourname/webforge
114
+ cd webforge
115
+ pip install -e .
116
+ ```
117
+
118
+ ---
119
+
120
+ ## Licence
121
+
122
+ MIT
@@ -0,0 +1,93 @@
1
+ """
2
+ WebForge - Create web applications easily
3
+
4
+ Usage in forge.py:
5
+ from WebForge import webforge, security, env as web
6
+
7
+ @web.app("/")
8
+ def index():
9
+ web.route("style", "/public/service/style.css")
10
+ return web.render("index.html")
11
+
12
+ @web.security("/")
13
+ def secure_home():
14
+ if not web.password(pw): return web.redirect("/login")
15
+ if not web.ip(allowed): return web.abort(403)
16
+
17
+ web.runapp(debug=True, port=5000)
18
+ """
19
+
20
+ from .core import WebForgeApp
21
+
22
+ # Single global app instance shared across the whole project
23
+ _app = WebForgeApp()
24
+
25
+
26
+ class _Namespace:
27
+ """
28
+ The WebForge namespace object.
29
+ Imported as `env as web` so that `web.env()`, `web.app()`, etc. all work.
30
+ Also callable directly: web("MY_VAR") == web.env("MY_VAR")
31
+ """
32
+
33
+ def __init__(self, app: WebForgeApp):
34
+ self._app = app
35
+
36
+ def __call__(self, key: str, default=None):
37
+ return self._app.env(key, default)
38
+
39
+ # ── Environment ─────────────────────────────────────
40
+ def env(self, key: str, default=None):
41
+ """Read an environment variable (like os.getenv)"""
42
+ return self._app.env(key, default)
43
+
44
+ # ── Routing ─────────────────────────────────────────
45
+ def app(self, route_path: str, methods=None):
46
+ """Decorator: register a route handler"""
47
+ return self._app.app(route_path, methods)
48
+
49
+ def route(self, name: str, path: str):
50
+ """Register a named static file route"""
51
+ return self._app.add_static_route(name, path)
52
+
53
+ # ── Responses ───────────────────────────────────────
54
+ def render(self, template: str, **context):
55
+ """Render an HTML template from public/pages/"""
56
+ return self._app.render(template, **context)
57
+
58
+ def redirect(self, url: str, code: int = 302):
59
+ """HTTP redirect"""
60
+ return self._app.redirect(url, code)
61
+
62
+ def abort(self, code: int, message=None):
63
+ """Abort with an HTTP error code"""
64
+ return self._app.abort(code, message)
65
+
66
+ # ── Security ────────────────────────────────────────
67
+ def security(self, route_path: str):
68
+ """Decorator: register security middleware for a route"""
69
+ return self._app.security(route_path)
70
+
71
+ def password(self, stored_password: str) -> bool:
72
+ """Check session password against stored value"""
73
+ return self._app.check_password(stored_password)
74
+
75
+ def ip(self, allowed_ip) -> bool:
76
+ """Check if request IP matches allowed value(s)"""
77
+ return self._app.check_ip(allowed_ip)
78
+
79
+ # ── Server ──────────────────────────────────────────
80
+ def runapp(self, debug: bool = False, port: int = 5000, host: str = "0.0.0.0"):
81
+ """Start the WebForge development server"""
82
+ return self._app.run(debug=debug, port=port, host=host)
83
+
84
+
85
+ _ns = _Namespace(_app)
86
+
87
+ # Public exports:
88
+ # from WebForge import webforge, security, env as web
89
+ webforge = _app # raw app instance (advanced use)
90
+ security = _ns.security # importable standalone decorator
91
+ env = _ns # becomes `web` via `env as web`
92
+
93
+ __all__ = ["WebForgeApp", "webforge", "security", "env"]
@@ -0,0 +1,222 @@
1
+ """
2
+ WebForge Core - Main application engine
3
+ """
4
+
5
+ import os
6
+ import sys
7
+ import functools
8
+ from pathlib import Path
9
+ from typing import Callable, Optional, List, Union
10
+
11
+ try:
12
+ from flask import (
13
+ Flask, request, session, redirect as flask_redirect,
14
+ abort as flask_abort, render_template_string, send_file,
15
+ make_response
16
+ )
17
+ FLASK_AVAILABLE = True
18
+ except ImportError:
19
+ FLASK_AVAILABLE = False
20
+
21
+
22
+ class WebForgeResponse:
23
+ """Wrapper for WebForge responses"""
24
+ def __init__(self, content, status=200, mimetype="text/html"):
25
+ self.content = content
26
+ self.status = status
27
+ self.mimetype = mimetype
28
+ self._is_redirect = False
29
+ self._is_abort = False
30
+ self._redirect_url = None
31
+ self._abort_code = None
32
+
33
+
34
+ class WebForgeApp:
35
+ """Main WebForge application class"""
36
+
37
+ def __init__(self):
38
+ self._routes = {} # route -> handler function
39
+ self._security = {} # route -> security function
40
+ self._static_routes = {} # name -> file path
41
+ self._template_dir = None
42
+ self._flask_app = None
43
+ self._project_root = None
44
+
45
+ def _find_project_root(self):
46
+ """Auto-detect project root from calling script location"""
47
+ # Walk up from the forge.py location
48
+ frame = sys._getframe(2)
49
+ caller_file = frame.f_globals.get("__file__", "")
50
+ if caller_file:
51
+ # forge.py is in backend/server/, so project root is 2 levels up
52
+ p = Path(caller_file).resolve()
53
+ for _ in range(3):
54
+ p = p.parent
55
+ if (p / "public").exists():
56
+ return p
57
+ return Path(caller_file).resolve().parent.parent.parent
58
+ return Path.cwd()
59
+
60
+ def _get_flask_app(self):
61
+ if self._flask_app is None:
62
+ if not FLASK_AVAILABLE:
63
+ raise RuntimeError(
64
+ "Flask is required. Install it with: pip install flask"
65
+ )
66
+ if self._project_root is None:
67
+ self._project_root = Path.cwd()
68
+
69
+ template_folder = str(self._project_root / "public" / "pages")
70
+ static_folder = str(self._project_root / "public" / "service")
71
+
72
+ self._flask_app = Flask(
73
+ __name__,
74
+ template_folder=template_folder,
75
+ static_folder=static_folder,
76
+ static_url_path="/static"
77
+ )
78
+ self._flask_app.secret_key = os.urandom(32)
79
+
80
+ # Register all routes
81
+ self._register_all_routes()
82
+
83
+ return self._flask_app
84
+
85
+ def _register_all_routes(self):
86
+ flask_app = self._flask_app
87
+
88
+ for route_path, handler in self._routes.items():
89
+ security_fn = self._security.get(route_path)
90
+ self._register_flask_route(flask_app, route_path, handler, security_fn)
91
+
92
+ # Register named static routes
93
+ for name, file_path in self._static_routes.items():
94
+ self._register_static_file(flask_app, name, file_path)
95
+
96
+ def _register_flask_route(self, flask_app, route_path, handler, security_fn):
97
+ endpoint = f"webforge_{route_path.replace('/', '_').strip('_') or 'index'}"
98
+
99
+ @flask_app.route(route_path, methods=["GET", "POST", "PUT", "DELETE", "PATCH"])
100
+ @functools.wraps(handler)
101
+ def view_func():
102
+ # Run security checks first
103
+ if security_fn:
104
+ self._current_request_context = True
105
+ result = security_fn()
106
+ if result is not None:
107
+ return self._process_response(result)
108
+
109
+ # Run route handler
110
+ result = handler()
111
+ return self._process_response(result)
112
+
113
+ view_func.__name__ = endpoint
114
+ # Flask already registered it via decorator above
115
+
116
+ def _register_static_file(self, flask_app, name, file_path):
117
+ """Register a named static file route"""
118
+ endpoint = f"static_{name}"
119
+ abs_path = self._project_root / file_path.lstrip("/")
120
+
121
+ @flask_app.route(f"/static/{name}")
122
+ def serve_static():
123
+ return send_file(str(abs_path))
124
+
125
+ serve_static.__name__ = endpoint
126
+
127
+ def _process_response(self, result):
128
+ """Convert WebForge response to Flask response"""
129
+ if result is None:
130
+ return ("", 200)
131
+
132
+ if isinstance(result, str):
133
+ return result
134
+
135
+ if isinstance(result, WebForgeResponse):
136
+ if result._is_redirect:
137
+ return flask_redirect(result._redirect_url, result.status)
138
+ if result._is_abort:
139
+ flask_abort(result._abort_code)
140
+ return (result.content, result.status)
141
+
142
+ # Flask response object passthrough
143
+ return result
144
+
145
+ # ─── Public API ─────────────────────────────────────────────────────────
146
+
147
+ def env(self, key: str, default=None):
148
+ """Get environment variable"""
149
+ return os.environ.get(key, default)
150
+
151
+ def app(self, route_path: str, methods: Optional[List[str]] = None):
152
+ """Decorator to register a route"""
153
+ def decorator(fn: Callable):
154
+ self._routes[route_path] = fn
155
+ return fn
156
+ return decorator
157
+
158
+ def security(self, route_path: str):
159
+ """Decorator to register security middleware for a route"""
160
+ def decorator(fn: Callable):
161
+ self._security[route_path] = fn
162
+ return fn
163
+ return decorator
164
+
165
+ def add_static_route(self, name: str, path: str):
166
+ """Register a static file route"""
167
+ self._static_routes[name] = path
168
+
169
+ def render(self, template: str, **context):
170
+ """Render an HTML template from public/pages/"""
171
+ from flask import render_template
172
+ return render_template(template, **context)
173
+
174
+ def redirect(self, url: str, code: int = 302):
175
+ """Redirect to URL"""
176
+ return flask_redirect(url, code)
177
+
178
+ def abort(self, code: int, message: Optional[str] = None):
179
+ """Abort with HTTP error"""
180
+ if message:
181
+ from flask import Response
182
+ return Response(message, status=code)
183
+ flask_abort(code)
184
+
185
+ def check_password(self, stored_password: str) -> bool:
186
+ """Check if current session has matching password"""
187
+ return session.get("password") == stored_password
188
+
189
+ def check_ip(self, allowed_ip: Union[str, List[str]]) -> bool:
190
+ """Check if request IP is in the allowed list"""
191
+ client_ip = request.remote_addr
192
+ if isinstance(allowed_ip, str):
193
+ return client_ip == allowed_ip
194
+ return client_ip in allowed_ip
195
+
196
+ def run(self, debug: bool = False, port: int = 5000, host: str = "0.0.0.0"):
197
+ """Start the development server"""
198
+ # Detect project root from the script that called run()
199
+ frame = sys._getframe(1)
200
+ caller_file = frame.f_globals.get("__file__", "")
201
+ if caller_file:
202
+ p = Path(caller_file).resolve().parent
203
+ # Search upward for a dir that has public/
204
+ for _ in range(5):
205
+ if (p / "public").exists():
206
+ self._project_root = p
207
+ break
208
+ p = p.parent
209
+ else:
210
+ self._project_root = Path(caller_file).resolve().parent
211
+
212
+ flask_app = self._get_flask_app()
213
+
214
+ print(f"""
215
+ ╔══════════════════════════════════════════════╗
216
+ β•‘ πŸ”₯ WebForge Server β•‘
217
+ β•‘ Running on http://{host}:{port} β•‘
218
+ β•‘ Debug: {str(debug):<37}β•‘
219
+ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
220
+ """)
221
+
222
+ flask_app.run(debug=debug, port=port, host=host)
@@ -0,0 +1,142 @@
1
+ Metadata-Version: 2.4
2
+ Name: WebForge
3
+ Version: 1.0.0
4
+ Summary: πŸ”₯ Create web applications easily β€” a Flask-powered micro-framework
5
+ Home-page: https://github.com/yourname/webforge
6
+ Author: Your Name
7
+ Author-email: you@example.com
8
+ License: MIT
9
+ Project-URL: Homepage, https://github.com/yourname/webforge
10
+ Project-URL: Repository, https://github.com/yourname/webforge
11
+ Project-URL: Issues, https://github.com/yourname/webforge/issues
12
+ Requires-Python: >=3.8
13
+ Description-Content-Type: text/markdown
14
+ Requires-Dist: flask>=3.0.0
15
+ Requires-Dist: python-dotenv>=1.0.0
16
+ Dynamic: author
17
+ Dynamic: author-email
18
+ Dynamic: home-page
19
+ Dynamic: requires-python
20
+
21
+ # πŸ”₯ WebForge
22
+
23
+ **Create web applications easily** β€” a minimal Flask-powered framework with clean syntax.
24
+
25
+ ```bash
26
+ pip install WebForge
27
+ webforge new mon-site
28
+ cd mon-site
29
+ python backend/server/forge.py
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Structure d'un projet WebForge
35
+
36
+ ```
37
+ mon-site/
38
+ β”œβ”€β”€ backend/
39
+ β”‚ β”œβ”€β”€ requirements.txt
40
+ β”‚ └── server/
41
+ β”‚ └── forge.py ← CΕ“ur du serveur
42
+ └── public/
43
+ β”œβ”€β”€ pages/
44
+ β”‚ └── index.html ← Templates HTML
45
+ β”œβ”€β”€ script/
46
+ β”‚ └── app.js ← JavaScript client
47
+ └── service/
48
+ └── style.css ← Fichiers statiques
49
+ ```
50
+
51
+ ---
52
+
53
+ ## Γ‰crire `forge.py`
54
+
55
+ ```python
56
+ from WebForge import webforge, security, env as web
57
+
58
+ # Variables d'environnement
59
+ password = web.env("PASSWORD")
60
+ ip = "192.168.1.123"
61
+
62
+ # ─── Routes ──────────────────────────────────────
63
+ @web.app("/")
64
+ def index():
65
+ web.route("style", "/public/service/style.css")
66
+ return web.render("index.html")
67
+
68
+ @web.app("/login")
69
+ def login():
70
+ return web.render("login.html")
71
+
72
+ # ─── SΓ©curitΓ© ────────────────────────────────────
73
+ @web.security("/")
74
+ def secure_home():
75
+ if not web.password(password): # vΓ©rif mot de passe en session
76
+ return web.redirect("/login")
77
+ if not web.ip(ip): # vΓ©rif IP
78
+ return web.abort(403)
79
+
80
+ # ─── DΓ©marrage ───────────────────────────────────
81
+ if __name__ == "__main__":
82
+ web.runapp(debug=True, port=5000, host="0.0.0.0")
83
+ ```
84
+
85
+ ---
86
+
87
+ ## API complète
88
+
89
+ ### Routes
90
+
91
+ | Fonction | Description |
92
+ |----------|-------------|
93
+ | `@web.app("/path")` | DΓ©finit une route |
94
+ | `@web.security("/path")` | Middleware de sΓ©curitΓ© pour une route |
95
+ | `web.route("name", "/path/to/file")` | Route vers un fichier statique |
96
+
97
+ ### RΓ©ponses
98
+
99
+ | Fonction | Description |
100
+ |----------|-------------|
101
+ | `web.render("page.html", **ctx)` | Rend un template HTML |
102
+ | `web.redirect("/url")` | Redirige vers une URL |
103
+ | `web.abort(403)` | Retourne une erreur HTTP |
104
+
105
+ ### SΓ©curitΓ©
106
+
107
+ | Fonction | Description |
108
+ |----------|-------------|
109
+ | `web.password("secret")` | VΓ©rifie le mot de passe en session |
110
+ | `web.ip("192.168.1.1")` | VΓ©rifie l'IP du client (accepte aussi une liste) |
111
+
112
+ ### Utilitaires
113
+
114
+ | Fonction | Description |
115
+ |----------|-------------|
116
+ | `web.env("VAR")` | Lit une variable d'environnement |
117
+ | `web.runapp(debug, port, host)` | DΓ©marre le serveur |
118
+
119
+ ---
120
+
121
+ ## CLI
122
+
123
+ ```bash
124
+ webforge new <nom> # CrΓ©e un nouveau projet
125
+ webforge run # Lance le projet courant
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Installation pour le dΓ©veloppement
131
+
132
+ ```bash
133
+ git clone https://github.com/yourname/webforge
134
+ cd webforge
135
+ pip install -e .
136
+ ```
137
+
138
+ ---
139
+
140
+ ## Licence
141
+
142
+ MIT
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.py
4
+ WebForge/__init__.py
5
+ WebForge/core.py
6
+ WebForge.egg-info/PKG-INFO
7
+ WebForge.egg-info/SOURCES.txt
8
+ WebForge.egg-info/dependency_links.txt
9
+ WebForge.egg-info/entry_points.txt
10
+ WebForge.egg-info/requires.txt
11
+ WebForge.egg-info/top_level.txt
12
+ WebForge_cli/cli.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ webforge = webforge_cli.cli:main
@@ -0,0 +1,2 @@
1
+ flask>=3.0.0
2
+ python-dotenv>=1.0.0
@@ -0,0 +1,2 @@
1
+ WebForge
2
+ WebForge_cli
@@ -0,0 +1,248 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ WebForge CLI - Scaffold new projects
4
+ Usage: webforge new <project-name>
5
+ webforge run
6
+ """
7
+
8
+ import sys
9
+ import os
10
+ import argparse
11
+ from pathlib import Path
12
+
13
+
14
+ FORGE_PY_TEMPLATE = '''\
15
+ from WebForge import webforge, security, env as web
16
+
17
+ # ─── Environment ─────────────────────────────────────────────────────────────
18
+ # password = web.env("PASSWORD")
19
+ # ip = "192.168.1.1"
20
+
21
+ # ─── Routes ──────────────────────────────────────────────────────────────────
22
+ @web.app("/")
23
+ def index():
24
+ web.route("style", "/public/service/style.css")
25
+ return web.render("index.html")
26
+
27
+ @web.app("/login")
28
+ def login():
29
+ return web.render("login.html")
30
+
31
+ # ─── Security ────────────────────────────────────────────────────────────────
32
+ # @web.security("/")
33
+ # def secure_home():
34
+ # if not web.password(password):
35
+ # return web.redirect("/login")
36
+ # if not web.ip(ip):
37
+ # return web.abort(403)
38
+
39
+ # ─── Run ─────────────────────────────────────────────────────────────────────
40
+ if __name__ == "__main__":
41
+ web.runapp(debug=True, port=5000, host="0.0.0.0")
42
+ '''
43
+
44
+ INDEX_HTML_TEMPLATE = '''\
45
+ <!DOCTYPE html>
46
+ <html lang="en">
47
+ <head>
48
+ <meta charset="UTF-8" />
49
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
50
+ <title>WebForge App</title>
51
+ <link rel="stylesheet" href="/static/style" />
52
+ </head>
53
+ <body>
54
+ <main>
55
+ <h1>πŸ”₯ Welcome to WebForge</h1>
56
+ <p>Your app is running. Edit <code>public/pages/index.html</code> to get started.</p>
57
+ <a href="/login">Go to Login</a>
58
+ </main>
59
+ <script src="/public/script/app.js"></script>
60
+ </body>
61
+ </html>
62
+ '''
63
+
64
+ LOGIN_HTML_TEMPLATE = '''\
65
+ <!DOCTYPE html>
66
+ <html lang="en">
67
+ <head>
68
+ <meta charset="UTF-8" />
69
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
70
+ <title>Login - WebForge</title>
71
+ <link rel="stylesheet" href="/static/style" />
72
+ </head>
73
+ <body>
74
+ <main>
75
+ <h1>πŸ” Login</h1>
76
+ <form method="POST" action="/login">
77
+ <input type="password" name="password" placeholder="Password" required />
78
+ <button type="submit">Sign In</button>
79
+ </form>
80
+ </main>
81
+ </body>
82
+ </html>
83
+ '''
84
+
85
+ STYLE_CSS_TEMPLATE = '''\
86
+ /* WebForge default styles */
87
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
88
+
89
+ body {
90
+ font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
91
+ background: #0f0f13;
92
+ color: #e8e8f0;
93
+ min-height: 100vh;
94
+ display: flex;
95
+ align-items: center;
96
+ justify-content: center;
97
+ }
98
+
99
+ main {
100
+ text-align: center;
101
+ padding: 3rem;
102
+ max-width: 600px;
103
+ }
104
+
105
+ h1 {
106
+ font-size: 2.5rem;
107
+ margin-bottom: 1rem;
108
+ background: linear-gradient(135deg, #ff6b35, #f7c59f);
109
+ -webkit-background-clip: text;
110
+ -webkit-text-fill-color: transparent;
111
+ }
112
+
113
+ p { color: #a0a0b8; margin-bottom: 1.5rem; }
114
+
115
+ a, button {
116
+ display: inline-block;
117
+ padding: 0.75rem 2rem;
118
+ background: #ff6b35;
119
+ color: white;
120
+ border: none;
121
+ border-radius: 8px;
122
+ text-decoration: none;
123
+ cursor: pointer;
124
+ font-size: 1rem;
125
+ transition: background 0.2s;
126
+ }
127
+
128
+ a:hover, button:hover { background: #e85a25; }
129
+
130
+ input {
131
+ display: block;
132
+ width: 100%;
133
+ padding: 0.75rem 1rem;
134
+ margin-bottom: 1rem;
135
+ background: #1a1a24;
136
+ border: 1px solid #333;
137
+ border-radius: 8px;
138
+ color: #e8e8f0;
139
+ font-size: 1rem;
140
+ }
141
+
142
+ code {
143
+ background: #1a1a24;
144
+ padding: 0.2rem 0.5rem;
145
+ border-radius: 4px;
146
+ font-family: monospace;
147
+ color: #ff6b35;
148
+ }
149
+ '''
150
+
151
+ APP_JS_TEMPLATE = '''\
152
+ // WebForge App - client-side scripts
153
+ console.log("πŸ”₯ WebForge App loaded");
154
+ '''
155
+
156
+ REQUIREMENTS_TEMPLATE = '''\
157
+ WebForge
158
+ flask>=3.0.0
159
+ python-dotenv>=1.0.0
160
+ '''
161
+
162
+ GITIGNORE_TEMPLATE = '''\
163
+ __pycache__/
164
+ *.py[cod]
165
+ .env
166
+ *.env
167
+ venv/
168
+ .venv/
169
+ '''
170
+
171
+
172
+ def scaffold(name: str):
173
+ base = Path(name)
174
+ if base.exists():
175
+ print(f"❌ Directory '{name}' already exists.")
176
+ sys.exit(1)
177
+
178
+ dirs = [
179
+ base / "backend" / "server",
180
+ base / "public" / "pages",
181
+ base / "public" / "script",
182
+ base / "public" / "service",
183
+ ]
184
+ for d in dirs:
185
+ d.mkdir(parents=True)
186
+
187
+ files = {
188
+ base / "backend" / "server" / "forge.py": FORGE_PY_TEMPLATE,
189
+ base / "backend" / "requirements.txt": REQUIREMENTS_TEMPLATE,
190
+ base / "public" / "pages" / "index.html": INDEX_HTML_TEMPLATE,
191
+ base / "public" / "pages" / "login.html": LOGIN_HTML_TEMPLATE,
192
+ base / "public" / "service" / "style.css": STYLE_CSS_TEMPLATE,
193
+ base / "public" / "script" / "app.js": APP_JS_TEMPLATE,
194
+ base / ".gitignore": GITIGNORE_TEMPLATE,
195
+ }
196
+
197
+ for path, content in files.items():
198
+ path.write_text(content, encoding="utf-8")
199
+
200
+ print(f"""
201
+ ╔══════════════════════════════════════════════╗
202
+ β•‘ πŸ”₯ WebForge Project Created! β•‘
203
+ ╠══════════════════════════════════════════════╣
204
+ β•‘ Project: {name:<35}β•‘
205
+ ╠══════════════════════════════════════════════╣
206
+ β•‘ Next steps: β•‘
207
+ β•‘ cd {name:<39}β•‘
208
+ β•‘ pip install -r backend/requirements.txt β•‘
209
+ β•‘ python backend/server/forge.py β•‘
210
+ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
211
+ """)
212
+
213
+
214
+ def run_server():
215
+ """Run the forge.py in the current project"""
216
+ forge = Path("backend/server/forge.py")
217
+ if not forge.exists():
218
+ print("❌ No forge.py found. Are you in a WebForge project directory?")
219
+ sys.exit(1)
220
+ os.execv(sys.executable, [sys.executable, str(forge)])
221
+
222
+
223
+ def main():
224
+ parser = argparse.ArgumentParser(
225
+ prog="webforge",
226
+ description="πŸ”₯ WebForge - Create web apps easily"
227
+ )
228
+ subparsers = parser.add_subparsers(dest="command")
229
+
230
+ # webforge new <name>
231
+ new_parser = subparsers.add_parser("new", help="Create a new WebForge project")
232
+ new_parser.add_argument("name", help="Project name")
233
+
234
+ # webforge run
235
+ subparsers.add_parser("run", help="Run the current WebForge project")
236
+
237
+ args = parser.parse_args()
238
+
239
+ if args.command == "new":
240
+ scaffold(args.name)
241
+ elif args.command == "run":
242
+ run_server()
243
+ else:
244
+ parser.print_help()
245
+
246
+
247
+ if __name__ == "__main__":
248
+ main()
@@ -0,0 +1,26 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "WebForge"
7
+ version = "1.0.0"
8
+ description = "πŸ”₯ Create web applications easily β€” a Flask-powered micro-framework"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "MIT" }
12
+ dependencies = [
13
+ "flask>=3.0.0",
14
+ "python-dotenv>=1.0.0",
15
+ ]
16
+
17
+ [project.scripts]
18
+ webforge = "webforge_cli.cli:main"
19
+
20
+ [project.urls]
21
+ Homepage = "https://github.com/yourname/webforge"
22
+ Repository = "https://github.com/yourname/webforge"
23
+ Issues = "https://github.com/yourname/webforge/issues"
24
+
25
+ [tool.setuptools.packages.find]
26
+ include = ["WebForge*", "webforge_cli*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,41 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ with open("README.md", "r", encoding="utf-8") as f:
4
+ long_description = f.read()
5
+
6
+ setup(
7
+ name="WebForge",
8
+ version="1.0.0",
9
+ author="Your Name",
10
+ author_email="you@example.com",
11
+ description="πŸ”₯ Create web applications easily β€” a Flask-powered micro-framework",
12
+ long_description=long_description,
13
+ long_description_content_type="text/markdown",
14
+ url="https://github.com/yourname/webforge",
15
+ packages=find_packages(),
16
+ include_package_data=True,
17
+ python_requires=">=3.8",
18
+ install_requires=[
19
+ "flask>=3.0.0",
20
+ "python-dotenv>=1.0.0",
21
+ ],
22
+ entry_points={
23
+ "console_scripts": [
24
+ "webforge=webforge_cli.cli:main",
25
+ ],
26
+ },
27
+ classifiers=[
28
+ "Development Status :: 4 - Beta",
29
+ "Intended Audience :: Developers",
30
+ "License :: OSI Approved :: MIT License",
31
+ "Programming Language :: Python :: 3",
32
+ "Programming Language :: Python :: 3.8",
33
+ "Programming Language :: Python :: 3.9",
34
+ "Programming Language :: Python :: 3.10",
35
+ "Programming Language :: Python :: 3.11",
36
+ "Programming Language :: Python :: 3.12",
37
+ "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
38
+ "Topic :: Software Development :: Libraries :: Application Frameworks",
39
+ ],
40
+ keywords="web framework flask simple easy webforge",
41
+ )