shafikul-cli 2.1.5__tar.gz → 2.2.6__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.
- {shafikul_cli-2.1.5/shafikul_cli.egg-info → shafikul_cli-2.2.6}/PKG-INFO +3 -3
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/README.md +2 -2
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/pyproject.toml +1 -1
- shafikul_cli-2.2.6/shafikul_cli/asset/icon.png +0 -0
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/main.py +70 -5
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/utility/create_file_content.py +13 -8
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/utility/helper_function.py +174 -42
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/utility/install_dependicy.py +3 -3
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6/shafikul_cli.egg-info}/PKG-INFO +3 -3
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli.egg-info/SOURCES.txt +1 -0
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/LICENSE +0 -0
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/setup.cfg +0 -0
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/__init__.py +0 -0
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/asset/__init__.py +0 -0
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/asset/logo.png +0 -0
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/utility/__init__.py +0 -0
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/utility/create_file_structure.py +0 -0
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/utility/create_html_file.py +0 -0
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/utility/ensure_db.py +0 -0
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/utility/ensure_main.py +0 -0
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/utility/env_generate.py +0 -0
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli.egg-info/dependency_links.txt +0 -0
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli.egg-info/entry_points.txt +0 -0
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli.egg-info/requires.txt +0 -0
- {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: shafikul_cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.6
|
|
4
4
|
Summary: CLI tool for FastAPI scaffolding with router, models, database, and templates.
|
|
5
5
|
Author-email: Md Shafikul Islam <buildwithshafikul@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/build-with-shafikul/shafikul_cli
|
|
@@ -21,7 +21,7 @@ Dynamic: license-file
|
|
|
21
21
|
|
|
22
22
|
<div align="center">
|
|
23
23
|
|
|
24
|
-

|
|
25
25
|

|
|
26
26
|

|
|
27
27
|
[](https://github.com/build-with-shafikul/shafikul_cli)
|
|
@@ -175,7 +175,7 @@ your-project/
|
|
|
175
175
|
│
|
|
176
176
|
├── 📂 static/ # Served static files
|
|
177
177
|
│ └── images/
|
|
178
|
-
│ └──
|
|
178
|
+
│ └── icon.png
|
|
179
179
|
│
|
|
180
180
|
├── 📂 utility/ # Helper functions
|
|
181
181
|
│ └── helper_function.py
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
<div align="center">
|
|
6
6
|
|
|
7
|
-

|
|
8
8
|

|
|
9
9
|

|
|
10
10
|
[](https://github.com/build-with-shafikul/shafikul_cli)
|
|
@@ -158,7 +158,7 @@ your-project/
|
|
|
158
158
|
│
|
|
159
159
|
├── 📂 static/ # Served static files
|
|
160
160
|
│ └── images/
|
|
161
|
-
│ └──
|
|
161
|
+
│ └── icon.png
|
|
162
162
|
│
|
|
163
163
|
├── 📂 utility/ # Helper functions
|
|
164
164
|
│ └── helper_function.py
|
|
Binary file
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import os, subprocess
|
|
2
2
|
import typer
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
from typing import Optional
|
|
5
|
+
import questionary
|
|
4
6
|
from rich.console import Console
|
|
5
7
|
from rich.panel import Panel
|
|
6
8
|
from rich.text import Text
|
|
@@ -34,6 +36,60 @@ def show_banner():
|
|
|
34
36
|
|
|
35
37
|
console.print(panel)
|
|
36
38
|
|
|
39
|
+
# =========================================================
|
|
40
|
+
# Select Folder DIR
|
|
41
|
+
# =========================================================
|
|
42
|
+
def select_project_directory() -> Path:
|
|
43
|
+
current_dir = Path.cwd()
|
|
44
|
+
|
|
45
|
+
choice = questionary.select(
|
|
46
|
+
"Where do you want to create the project?",
|
|
47
|
+
choices=[
|
|
48
|
+
"New folder",
|
|
49
|
+
"Current directory",
|
|
50
|
+
"Default (demo_folder)",
|
|
51
|
+
],
|
|
52
|
+
default="Default (demo_folder)",
|
|
53
|
+
).ask()
|
|
54
|
+
|
|
55
|
+
if choice is None:
|
|
56
|
+
raise typer.Abort()
|
|
57
|
+
|
|
58
|
+
if choice == "Current directory":
|
|
59
|
+
return current_dir
|
|
60
|
+
|
|
61
|
+
if choice == "Default (demo_folder)":
|
|
62
|
+
project_dir = current_dir / "demo_folder"
|
|
63
|
+
|
|
64
|
+
else:
|
|
65
|
+
folder_name = questionary.text(
|
|
66
|
+
"Enter your project folder name:"
|
|
67
|
+
).ask()
|
|
68
|
+
|
|
69
|
+
if not folder_name or not folder_name.strip():
|
|
70
|
+
raise typer.Abort()
|
|
71
|
+
|
|
72
|
+
folder_name = folder_name.strip()
|
|
73
|
+
|
|
74
|
+
if (
|
|
75
|
+
folder_name in {".", ".."}
|
|
76
|
+
or Path(folder_name).is_absolute()
|
|
77
|
+
or len(Path(folder_name).parts) != 1
|
|
78
|
+
):
|
|
79
|
+
typer.echo("Invalid folder name.")
|
|
80
|
+
raise typer.Exit(code=1)
|
|
81
|
+
|
|
82
|
+
project_dir = current_dir / folder_name
|
|
83
|
+
|
|
84
|
+
if project_dir.exists() and (
|
|
85
|
+
not project_dir.is_dir() or any(project_dir.iterdir())
|
|
86
|
+
):
|
|
87
|
+
typer.echo(f"Folder already exists and is not empty: {project_dir}")
|
|
88
|
+
raise typer.Exit(code=1)
|
|
89
|
+
|
|
90
|
+
project_dir.mkdir(parents=True, exist_ok=True)
|
|
91
|
+
|
|
92
|
+
return project_dir
|
|
37
93
|
|
|
38
94
|
# =========================================================
|
|
39
95
|
# 📘 ABOUT & (--version) COMMAND
|
|
@@ -78,9 +134,15 @@ def create_app():
|
|
|
78
134
|
# target: Optional[str] = typer.Argument(None, help="structure")
|
|
79
135
|
|
|
80
136
|
show_banner()
|
|
137
|
+
project_dir = select_project_directory()
|
|
138
|
+
# All relative paths will now point inside the selected folder
|
|
139
|
+
os.chdir(project_dir)
|
|
140
|
+
console.print(f"[bold cyan]Project location:[/] {project_dir.resolve()}")
|
|
81
141
|
|
|
82
142
|
folders = [
|
|
83
143
|
"app",
|
|
144
|
+
"app/models",
|
|
145
|
+
"app/database",
|
|
84
146
|
"config",
|
|
85
147
|
"asset/css",
|
|
86
148
|
"asset/images",
|
|
@@ -104,11 +166,12 @@ def create_app():
|
|
|
104
166
|
"README.md",
|
|
105
167
|
"main.py",
|
|
106
168
|
"config/app.py",
|
|
107
|
-
"app/database.py",
|
|
108
|
-
"app/models.py",
|
|
169
|
+
"app/database/database.py",
|
|
170
|
+
"app/models/__init__.py",
|
|
171
|
+
"app/models/users.py",
|
|
109
172
|
"asset/css/style.css",
|
|
110
|
-
"
|
|
111
|
-
"static/images/
|
|
173
|
+
"static/images/icon.png",
|
|
174
|
+
"static/images/favicon.ico",
|
|
112
175
|
"asset/js/script.js",
|
|
113
176
|
"router/api/users.py",
|
|
114
177
|
"router/web/auth.py",
|
|
@@ -129,7 +192,7 @@ def create_app():
|
|
|
129
192
|
os.makedirs(folder, exist_ok=True)
|
|
130
193
|
|
|
131
194
|
for file in files:
|
|
132
|
-
file_inside_content(file)
|
|
195
|
+
file_inside_content(file, project_version('shafikul-cli'))
|
|
133
196
|
|
|
134
197
|
console.print("\n[green]Project structure created successfully![/]")
|
|
135
198
|
|
|
@@ -146,6 +209,8 @@ def create_app():
|
|
|
146
209
|
"passlib[bcrypt]",
|
|
147
210
|
"argon2-cffi",
|
|
148
211
|
"python-multipart",
|
|
212
|
+
"alembic",
|
|
213
|
+
"pymysql"
|
|
149
214
|
])
|
|
150
215
|
|
|
151
216
|
# ==========================================
|
|
@@ -3,7 +3,7 @@ from . import helper_function as Hfn
|
|
|
3
3
|
|
|
4
4
|
console = Console()
|
|
5
5
|
|
|
6
|
-
def file_inside_content(file_name: str):
|
|
6
|
+
def file_inside_content(file_name: str, project_version):
|
|
7
7
|
console.print("[bold magenta]Creating File: [/] " + file_name )
|
|
8
8
|
if file_name == '.env':
|
|
9
9
|
Hfn.env(file_name)
|
|
@@ -26,11 +26,14 @@ def file_inside_content(file_name: str):
|
|
|
26
26
|
if file_name == 'config/app.py':
|
|
27
27
|
Hfn.config_app(file_name)
|
|
28
28
|
|
|
29
|
-
if file_name == 'app/database.py':
|
|
29
|
+
if file_name == 'app/database/database.py':
|
|
30
30
|
Hfn.database(file_name)
|
|
31
31
|
|
|
32
|
-
if file_name == 'app/models.py':
|
|
33
|
-
Hfn.
|
|
32
|
+
if file_name == 'app/models/__init__.py':
|
|
33
|
+
Hfn.models_init(file_name)
|
|
34
|
+
|
|
35
|
+
if file_name == 'app/models/users.py':
|
|
36
|
+
Hfn.models_user(file_name)
|
|
34
37
|
|
|
35
38
|
if file_name == 'router/api/users.py':
|
|
36
39
|
Hfn.api_user_route(file_name)
|
|
@@ -48,10 +51,10 @@ def file_inside_content(file_name: str):
|
|
|
48
51
|
Hfn.helper_function(file_name)
|
|
49
52
|
|
|
50
53
|
if file_name == 'templates/layouts/main.html':
|
|
51
|
-
Hfn.main_layout(file_name)
|
|
54
|
+
Hfn.main_layout(file_name, project_version)
|
|
52
55
|
|
|
53
56
|
if file_name == 'templates/layouts/auth.html':
|
|
54
|
-
Hfn.auth_layout(file_name)
|
|
57
|
+
Hfn.auth_layout(file_name, project_version)
|
|
55
58
|
|
|
56
59
|
if file_name == 'templates/pages/public/index.html':
|
|
57
60
|
Hfn.index(file_name)
|
|
@@ -67,11 +70,13 @@ def file_inside_content(file_name: str):
|
|
|
67
70
|
|
|
68
71
|
if file_name == 'templates/pages/auth/registater.html':
|
|
69
72
|
Hfn.registater(file_name)
|
|
70
|
-
|
|
71
73
|
|
|
72
|
-
if file_name == 'static/images/
|
|
74
|
+
if file_name == 'static/images/favicon.ico':
|
|
73
75
|
Hfn.static_logo(file_name)
|
|
74
76
|
|
|
77
|
+
if file_name == 'static/images/icon.png':
|
|
78
|
+
Hfn.static_icon(file_name)
|
|
79
|
+
|
|
75
80
|
# if file_name == 'main':
|
|
76
81
|
# pass
|
|
77
82
|
|
|
@@ -25,8 +25,20 @@ DATABASE_URL="sqlite:///./test.db"
|
|
|
25
25
|
ALLOW_ORIGIN=http://localhost:3000,http://localhost:8000
|
|
26
26
|
ALLOW_METHODS=GET,POST,PUT,DELETE,PATCH,OPTIONS
|
|
27
27
|
ALLOW_HEADERS=*
|
|
28
|
-
SESSION_SECRET="
|
|
28
|
+
SESSION_SECRET="aa0d-00e09_8032b8c"
|
|
29
29
|
|
|
30
|
+
# Database Selector ('mysql' OR 'sqlite')
|
|
31
|
+
CONNECTOR_TYPE="sqlite"
|
|
32
|
+
|
|
33
|
+
# SQLite Configuration (if CONNECTOR_TYPE='sqlite')
|
|
34
|
+
SQLITE_DB_PATH="./test.db"
|
|
35
|
+
|
|
36
|
+
# MySQL Configuration (if CONNECTOR_TYPE='mysql')
|
|
37
|
+
DB_HOST="localhost"
|
|
38
|
+
DB_PORT=4000
|
|
39
|
+
DB_USERNAME=""
|
|
40
|
+
DB_PASSWORD=""
|
|
41
|
+
DB_DATABASE="test"
|
|
30
42
|
""")
|
|
31
43
|
|
|
32
44
|
# ==========================================
|
|
@@ -43,6 +55,18 @@ ALLOW_METHODS=GET,POST,PUT,DELETE,PATCH,OPTIONS
|
|
|
43
55
|
ALLOW_HEADERS=*
|
|
44
56
|
SESSION_SECRET=""
|
|
45
57
|
|
|
58
|
+
# Database Selector ('mysql' OR 'sqlite')
|
|
59
|
+
CONNECTOR_TYPE="mysql"
|
|
60
|
+
|
|
61
|
+
# SQLite Configuration (if CONNECTOR_TYPE='sqlite')
|
|
62
|
+
SQLITE_DB_PATH="./test.db"
|
|
63
|
+
|
|
64
|
+
# MySQL Configuration (if CONNECTOR_TYPE='mysql')
|
|
65
|
+
DB_HOST="localhost"
|
|
66
|
+
DB_PORT=4000
|
|
67
|
+
DB_USERNAME=""
|
|
68
|
+
DB_PASSWORD=""
|
|
69
|
+
DB_DATABASE="test"
|
|
46
70
|
""")
|
|
47
71
|
|
|
48
72
|
|
|
@@ -69,12 +93,14 @@ def requirements(file_nm: str):
|
|
|
69
93
|
sqlalchemy
|
|
70
94
|
fastapi
|
|
71
95
|
jinja2
|
|
72
|
-
uvicorn[
|
|
96
|
+
uvicorn[standard]
|
|
73
97
|
python-dotenv
|
|
74
98
|
itsdangerous
|
|
75
99
|
python-multipart
|
|
76
100
|
argon2-cffi
|
|
77
101
|
passlib[bcrypt]
|
|
102
|
+
alembic
|
|
103
|
+
pymysql
|
|
78
104
|
""")
|
|
79
105
|
|
|
80
106
|
# ==========================================
|
|
@@ -94,7 +120,8 @@ def mainfile(file_nm: str):
|
|
|
94
120
|
with open(file_nm, "w" ,encoding="utf-8") as f:
|
|
95
121
|
f.write("""
|
|
96
122
|
import uvicorn
|
|
97
|
-
from config.app import app,
|
|
123
|
+
from config.app import app, Request, BASE_DIR
|
|
124
|
+
from fastapi.responses import FileResponse
|
|
98
125
|
|
|
99
126
|
# =================================
|
|
100
127
|
# ADD API route
|
|
@@ -111,7 +138,23 @@ from router.web import auth
|
|
|
111
138
|
from router.web import error
|
|
112
139
|
|
|
113
140
|
# ======================
|
|
114
|
-
#
|
|
141
|
+
# Global Favicon Icon
|
|
142
|
+
# ======================
|
|
143
|
+
FAVICON_PATH = BASE_DIR / "static" / "images" / "favicon.ico"
|
|
144
|
+
@app.get("/favicon.ico", include_in_schema=False)
|
|
145
|
+
async def favicon():
|
|
146
|
+
return FileResponse(FAVICON_PATH, media_type="image/x-icon")
|
|
147
|
+
|
|
148
|
+
# ======================
|
|
149
|
+
# Global Png Icon
|
|
150
|
+
# ======================
|
|
151
|
+
ICON_PATH = BASE_DIR / "static" / "images" / "icon.png"
|
|
152
|
+
@app.get("/icon.png", include_in_schema=False)
|
|
153
|
+
async def icon():
|
|
154
|
+
return FileResponse(ICON_PATH, media_type="image/png")
|
|
155
|
+
|
|
156
|
+
# ======================
|
|
157
|
+
# session or authention debug check
|
|
115
158
|
# ======================
|
|
116
159
|
@app.get("/debug")
|
|
117
160
|
async def debug(request: Request):
|
|
@@ -121,7 +164,7 @@ async def debug(request: Request):
|
|
|
121
164
|
}
|
|
122
165
|
|
|
123
166
|
if __name__ == "__main__":
|
|
124
|
-
uvicorn.run(app, host="
|
|
167
|
+
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
|
|
125
168
|
""")
|
|
126
169
|
|
|
127
170
|
# ==========================================
|
|
@@ -131,8 +174,8 @@ def config_app(file_nm: str):
|
|
|
131
174
|
with open(file_nm, "w" ,encoding="utf-8") as f:
|
|
132
175
|
f.write("""
|
|
133
176
|
import os
|
|
134
|
-
from fastapi import FastAPI,Request
|
|
135
|
-
from fastapi.responses import RedirectResponse
|
|
177
|
+
from fastapi import FastAPI, Request
|
|
178
|
+
from fastapi.responses import RedirectResponse
|
|
136
179
|
from fastapi.templating import Jinja2Templates
|
|
137
180
|
from fastapi.staticfiles import StaticFiles
|
|
138
181
|
from dotenv import load_dotenv
|
|
@@ -213,14 +256,30 @@ from dotenv import load_dotenv
|
|
|
213
256
|
|
|
214
257
|
load_dotenv()
|
|
215
258
|
|
|
216
|
-
|
|
259
|
+
CONNECTOR_TYPE = os.getenv("CONNECTOR_TYPE", "sqlite").lower()
|
|
217
260
|
|
|
261
|
+
if CONNECTOR_TYPE == "mysql":
|
|
262
|
+
db_user = os.getenv("DB_USERNAME")
|
|
263
|
+
db_pass = os.getenv("DB_PASSWORD")
|
|
264
|
+
db_host = os.getenv("DB_HOST")
|
|
265
|
+
db_port = os.getenv("DB_PORT", "4000")
|
|
266
|
+
db_name = os.getenv("DB_DATABASE")
|
|
267
|
+
|
|
268
|
+
DATABASE_URL = f"mysql+pymysql://{db_user}:{db_pass}@{db_host}:{db_port}/{db_name}?ssl_verify_cert=true&ssl_verify_identity=true"
|
|
269
|
+
connect_args = {} # MySQL extra parameters & args, like SSL
|
|
270
|
+
|
|
271
|
+
else:
|
|
272
|
+
sqlite_path = os.getenv("SQLITE_DB_PATH", "./test.db")
|
|
273
|
+
DATABASE_URL = f"sqlite:///{sqlite_path}"
|
|
274
|
+
connect_args = {"check_same_thread": False} # only SQLite
|
|
275
|
+
|
|
276
|
+
# SQLAlchemy Engine
|
|
218
277
|
engine = create_engine(
|
|
219
278
|
DATABASE_URL,
|
|
220
279
|
pool_size=10,
|
|
221
280
|
max_overflow=20,
|
|
222
281
|
pool_pre_ping=True,
|
|
223
|
-
connect_args=
|
|
282
|
+
connect_args=connect_args
|
|
224
283
|
)
|
|
225
284
|
|
|
226
285
|
SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False)
|
|
@@ -237,37 +296,54 @@ def get_db():
|
|
|
237
296
|
""")
|
|
238
297
|
|
|
239
298
|
# ==========================================
|
|
240
|
-
# app/
|
|
299
|
+
# app/__init__.py file create
|
|
300
|
+
# ==========================================
|
|
301
|
+
def models_init(file_nm: str):
|
|
302
|
+
with open(file_nm, "w" ,encoding="utf-8") as f:
|
|
303
|
+
f.write("""
|
|
304
|
+
from app.models.users import User
|
|
305
|
+
|
|
306
|
+
# Add More Models
|
|
307
|
+
""")
|
|
308
|
+
|
|
309
|
+
# ==========================================
|
|
310
|
+
# app/models/users.py file create
|
|
241
311
|
# ==========================================
|
|
242
|
-
def
|
|
312
|
+
def models_user(file_nm: str):
|
|
243
313
|
with open(file_nm, "w" ,encoding="utf-8") as f:
|
|
244
314
|
f.write("""
|
|
245
|
-
from sqlalchemy import Column, Integer, String, Boolean, DateTime
|
|
315
|
+
from sqlalchemy import Column, Integer, String, Boolean, DateTime, Enum
|
|
246
316
|
from datetime import datetime
|
|
247
|
-
from .database import Base
|
|
317
|
+
from app.database.database import Base
|
|
248
318
|
|
|
249
319
|
class User(Base):
|
|
250
320
|
__tablename__ = "users"
|
|
251
321
|
|
|
252
322
|
id = Column(Integer, primary_key=True, index=True)
|
|
253
323
|
username = Column(String(50), unique=True, nullable=False)
|
|
254
|
-
name = Column(String, index=True)
|
|
255
|
-
email = Column(String, unique=True, index=True)
|
|
256
|
-
|
|
324
|
+
name = Column(String(200), index=True)
|
|
325
|
+
email = Column(String(255), unique=True, index=True)
|
|
326
|
+
role = Column(Enum('users', 'admin', 'moderator', name='user_role'),nullable=False,server_default='users')
|
|
327
|
+
password = Column(String(255), nullable=False)
|
|
257
328
|
is_active = Column(Boolean, default=True)
|
|
258
329
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
259
330
|
|
|
260
|
-
# Add More Table ....
|
|
261
|
-
|
|
262
331
|
""")
|
|
263
332
|
|
|
264
333
|
# ==========================================
|
|
265
|
-
# static/images/
|
|
334
|
+
# static/images/favicon.ico file create
|
|
266
335
|
# ==========================================
|
|
267
336
|
def static_logo(file_nm: str):
|
|
268
|
-
logo_path = files("shafikul_cli.asset").joinpath("
|
|
337
|
+
logo_path = files("shafikul_cli.asset").joinpath("favicon.ico")
|
|
269
338
|
shutil.copy(logo_path, file_nm)
|
|
270
339
|
|
|
340
|
+
# ==========================================
|
|
341
|
+
# static/images/icon.png file create
|
|
342
|
+
# ==========================================
|
|
343
|
+
def static_icon(file_nm: str):
|
|
344
|
+
icon_path = files("shafikul_cli.asset").joinpath("icon.png")
|
|
345
|
+
shutil.copy(icon_path, file_nm)
|
|
346
|
+
|
|
271
347
|
# ==========================================
|
|
272
348
|
# router/api/users.py file create
|
|
273
349
|
# ==========================================
|
|
@@ -293,10 +369,11 @@ def index():
|
|
|
293
369
|
def web_auth_route(file_nm: str):
|
|
294
370
|
with open(file_nm, "w" ,encoding="utf-8") as f:
|
|
295
371
|
f.write("""
|
|
296
|
-
from config.app import app, templates, os, Request
|
|
372
|
+
from config.app import app, templates, os, Request
|
|
373
|
+
from fastapi.responses import HTMLResponse, RedirectResponse
|
|
297
374
|
from fastapi import Form,Depends
|
|
298
375
|
from sqlalchemy.orm import Session
|
|
299
|
-
from app.database import get_db
|
|
376
|
+
from app.database.database import get_db
|
|
300
377
|
from app.models import User
|
|
301
378
|
from utility.helper_function import hash_password, verify_password
|
|
302
379
|
import random, uuid
|
|
@@ -432,7 +509,8 @@ def dashboard(request: Request,db: Session = Depends(get_db)):
|
|
|
432
509
|
def web_error_route(file_nm: str):
|
|
433
510
|
with open(file_nm, "w" ,encoding="utf-8") as f:
|
|
434
511
|
f.write("""
|
|
435
|
-
from config.app import app, templates, Request
|
|
512
|
+
from config.app import app, templates, Request
|
|
513
|
+
from fastapi.responses import JSONResponse
|
|
436
514
|
from starlette.exceptions import HTTPException as NotFoundException
|
|
437
515
|
|
|
438
516
|
@app.exception_handler(NotFoundException)
|
|
@@ -457,7 +535,8 @@ async def http_exception_handler(request: Request, exc: NotFoundException):
|
|
|
457
535
|
def web_public_route(file_nm: str):
|
|
458
536
|
with open(file_nm, "w" ,encoding="utf-8") as f:
|
|
459
537
|
f.write("""
|
|
460
|
-
from config.app import app, templates, os,
|
|
538
|
+
from config.app import app, templates, os, Request
|
|
539
|
+
from fastapi.responses import HTMLResponse
|
|
461
540
|
|
|
462
541
|
@app.get('/', response_class=HTMLResponse)
|
|
463
542
|
def index(request: Request):
|
|
@@ -529,12 +608,8 @@ def index(file_nm: str):
|
|
|
529
608
|
text-center transform transition-all hover:shadow-2xl">
|
|
530
609
|
|
|
531
610
|
<!-- Icon And Logo -->
|
|
532
|
-
<div class="inline-flex p-3 rounded-2xl
|
|
533
|
-
<
|
|
534
|
-
viewBox="0 0 24 24">
|
|
535
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
536
|
-
d="M13 10V3L4 14h7v7l9-11h-7z"></path>
|
|
537
|
-
</svg>
|
|
611
|
+
<div class="inline-flex p-3 rounded-2xl mb-6">
|
|
612
|
+
<img src="/icon.png" alt="Icon" class="w-[5rem] h-[5rem] rounded-full animate-float">
|
|
538
613
|
</div>
|
|
539
614
|
|
|
540
615
|
<h1
|
|
@@ -550,12 +625,12 @@ def index(file_nm: str):
|
|
|
550
625
|
<div class="space-y-4">
|
|
551
626
|
<a href="/signup"
|
|
552
627
|
class="block w-full py-3 px-6 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold rounded-xl shadow-lg shadow-indigo-500/30 transition-all hover:-translate-y-1">
|
|
553
|
-
|
|
628
|
+
Sign Up
|
|
554
629
|
</a>
|
|
555
630
|
|
|
556
631
|
<a href="/login"
|
|
557
632
|
class="block w-full py-3 px-6 bg-white/50 dark:bg-white/10 hover:bg-white/80 dark:hover:bg-white/20 text-slate-800 dark:text-white font-semibold rounded-xl border border-white/20 transition-all">
|
|
558
|
-
|
|
633
|
+
Login
|
|
559
634
|
</a>
|
|
560
635
|
</div>
|
|
561
636
|
|
|
@@ -1002,7 +1077,7 @@ Not Found
|
|
|
1002
1077
|
# ==========================================
|
|
1003
1078
|
# templates/layouts/main.html file create
|
|
1004
1079
|
# ==========================================
|
|
1005
|
-
def main_layout(file_nm: str):
|
|
1080
|
+
def main_layout(file_nm: str, project_version):
|
|
1006
1081
|
with open(file_nm, "w" ,encoding="utf-8") as f:
|
|
1007
1082
|
f.write("""
|
|
1008
1083
|
<!DOCTYPE html>
|
|
@@ -1011,11 +1086,37 @@ def main_layout(file_nm: str):
|
|
|
1011
1086
|
<head>
|
|
1012
1087
|
<meta charset="UTF-8">
|
|
1013
1088
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1014
|
-
<title>Shafikul FastAPI App {% block title %}
|
|
1015
|
-
<
|
|
1089
|
+
<title>Shafikul FastAPI App {% block title %}v""" + project_version + """{% endblock %}</title>
|
|
1090
|
+
<meta name="description" content="Generate production-ready boilerplate with user authentication, database models, and a complete folder structure in seconds.">
|
|
1091
|
+
<meta name="keywords" content="FastAPI, Python, Web Development">
|
|
1092
|
+
<meta name="author" content="Shafikul Islam">
|
|
1093
|
+
<meta name="robots" content="index, follow">
|
|
1094
|
+
<meta name="language" content="English">
|
|
1095
|
+
<meta name="theme-color" content="#0f172a">
|
|
1096
|
+
<meta name="color-scheme" content="dark light">
|
|
1097
|
+
<meta name="application-name" content="Shafikul FastAPI App">
|
|
1098
|
+
<meta name="referrer" content="strict-origin-when-cross-origin">
|
|
1099
|
+
<meta name="mobile-web-app-capable" content="yes">
|
|
1100
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
1101
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
1102
|
+
<meta name="apple-mobile-web-app-title" content="Shafikul FastAPI App">
|
|
1103
|
+
<meta http-equiv="X-Content-Type-Options" content="nosniff">
|
|
1104
|
+
<link rel="icon" href="{{ request.base_url }}favicon.ico">
|
|
1105
|
+
<link rel="apple-touch-icon" href="{{ request.base_url }}icon.png">
|
|
1106
|
+
<link rel="canonical" href="{{ request.base_url }}">
|
|
1107
|
+
<meta property="og:type" content="website">
|
|
1108
|
+
<meta property="og:title" content="Shafikul FastAPI App — A Modern Web Application">
|
|
1109
|
+
<meta property="og:description" content="A modern web application built with FastAPI and Python.">
|
|
1110
|
+
<meta property="og:image" content="{{ request.base_url }}icon.png">
|
|
1111
|
+
<meta property="og:url" content="{{ request.base_url }}">
|
|
1112
|
+
<meta property="og:site_name" content="Shafikul FastAPI App">
|
|
1113
|
+
<meta property="og:locale" content="en_US">
|
|
1114
|
+
<meta name="twitter:card" content="summary_large_image">
|
|
1115
|
+
<meta name="twitter:title" content="Shafikul FastAPI App — A Modern Web Application">
|
|
1116
|
+
<meta name="twitter:description" content="A modern web application built with FastAPI and Python.">
|
|
1117
|
+
<meta name="twitter:image" content="{{ request.base_url }}icon.png">
|
|
1016
1118
|
|
|
1017
1119
|
{% block add_head %}{% endblock %}
|
|
1018
|
-
|
|
1019
1120
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
1020
1121
|
<script>
|
|
1021
1122
|
tailwind.config = {
|
|
@@ -1055,7 +1156,7 @@ def main_layout(file_nm: str):
|
|
|
1055
1156
|
# ==========================================
|
|
1056
1157
|
# templates/layouts/auth.html file create
|
|
1057
1158
|
# ==========================================
|
|
1058
|
-
def auth_layout(file_nm: str):
|
|
1159
|
+
def auth_layout(file_nm: str, project_version):
|
|
1059
1160
|
with open(file_nm, "w" ,encoding="utf-8") as f:
|
|
1060
1161
|
f.write("""
|
|
1061
1162
|
<!DOCTYPE html>
|
|
@@ -1064,9 +1165,21 @@ def auth_layout(file_nm: str):
|
|
|
1064
1165
|
<head>
|
|
1065
1166
|
<meta charset="UTF-8">
|
|
1066
1167
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1067
|
-
<title>Shafikul - {% block title %}
|
|
1068
|
-
<
|
|
1168
|
+
<title>Shafikul Auth Page - {% block title %}v""" + project_version + """{% endblock %}</title>
|
|
1169
|
+
<meta name="description" content="Generate production-ready boilerplate with user authentication, database models, and a complete folder structure in seconds.">
|
|
1170
|
+
<meta name="author" content="Shafikul Islam">
|
|
1171
|
+
<meta name="robots" content="nofollow">
|
|
1172
|
+
<meta name="language" content="English">
|
|
1173
|
+
<meta name="application-name" content="Shafikul FastAPI App Auth Page">
|
|
1174
|
+
<meta name="referrer" content="strict-origin-when-cross-origin">
|
|
1175
|
+
<meta name="mobile-web-app-capable" content="yes">
|
|
1176
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
1177
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
1178
|
+
<meta name="apple-mobile-web-app-title" content="Shafikul FastAPI App">
|
|
1179
|
+
<meta http-equiv="X-Content-Type-Options" content="nosniff">
|
|
1180
|
+
<link rel="shortcut icon" href="{{ request.base_url }}favicon.ico">
|
|
1069
1181
|
|
|
1182
|
+
|
|
1070
1183
|
{% block add_head %}{% endblock %}
|
|
1071
1184
|
|
|
1072
1185
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
@@ -1826,15 +1939,22 @@ def edit_migration_env():
|
|
|
1826
1939
|
import_added = False
|
|
1827
1940
|
load_dotenv_added = False
|
|
1828
1941
|
db_url_added = False
|
|
1942
|
+
skipping_connectable = False
|
|
1829
1943
|
|
|
1830
1944
|
for line in lines:
|
|
1831
1945
|
stripped = line.strip()
|
|
1832
1946
|
|
|
1947
|
+
# engine_from_config(...) block-er baki line-gulo skip kora
|
|
1948
|
+
if skipping_connectable:
|
|
1949
|
+
if stripped == ")":
|
|
1950
|
+
skipping_connectable = False
|
|
1951
|
+
continue
|
|
1952
|
+
|
|
1833
1953
|
# ১. ইম্পোর্ট অ্যাড করা (একবারই হবে)
|
|
1834
1954
|
if not import_added and ("from alembic import context" in stripped or stripped.startswith("import")):
|
|
1835
1955
|
new_lines.append("import os\n")
|
|
1836
|
-
new_lines.append("from app.database import Base\n")
|
|
1837
|
-
new_lines.append("
|
|
1956
|
+
new_lines.append("from app.database.database import Base, engine, DATABASE_URL\n")
|
|
1957
|
+
new_lines.append("import app.models\n")
|
|
1838
1958
|
new_lines.append("from dotenv import load_dotenv\n\n")
|
|
1839
1959
|
import_added = True
|
|
1840
1960
|
|
|
@@ -1848,12 +1968,24 @@ def edit_migration_env():
|
|
|
1848
1968
|
new_lines.append("target_metadata = Base.metadata\n")
|
|
1849
1969
|
continue # এটা দিলে নিচের new_lines.append(line) রান হবে না, ফলে পুরনো লাইনটি বাদ যাবে
|
|
1850
1970
|
|
|
1971
|
+
# 4. connectable block: original line likhar AGE-i replace kora
|
|
1972
|
+
if stripped == "connectable = engine_from_config(":
|
|
1973
|
+
indent = line[: len(line) - len(line.lstrip())]
|
|
1974
|
+
new_lines.append(f"{indent}# connectable = engine_from_config(\n")
|
|
1975
|
+
new_lines.append(f"{indent}# config.get_section(config.config_ini_section, {{}}),\n")
|
|
1976
|
+
new_lines.append(f'{indent}# prefix="sqlalchemy.",\n')
|
|
1977
|
+
new_lines.append(f"{indent}# poolclass=pool.NullPool,\n")
|
|
1978
|
+
new_lines.append(f"{indent}# )\n")
|
|
1979
|
+
new_lines.append(f"{indent}connectable = engine\n")
|
|
1980
|
+
skipping_connectable = True
|
|
1981
|
+
continue
|
|
1982
|
+
|
|
1851
1983
|
# মূল লাইনটি অ্যাড করা (যদি উপরের 'continue' হিট না করে)
|
|
1852
1984
|
new_lines.append(line)
|
|
1853
1985
|
|
|
1854
1986
|
# ৪. config = context.config লাইনের পরে DATABASE_URL সেট করা
|
|
1855
1987
|
if "config = context.config" in stripped and not db_url_added:
|
|
1856
|
-
new_lines.append('config.set_main_option("sqlalchemy.url",
|
|
1988
|
+
new_lines.append('config.set_main_option("sqlalchemy.url", DATABASE_URL)\n\n')
|
|
1857
1989
|
db_url_added = True
|
|
1858
1990
|
|
|
1859
1991
|
with open(file_path, "w", encoding="utf-8") as f:
|
|
@@ -33,7 +33,7 @@ def alembic_init():
|
|
|
33
33
|
|
|
34
34
|
try:
|
|
35
35
|
subprocess.run(
|
|
36
|
-
["alembic", "init", "migrations"],
|
|
36
|
+
[sys.executable, "-m", "alembic", "init", "migrations"],
|
|
37
37
|
check=True
|
|
38
38
|
)
|
|
39
39
|
console.print("[green]Alembic initialized successfully[/]")
|
|
@@ -55,13 +55,13 @@ def user_table_autogenerate():
|
|
|
55
55
|
|
|
56
56
|
try:
|
|
57
57
|
subprocess.run(
|
|
58
|
-
["alembic", "revision", "--autogenerate", "-m", "create users table"],
|
|
58
|
+
[ sys.executable, "-m", "alembic", "revision", "--autogenerate", "-m", "create users table"],
|
|
59
59
|
check=True
|
|
60
60
|
)
|
|
61
61
|
|
|
62
62
|
console.print("[green]Migrate Table In DB[/]")
|
|
63
63
|
subprocess.run(
|
|
64
|
-
["alembic", "upgrade", "head"],
|
|
64
|
+
[sys.executable, "-m", "alembic", "upgrade", "head"],
|
|
65
65
|
check=True
|
|
66
66
|
)
|
|
67
67
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: shafikul_cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.6
|
|
4
4
|
Summary: CLI tool for FastAPI scaffolding with router, models, database, and templates.
|
|
5
5
|
Author-email: Md Shafikul Islam <buildwithshafikul@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/build-with-shafikul/shafikul_cli
|
|
@@ -21,7 +21,7 @@ Dynamic: license-file
|
|
|
21
21
|
|
|
22
22
|
<div align="center">
|
|
23
23
|
|
|
24
|
-

|
|
25
25
|

|
|
26
26
|

|
|
27
27
|
[](https://github.com/build-with-shafikul/shafikul_cli)
|
|
@@ -175,7 +175,7 @@ your-project/
|
|
|
175
175
|
│
|
|
176
176
|
├── 📂 static/ # Served static files
|
|
177
177
|
│ └── images/
|
|
178
|
-
│ └──
|
|
178
|
+
│ └── icon.png
|
|
179
179
|
│
|
|
180
180
|
├── 📂 utility/ # Helper functions
|
|
181
181
|
│ └── helper_function.py
|
|
@@ -10,6 +10,7 @@ shafikul_cli.egg-info/entry_points.txt
|
|
|
10
10
|
shafikul_cli.egg-info/requires.txt
|
|
11
11
|
shafikul_cli.egg-info/top_level.txt
|
|
12
12
|
shafikul_cli/asset/__init__.py
|
|
13
|
+
shafikul_cli/asset/icon.png
|
|
13
14
|
shafikul_cli/asset/logo.png
|
|
14
15
|
shafikul_cli/utility/__init__.py
|
|
15
16
|
shafikul_cli/utility/create_file_content.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|