fastbrick 0.4__py3-none-any.whl → 0.5.1__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.
- fastbrick/__init__.py +0 -0
- fastbrick/cli.py +262 -0
- {fastbrick-0.4.dist-info → fastbrick-0.5.1.dist-info}/METADATA +4 -6
- fastbrick-0.5.1.dist-info/RECORD +8 -0
- {fastbrick-0.4.dist-info → fastbrick-0.5.1.dist-info}/WHEEL +1 -1
- fastbrick-0.5.1.dist-info/entry_points.txt +3 -0
- fastbrick-0.5.1.dist-info/top_level.txt +1 -0
- fastbrick-0.4.dist-info/RECORD +0 -6
- fastbrick-0.4.dist-info/entry_points.txt +0 -3
- fastbrick-0.4.dist-info/top_level.txt +0 -1
- {fastbrick-0.4.dist-info → fastbrick-0.5.1.dist-info}/LICENSE +0 -0
fastbrick/__init__.py
ADDED
File without changes
|
fastbrick/cli.py
ADDED
@@ -0,0 +1,262 @@
|
|
1
|
+
import os
|
2
|
+
import click
|
3
|
+
from jinja2 import Template
|
4
|
+
|
5
|
+
# Default template for main.py
|
6
|
+
MAIN_TEMPLATE = """\
|
7
|
+
from fastapi import FastAPI
|
8
|
+
from middlewares.middleware import GlobalMiddleware
|
9
|
+
from settings.routing import router
|
10
|
+
from settings.database import engine, Base
|
11
|
+
|
12
|
+
# ✅ Create all tables in the database
|
13
|
+
Base.metadata.create_all(bind=engine)
|
14
|
+
|
15
|
+
app = FastAPI()
|
16
|
+
|
17
|
+
# ✅ Add Middleware
|
18
|
+
app.add_middleware(GlobalMiddleware)
|
19
|
+
|
20
|
+
# ✅ Include API routes
|
21
|
+
app.include_router(router)
|
22
|
+
|
23
|
+
@app.get("/")
|
24
|
+
def root():
|
25
|
+
return {"message": "Welcome to FastAPI Project"}
|
26
|
+
"""
|
27
|
+
|
28
|
+
# Default template for new app routers
|
29
|
+
APP_ROUTER_TEMPLATE = """\
|
30
|
+
from fastapi import APIRouter
|
31
|
+
|
32
|
+
router = APIRouter()
|
33
|
+
|
34
|
+
@router.get("/")
|
35
|
+
def {{ app_name }}_home():
|
36
|
+
return {"message": "Welcome to the {{ app_name }} app"}
|
37
|
+
"""
|
38
|
+
|
39
|
+
# Default template for database settings
|
40
|
+
DATABASE_TEMPLATE = """\
|
41
|
+
from sqlalchemy import create_engine, MetaData
|
42
|
+
from sqlalchemy.orm import sessionmaker, declarative_base
|
43
|
+
from settings.config import DATABASE_URL
|
44
|
+
|
45
|
+
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
46
|
+
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
47
|
+
Base = declarative_base()
|
48
|
+
|
49
|
+
# Dependency: Get DB Session
|
50
|
+
def get_db():
|
51
|
+
db = SessionLocal()
|
52
|
+
try:
|
53
|
+
yield db
|
54
|
+
finally:
|
55
|
+
db.close()
|
56
|
+
|
57
|
+
"""
|
58
|
+
|
59
|
+
# Template for dynamic routing
|
60
|
+
ROUTING_TEMPLATE = """\
|
61
|
+
import importlib
|
62
|
+
import pkgutil
|
63
|
+
from fastapi import APIRouter
|
64
|
+
import routers
|
65
|
+
|
66
|
+
# Import custom route mappings from routes.py
|
67
|
+
from routes import app_routes
|
68
|
+
|
69
|
+
router = APIRouter()
|
70
|
+
|
71
|
+
# Dynamically import and include all routers from 'routers' package
|
72
|
+
for _, module_name, _ in pkgutil.iter_modules(routers.__path__):
|
73
|
+
module = importlib.import_module(f"routers.{module_name}")
|
74
|
+
|
75
|
+
if hasattr(module, "router"):
|
76
|
+
prefix = app_routes.get(module_name, f"/{module_name}")
|
77
|
+
router.include_router(module.router, prefix=prefix, tags=[module_name.capitalize()])
|
78
|
+
"""
|
79
|
+
|
80
|
+
# routes.py template (Separate file for `app_routes`)
|
81
|
+
ROUTES_TEMPLATE = """\
|
82
|
+
# Users can modify this dictionary to change route prefixes
|
83
|
+
app_routes = {}
|
84
|
+
"""
|
85
|
+
|
86
|
+
MODELS_TEMPLATE = """\
|
87
|
+
from sqlalchemy import Column, Integer, String
|
88
|
+
from settings.database import Base
|
89
|
+
|
90
|
+
class User(Base):
|
91
|
+
__tablename__ = "users"
|
92
|
+
id = Column(Integer, primary_key=True, index=True)
|
93
|
+
email = Column(String, unique=True, index=True)
|
94
|
+
hashed_password = Column(String)
|
95
|
+
"""
|
96
|
+
|
97
|
+
SCHEMAS_TEMPLATE = """\
|
98
|
+
from pydantic import BaseModel
|
99
|
+
|
100
|
+
class UserBase(BaseModel):
|
101
|
+
email: str
|
102
|
+
hashed_password: str
|
103
|
+
|
104
|
+
"""
|
105
|
+
|
106
|
+
MIDDLEWARE_TEMPLATE = """\
|
107
|
+
import time
|
108
|
+
import logging
|
109
|
+
from fastapi import Request, Response
|
110
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
111
|
+
from settings.config import ALLOWED_IPS
|
112
|
+
|
113
|
+
# Configure logging
|
114
|
+
logging.basicConfig(level=logging.INFO)
|
115
|
+
logger = logging.getLogger("middleware")
|
116
|
+
|
117
|
+
|
118
|
+
class GlobalMiddleware(BaseHTTPMiddleware):
|
119
|
+
async def dispatch(self, request: Request, call_next):
|
120
|
+
start_time = time.time()
|
121
|
+
client_ip = request.client.host
|
122
|
+
|
123
|
+
# ✅ Allow Only Specific IPs
|
124
|
+
if "*" not in ALLOWED_IPS and client_ip not in ALLOWED_IPS:
|
125
|
+
return Response(content="⛔ Access Denied", status_code=403)
|
126
|
+
|
127
|
+
# ⏳ Process Request
|
128
|
+
try:
|
129
|
+
response = await call_next(request)
|
130
|
+
except Exception as e:
|
131
|
+
logger.error(f"❌ Exception: {e}")
|
132
|
+
return Response(content="🚨 Internal Server Error", status_code=500)
|
133
|
+
|
134
|
+
# ⏱️ Track Request Time
|
135
|
+
process_time = time.time() - start_time
|
136
|
+
response.headers["X-Process-Time"] = str(process_time)
|
137
|
+
response.headers["X-App-Version"] = "1.0.0"
|
138
|
+
|
139
|
+
logger.info(f"✅ {request.method} {request.url} - {response.status_code} ({process_time:.4f}s)")
|
140
|
+
return response
|
141
|
+
"""
|
142
|
+
|
143
|
+
CONFIG_TEMPLATE="""\
|
144
|
+
DATABASE_URL = "sqlite:///./test.db"
|
145
|
+
|
146
|
+
# Define Allowed IPs
|
147
|
+
ALLOWED_IPS = {"*"} # Set specific IPs to restrict access, or "*" to allow all
|
148
|
+
"""
|
149
|
+
|
150
|
+
@click.group()
|
151
|
+
def cli():
|
152
|
+
"""FastAPI Project Generator CLI"""
|
153
|
+
pass
|
154
|
+
|
155
|
+
@click.command(name="create-project")
|
156
|
+
@click.argument("name")
|
157
|
+
def create_project(name):
|
158
|
+
"""Create a new FastAPI project with a clean structure."""
|
159
|
+
|
160
|
+
if os.path.exists(name):
|
161
|
+
click.echo(f"❌ Error: Project '{name}' already exists! Choose a different name.")
|
162
|
+
return
|
163
|
+
|
164
|
+
# Create project directories
|
165
|
+
os.makedirs(f"{name}/routers", exist_ok=True)
|
166
|
+
os.makedirs(f"{name}/settings", exist_ok=True)
|
167
|
+
os.makedirs(f"{name}/alembic", exist_ok=True)
|
168
|
+
os.makedirs(f"{name}/middlewares", exist_ok=True)
|
169
|
+
|
170
|
+
# Create main.py
|
171
|
+
with open(f"{name}/main.py", "w") as f:
|
172
|
+
f.write(Template(MAIN_TEMPLATE).render(project_name=name))
|
173
|
+
|
174
|
+
# Create settings/database.py
|
175
|
+
with open(f"{name}/settings/database.py", "w") as f:
|
176
|
+
f.write(DATABASE_TEMPLATE)
|
177
|
+
|
178
|
+
# Create settings/routing.py
|
179
|
+
with open(f"{name}/settings/routing.py", "w") as f:
|
180
|
+
f.write(ROUTING_TEMPLATE)
|
181
|
+
|
182
|
+
# Create settings/config.py
|
183
|
+
with open(f"{name}/settings/config.py", "w") as f:
|
184
|
+
f.write(CONFIG_TEMPLATE)
|
185
|
+
|
186
|
+
# Create middlewares/middleware.py
|
187
|
+
with open(f"{name}/middlewares/middleware.py", "w") as f:
|
188
|
+
f.write(MIDDLEWARE_TEMPLATE)
|
189
|
+
|
190
|
+
# Create models.py
|
191
|
+
with open(f"{name}/models.py", "w") as f:
|
192
|
+
f.write(MODELS_TEMPLATE)
|
193
|
+
|
194
|
+
# Create schemas.py
|
195
|
+
with open(f"{name}/schemas.py", "w") as f:
|
196
|
+
f.write(SCHEMAS_TEMPLATE)
|
197
|
+
|
198
|
+
# Create routes.py
|
199
|
+
with open(f"{name}/routes.py", "w") as f:
|
200
|
+
f.write(ROUTES_TEMPLATE)
|
201
|
+
|
202
|
+
click.echo(f"✅ FastAPI project '{name}' created successfully!")
|
203
|
+
|
204
|
+
|
205
|
+
@click.command(name="create-app")
|
206
|
+
@click.argument("name")
|
207
|
+
def create_app(name):
|
208
|
+
"""Create a new FastAPI app (router) with database settings."""
|
209
|
+
app_path = f"routers/{name}.py"
|
210
|
+
settings_path = "settings/"
|
211
|
+
routing_config_path = os.path.join(settings_path, "routing.py")
|
212
|
+
|
213
|
+
if not os.path.exists("routers"):
|
214
|
+
click.echo("❌ Error: No 'routers' directory found! Are you inside a FastAPI project?")
|
215
|
+
return
|
216
|
+
|
217
|
+
if os.path.exists(app_path):
|
218
|
+
click.echo(f"❌ Error: App '{name}' already exists! Choose a different name.")
|
219
|
+
return
|
220
|
+
|
221
|
+
# Ensure settings folder exists
|
222
|
+
os.makedirs(settings_path, exist_ok=True)
|
223
|
+
|
224
|
+
# Create router file
|
225
|
+
with open(app_path, "w") as f:
|
226
|
+
f.write(Template(APP_ROUTER_TEMPLATE).render(app_name=name))
|
227
|
+
|
228
|
+
# Ensure database.py exists inside settings/
|
229
|
+
db_config_path = os.path.join(settings_path, "database.py")
|
230
|
+
if not os.path.exists(db_config_path):
|
231
|
+
with open(db_config_path, "w") as f:
|
232
|
+
f.write(DATABASE_TEMPLATE)
|
233
|
+
|
234
|
+
# Ensure routing.py exists inside settings/
|
235
|
+
if not os.path.exists(routing_config_path):
|
236
|
+
with open(routing_config_path, "w") as f:
|
237
|
+
f.write(ROUTING_TEMPLATE)
|
238
|
+
|
239
|
+
# Ensure routes.py exists outside settings/
|
240
|
+
if not os.path.exists("routes.py"):
|
241
|
+
with open("routes.py", "w") as f:
|
242
|
+
f.write(ROUTING_TEMPLATE)
|
243
|
+
|
244
|
+
# Update `settings/routing.py` with the new default prefix
|
245
|
+
with open("routes.py", "r+") as f:
|
246
|
+
content = f.read()
|
247
|
+
if "app_routes = {" in content:
|
248
|
+
new_entry = f' "{name}": "/{name}",\n'
|
249
|
+
if f'"{name}":' not in content: # Prevent duplicate entries
|
250
|
+
updated_content = content.replace("app_routes = {", f"app_routes = {{\n{new_entry}")
|
251
|
+
f.seek(0)
|
252
|
+
f.write(updated_content)
|
253
|
+
f.truncate()
|
254
|
+
|
255
|
+
click.echo(f"✅ App '{name}' created successfully")
|
256
|
+
|
257
|
+
|
258
|
+
cli.add_command(create_project)
|
259
|
+
cli.add_command(create_app)
|
260
|
+
|
261
|
+
if __name__ == "__main__":
|
262
|
+
cli()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fastbrick
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.5.1
|
4
4
|
Summary: A CLI tool for generating FastAPI projects and apps
|
5
5
|
Home-page: UNKNOWN
|
6
6
|
Author: Sandeep Singh Negi
|
@@ -12,9 +12,8 @@ Classifier: License :: OSI Approved :: MIT License
|
|
12
12
|
Classifier: Operating System :: OS Independent
|
13
13
|
Requires-Python: >=3.7
|
14
14
|
Description-Content-Type: text/markdown
|
15
|
-
License-File: LICENSE
|
16
|
-
Requires-Dist: click
|
17
15
|
Requires-Dist: fastapi
|
16
|
+
Requires-Dist: click
|
18
17
|
Requires-Dist: jinja2
|
19
18
|
|
20
19
|
# FastBrick 🚀
|
@@ -60,17 +59,16 @@ fastbirck create-app my_app
|
|
60
59
|
```
|
61
60
|
myproject/
|
62
61
|
│── main.py # Entry point for FastAPI app
|
63
|
-
│── routes.py # Contains '
|
64
|
-
│── alembic.ini
|
62
|
+
│── routes.py # Contains 'app routes'
|
65
63
|
│── models.py
|
66
64
|
│── schemas.py
|
67
65
|
│── middlewares/
|
68
66
|
│ ├── middleware.py # Global middleware logic
|
69
67
|
│── routers/ # API route modules
|
70
68
|
│── settings/
|
69
|
+
│ ├── config.py # Database configuration
|
71
70
|
│ ├── database.py # Database configuration
|
72
71
|
│ ├── routing.py # Router configurations
|
73
|
-
│── alembic/
|
74
72
|
```
|
75
73
|
|
76
74
|
This structure ensures modularity and scalability for your FastAPI project. Adjust the folders and files as needed based on your project requirements.
|
@@ -0,0 +1,8 @@
|
|
1
|
+
fastbrick/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
fastbrick/cli.py,sha256=-G5Rg7oxfmgDY36ygs8JlD4a5uQajw-LbSFkJrGVJlU,7605
|
3
|
+
fastbrick-0.5.1.dist-info/LICENSE,sha256=VueWmlho5XxqjRpByo5KlMuWT7T-_AjQOVeiXRad66o,244
|
4
|
+
fastbrick-0.5.1.dist-info/METADATA,sha256=fF_9DTuXs7BM6ccDJy3dgMHaLRLLDw_02Sxb6gtwUYc,1729
|
5
|
+
fastbrick-0.5.1.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
6
|
+
fastbrick-0.5.1.dist-info/entry_points.txt,sha256=Wvu-TvvfanU7zthWJ0G1nNQVIyPjBNQvuc55-XsDx5M,49
|
7
|
+
fastbrick-0.5.1.dist-info/top_level.txt,sha256=wP0Z3gRwVZj2qAQ1WHAvQtmRwCjEFFUe-lKKijnnf-Y,10
|
8
|
+
fastbrick-0.5.1.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
fastbrick
|
fastbrick-0.4.dist-info/RECORD
DELETED
@@ -1,6 +0,0 @@
|
|
1
|
-
fastbrick-0.4.dist-info/LICENSE,sha256=VueWmlho5XxqjRpByo5KlMuWT7T-_AjQOVeiXRad66o,244
|
2
|
-
fastbrick-0.4.dist-info/METADATA,sha256=Ce94KytoRlAE9o0P4j_SVzZktPYU4VbEgTV9UNL9NTo,1741
|
3
|
-
fastbrick-0.4.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
4
|
-
fastbrick-0.4.dist-info/entry_points.txt,sha256=E25qyIpsqpZCjdza4bncHZhd7tmQwWqnWaz8eTz5luE,45
|
5
|
-
fastbrick-0.4.dist-info/top_level.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
6
|
-
fastbrick-0.4.dist-info/RECORD,,
|
@@ -1 +0,0 @@
|
|
1
|
-
|
File without changes
|