shafikul-cli 2.1.6__tar.gz → 2.2.7__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.
Files changed (26) hide show
  1. {shafikul_cli-2.1.6/shafikul_cli.egg-info → shafikul_cli-2.2.7}/PKG-INFO +4 -6
  2. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/README.md +2 -2
  3. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/pyproject.toml +2 -8
  4. shafikul_cli-2.2.7/shafikul_cli/asset/icon.png +0 -0
  5. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/shafikul_cli/main.py +69 -5
  6. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/shafikul_cli/utility/create_file_content.py +13 -8
  7. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/shafikul_cli/utility/helper_function.py +172 -41
  8. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/shafikul_cli/utility/install_dependicy.py +3 -9
  9. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7/shafikul_cli.egg-info}/PKG-INFO +4 -6
  10. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/shafikul_cli.egg-info/SOURCES.txt +1 -0
  11. shafikul_cli-2.2.7/shafikul_cli.egg-info/requires.txt +3 -0
  12. shafikul_cli-2.1.6/shafikul_cli.egg-info/requires.txt +0 -5
  13. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/LICENSE +0 -0
  14. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/setup.cfg +0 -0
  15. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/shafikul_cli/__init__.py +0 -0
  16. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/shafikul_cli/asset/__init__.py +0 -0
  17. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/shafikul_cli/asset/logo.png +0 -0
  18. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/shafikul_cli/utility/__init__.py +0 -0
  19. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/shafikul_cli/utility/create_file_structure.py +0 -0
  20. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/shafikul_cli/utility/create_html_file.py +0 -0
  21. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/shafikul_cli/utility/ensure_db.py +0 -0
  22. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/shafikul_cli/utility/ensure_main.py +0 -0
  23. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/shafikul_cli/utility/env_generate.py +0 -0
  24. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/shafikul_cli.egg-info/dependency_links.txt +0 -0
  25. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/shafikul_cli.egg-info/entry_points.txt +0 -0
  26. {shafikul_cli-2.1.6 → shafikul_cli-2.2.7}/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.1.6
3
+ Version: 2.2.7
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
@@ -10,9 +10,7 @@ Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
11
  Requires-Dist: typer
12
12
  Requires-Dist: rich
13
- Requires-Dist: keyboard
14
- Requires-Dist: pyautogui
15
- Requires-Dist: pyperclip
13
+ Requires-Dist: questionary
16
14
  Dynamic: license-file
17
15
 
18
16
  # 🚀 Shafikul CLI
@@ -21,7 +19,7 @@ Dynamic: license-file
21
19
 
22
20
  <div align="center">
23
21
 
24
- ![Version](https://img.shields.io/badge/version-2.1.6-blue?style=flat-square)
22
+ ![Version](https://img.shields.io/badge/version-2.2.7-blue?style=flat-square)
25
23
  ![Python](https://img.shields.io/badge/python-3.9+-green?style=flat-square)
26
24
  ![License](https://img.shields.io/badge/license-MIT-purple?style=flat-square)
27
25
  [![GitHub](https://img.shields.io/badge/GitHub-build--with--shafikul-black?style=flat-square&logo=github)](https://github.com/build-with-shafikul/shafikul_cli)
@@ -175,7 +173,7 @@ your-project/
175
173
  │
176
174
  ├── 📂 static/ # Served static files
177
175
  │ └── images/
178
- │ └── logo.png
176
+ │ └── icon.png
179
177
  │
180
178
  ├── 📂 utility/ # Helper functions
181
179
  │ └── helper_function.py
@@ -4,7 +4,7 @@
4
4
 
5
5
  <div align="center">
6
6
 
7
- ![Version](https://img.shields.io/badge/version-2.1.6-blue?style=flat-square)
7
+ ![Version](https://img.shields.io/badge/version-2.2.7-blue?style=flat-square)
8
8
  ![Python](https://img.shields.io/badge/python-3.9+-green?style=flat-square)
9
9
  ![License](https://img.shields.io/badge/license-MIT-purple?style=flat-square)
10
10
  [![GitHub](https://img.shields.io/badge/GitHub-build--with--shafikul-black?style=flat-square&logo=github)](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
- │ └── logo.png
161
+ │ └── icon.png
162
162
  │
163
163
  ├── 📂 utility/ # Helper functions
164
164
  │ └── helper_function.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "shafikul_cli"
7
- version = "2.1.6"
7
+ version = "2.2.7"
8
8
  description = "CLI tool for FastAPI scaffolding with router, models, database, and templates."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -13,13 +13,7 @@ authors = [
13
13
  { name = "Md Shafikul Islam", email = "buildwithshafikul@gmail.com" }
14
14
  ]
15
15
 
16
- dependencies = [
17
- "typer",
18
- "rich",
19
- "keyboard",
20
- "pyautogui",
21
- "pyperclip"
22
- ]
16
+ dependencies = ["typer", "rich", "questionary"]
23
17
 
24
18
  [project.urls]
25
19
  Homepage = "https://github.com/build-with-shafikul/shafikul_cli"
@@ -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
- "asset/images/icon.png",
111
- "static/images/logo.png",
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
 
@@ -147,6 +210,7 @@ def create_app():
147
210
  "argon2-cffi",
148
211
  "python-multipart",
149
212
  "alembic",
213
+ "pymysql"
150
214
  ])
151
215
 
152
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.models(file_name)
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/logo.png':
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="my-custom_text"
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
 
@@ -76,6 +100,7 @@ python-multipart
76
100
  argon2-cffi
77
101
  passlib[bcrypt]
78
102
  alembic
103
+ pymysql
79
104
  """)
80
105
 
81
106
  # ==========================================
@@ -95,7 +120,8 @@ def mainfile(file_nm: str):
95
120
  with open(file_nm, "w" ,encoding="utf-8") as f:
96
121
  f.write("""
97
122
  import uvicorn
98
- from config.app import app, RedirectResponse, Request
123
+ from config.app import app, Request, BASE_DIR
124
+ from fastapi.responses import FileResponse
99
125
 
100
126
  # =================================
101
127
  # ADD API route
@@ -112,7 +138,23 @@ from router.web import auth
112
138
  from router.web import error
113
139
 
114
140
  # ======================
115
- # session or authention debug korar jonno
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
116
158
  # ======================
117
159
  @app.get("/debug")
118
160
  async def debug(request: Request):
@@ -122,7 +164,7 @@ async def debug(request: Request):
122
164
  }
123
165
 
124
166
  if __name__ == "__main__":
125
- uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)
167
+ uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
126
168
  """)
127
169
 
128
170
  # ==========================================
@@ -132,8 +174,8 @@ def config_app(file_nm: str):
132
174
  with open(file_nm, "w" ,encoding="utf-8") as f:
133
175
  f.write("""
134
176
  import os
135
- from fastapi import FastAPI,Request,HTTPException
136
- from fastapi.responses import RedirectResponse, JSONResponse, HTMLResponse
177
+ from fastapi import FastAPI, Request
178
+ from fastapi.responses import RedirectResponse
137
179
  from fastapi.templating import Jinja2Templates
138
180
  from fastapi.staticfiles import StaticFiles
139
181
  from dotenv import load_dotenv
@@ -214,14 +256,30 @@ from dotenv import load_dotenv
214
256
 
215
257
  load_dotenv()
216
258
 
217
- DATABASE_URL = os.getenv("DATABASE_URL")
259
+ CONNECTOR_TYPE = os.getenv("CONNECTOR_TYPE", "sqlite").lower()
218
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
219
277
  engine = create_engine(
220
278
  DATABASE_URL,
221
279
  pool_size=10,
222
280
  max_overflow=20,
223
281
  pool_pre_ping=True,
224
- connect_args={"check_same_thread": False} # sqlite connect er jonno eita lagbe
282
+ connect_args=connect_args
225
283
  )
226
284
 
227
285
  SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False)
@@ -238,37 +296,54 @@ def get_db():
238
296
  """)
239
297
 
240
298
  # ==========================================
241
- # app/models.py file create
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
242
311
  # ==========================================
243
- def models(file_nm: str):
312
+ def models_user(file_nm: str):
244
313
  with open(file_nm, "w" ,encoding="utf-8") as f:
245
314
  f.write("""
246
- from sqlalchemy import Column, Integer, String, Boolean, DateTime
315
+ from sqlalchemy import Column, Integer, String, Boolean, DateTime, Enum
247
316
  from datetime import datetime
248
- from .database import Base
317
+ from app.database.database import Base
249
318
 
250
319
  class User(Base):
251
320
  __tablename__ = "users"
252
321
 
253
322
  id = Column(Integer, primary_key=True, index=True)
254
323
  username = Column(String(50), unique=True, nullable=False)
255
- name = Column(String, index=True)
256
- email = Column(String, unique=True, index=True)
257
- password = Column(String)
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)
258
328
  is_active = Column(Boolean, default=True)
259
329
  created_at = Column(DateTime, default=datetime.utcnow)
260
330
 
261
- # Add More Table ....
262
-
263
331
  """)
264
332
 
265
333
  # ==========================================
266
- # static/images/logo.png file create
334
+ # static/images/favicon.ico file create
267
335
  # ==========================================
268
336
  def static_logo(file_nm: str):
269
- logo_path = files("shafikul_cli.asset").joinpath("logo.png")
337
+ logo_path = files("shafikul_cli.asset").joinpath("favicon.ico")
270
338
  shutil.copy(logo_path, file_nm)
271
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
+
272
347
  # ==========================================
273
348
  # router/api/users.py file create
274
349
  # ==========================================
@@ -294,10 +369,11 @@ def index():
294
369
  def web_auth_route(file_nm: str):
295
370
  with open(file_nm, "w" ,encoding="utf-8") as f:
296
371
  f.write("""
297
- from config.app import app, templates, os, Request,HTMLResponse,RedirectResponse
372
+ from config.app import app, templates, os, Request
373
+ from fastapi.responses import HTMLResponse, RedirectResponse
298
374
  from fastapi import Form,Depends
299
375
  from sqlalchemy.orm import Session
300
- from app.database import get_db
376
+ from app.database.database import get_db
301
377
  from app.models import User
302
378
  from utility.helper_function import hash_password, verify_password
303
379
  import random, uuid
@@ -433,7 +509,8 @@ def dashboard(request: Request,db: Session = Depends(get_db)):
433
509
  def web_error_route(file_nm: str):
434
510
  with open(file_nm, "w" ,encoding="utf-8") as f:
435
511
  f.write("""
436
- from config.app import app, templates, Request, JSONResponse
512
+ from config.app import app, templates, Request
513
+ from fastapi.responses import JSONResponse
437
514
  from starlette.exceptions import HTTPException as NotFoundException
438
515
 
439
516
  @app.exception_handler(NotFoundException)
@@ -458,7 +535,8 @@ async def http_exception_handler(request: Request, exc: NotFoundException):
458
535
  def web_public_route(file_nm: str):
459
536
  with open(file_nm, "w" ,encoding="utf-8") as f:
460
537
  f.write("""
461
- from config.app import app, templates, os, HTMLResponse, Request
538
+ from config.app import app, templates, os, Request
539
+ from fastapi.responses import HTMLResponse
462
540
 
463
541
  @app.get('/', response_class=HTMLResponse)
464
542
  def index(request: Request):
@@ -530,12 +608,8 @@ def index(file_nm: str):
530
608
  text-center transform transition-all hover:shadow-2xl">
531
609
 
532
610
  <!-- Icon And Logo -->
533
- <div class="inline-flex p-3 rounded-2xl bg-indigo-500/20 mb-6">
534
- <svg class="w-10 h-10 text-indigo-600 dark:text-indigo-400" fill="none" stroke="currentColor"
535
- viewBox="0 0 24 24">
536
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
537
- d="M13 10V3L4 14h7v7l9-11h-7z"></path>
538
- </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">
539
613
  </div>
540
614
 
541
615
  <h1
@@ -551,12 +625,12 @@ def index(file_nm: str):
551
625
  <div class="space-y-4">
552
626
  <a href="/signup"
553
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">
554
- Get Started
628
+ Sign Up
555
629
  </a>
556
630
 
557
631
  <a href="/login"
558
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">
559
- Documentation
633
+ Login
560
634
  </a>
561
635
  </div>
562
636
 
@@ -1003,7 +1077,7 @@ Not Found
1003
1077
  # ==========================================
1004
1078
  # templates/layouts/main.html file create
1005
1079
  # ==========================================
1006
- def main_layout(file_nm: str):
1080
+ def main_layout(file_nm: str, project_version):
1007
1081
  with open(file_nm, "w" ,encoding="utf-8") as f:
1008
1082
  f.write("""
1009
1083
  <!DOCTYPE html>
@@ -1012,11 +1086,37 @@ def main_layout(file_nm: str):
1012
1086
  <head>
1013
1087
  <meta charset="UTF-8">
1014
1088
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1015
- <title>Shafikul FastAPI App {% block title %}v1.0.0{% endblock %}</title>
1016
- <link rel="shortcut icon" href="{{ url_for('static', path='images/logo.png') }}" type="image/x-icon">
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">
1017
1118
 
1018
1119
  {% block add_head %}{% endblock %}
1019
-
1020
1120
  <script src="https://cdn.tailwindcss.com"></script>
1021
1121
  <script>
1022
1122
  tailwind.config = {
@@ -1056,7 +1156,7 @@ def main_layout(file_nm: str):
1056
1156
  # ==========================================
1057
1157
  # templates/layouts/auth.html file create
1058
1158
  # ==========================================
1059
- def auth_layout(file_nm: str):
1159
+ def auth_layout(file_nm: str, project_version):
1060
1160
  with open(file_nm, "w" ,encoding="utf-8") as f:
1061
1161
  f.write("""
1062
1162
  <!DOCTYPE html>
@@ -1065,9 +1165,21 @@ def auth_layout(file_nm: str):
1065
1165
  <head>
1066
1166
  <meta charset="UTF-8">
1067
1167
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1068
- <title>Shafikul - {% block title %}Auth Page{% endblock %}</title>
1069
- <link rel="shortcut icon" href="{{ url_for('static', path='images/logo.png') }}" type="image/x-icon">
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">
1070
1181
 
1182
+
1071
1183
  {% block add_head %}{% endblock %}
1072
1184
 
1073
1185
  <script src="https://cdn.tailwindcss.com"></script>
@@ -1827,15 +1939,22 @@ def edit_migration_env():
1827
1939
  import_added = False
1828
1940
  load_dotenv_added = False
1829
1941
  db_url_added = False
1942
+ skipping_connectable = False
1830
1943
 
1831
1944
  for line in lines:
1832
1945
  stripped = line.strip()
1833
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
+
1834
1953
  # ১. ইম্পোর্ট অ্যাড করা (একবারই হবে)
1835
1954
  if not import_added and ("from alembic import context" in stripped or stripped.startswith("import")):
1836
1955
  new_lines.append("import os\n")
1837
- new_lines.append("from app.database import Base\n")
1838
- new_lines.append("from app import models\n")
1956
+ new_lines.append("from app.database.database import Base, engine, DATABASE_URL\n")
1957
+ new_lines.append("import app.models\n")
1839
1958
  new_lines.append("from dotenv import load_dotenv\n\n")
1840
1959
  import_added = True
1841
1960
 
@@ -1849,12 +1968,24 @@ def edit_migration_env():
1849
1968
  new_lines.append("target_metadata = Base.metadata\n")
1850
1969
  continue # এটা দিলে নিচের new_lines.append(line) রান হবে না, ফলে পুরনো লাইনটি বাদ যাবে
1851
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
+
1852
1983
  # মূল লাইনটি অ্যাড করা (যদি উপরের 'continue' হিট না করে)
1853
1984
  new_lines.append(line)
1854
1985
 
1855
1986
  # ৪. config = context.config লাইনের পরে DATABASE_URL সেট করা
1856
1987
  if "config = context.config" in stripped and not db_url_added:
1857
- new_lines.append('config.set_main_option("sqlalchemy.url", os.getenv("DATABASE_URL"))\n\n')
1988
+ new_lines.append('config.set_main_option("sqlalchemy.url", DATABASE_URL)\n\n')
1858
1989
  db_url_added = True
1859
1990
 
1860
1991
  with open(file_path, "w", encoding="utf-8") as f:
@@ -33,15 +33,9 @@ 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
- subprocess.run([
40
- sys.executable,
41
- "alembic",
42
- "init",
43
- "migrations"
44
- ])
45
39
  console.print("[green]Alembic initialized successfully[/]")
46
40
  except subprocess.CalledProcessError as e:
47
41
  console.print(f"[red]Failed to initialize Alembic: {e}[/]")
@@ -61,13 +55,13 @@ def user_table_autogenerate():
61
55
 
62
56
  try:
63
57
  subprocess.run(
64
- ["alembic", "revision", "--autogenerate", "-m", "create users table"],
58
+ [ sys.executable, "-m", "alembic", "revision", "--autogenerate", "-m", "create users table"],
65
59
  check=True
66
60
  )
67
61
 
68
62
  console.print("[green]Migrate Table In DB[/]")
69
63
  subprocess.run(
70
- ["alembic", "upgrade", "head"],
64
+ [sys.executable, "-m", "alembic", "upgrade", "head"],
71
65
  check=True
72
66
  )
73
67
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shafikul_cli
3
- Version: 2.1.6
3
+ Version: 2.2.7
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
@@ -10,9 +10,7 @@ Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
11
  Requires-Dist: typer
12
12
  Requires-Dist: rich
13
- Requires-Dist: keyboard
14
- Requires-Dist: pyautogui
15
- Requires-Dist: pyperclip
13
+ Requires-Dist: questionary
16
14
  Dynamic: license-file
17
15
 
18
16
  # 🚀 Shafikul CLI
@@ -21,7 +19,7 @@ Dynamic: license-file
21
19
 
22
20
  <div align="center">
23
21
 
24
- ![Version](https://img.shields.io/badge/version-2.1.6-blue?style=flat-square)
22
+ ![Version](https://img.shields.io/badge/version-2.2.7-blue?style=flat-square)
25
23
  ![Python](https://img.shields.io/badge/python-3.9+-green?style=flat-square)
26
24
  ![License](https://img.shields.io/badge/license-MIT-purple?style=flat-square)
27
25
  [![GitHub](https://img.shields.io/badge/GitHub-build--with--shafikul-black?style=flat-square&logo=github)](https://github.com/build-with-shafikul/shafikul_cli)
@@ -175,7 +173,7 @@ your-project/
175
173
  │
176
174
  ├── 📂 static/ # Served static files
177
175
  │ └── images/
178
- │ └── logo.png
176
+ │ └── icon.png
179
177
  │
180
178
  ├── 📂 utility/ # Helper functions
181
179
  │ └── 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
@@ -0,0 +1,3 @@
1
+ typer
2
+ rich
3
+ questionary
@@ -1,5 +0,0 @@
1
- typer
2
- rich
3
- keyboard
4
- pyautogui
5
- pyperclip
File without changes
File without changes