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.
Files changed (25) hide show
  1. {shafikul_cli-2.1.5/shafikul_cli.egg-info → shafikul_cli-2.2.6}/PKG-INFO +3 -3
  2. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/README.md +2 -2
  3. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/pyproject.toml +1 -1
  4. shafikul_cli-2.2.6/shafikul_cli/asset/icon.png +0 -0
  5. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/main.py +70 -5
  6. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/utility/create_file_content.py +13 -8
  7. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/utility/helper_function.py +174 -42
  8. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/utility/install_dependicy.py +3 -3
  9. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6/shafikul_cli.egg-info}/PKG-INFO +3 -3
  10. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli.egg-info/SOURCES.txt +1 -0
  11. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/LICENSE +0 -0
  12. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/setup.cfg +0 -0
  13. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/__init__.py +0 -0
  14. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/asset/__init__.py +0 -0
  15. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/asset/logo.png +0 -0
  16. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/utility/__init__.py +0 -0
  17. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/utility/create_file_structure.py +0 -0
  18. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/utility/create_html_file.py +0 -0
  19. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/utility/ensure_db.py +0 -0
  20. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/utility/ensure_main.py +0 -0
  21. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli/utility/env_generate.py +0 -0
  22. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli.egg-info/dependency_links.txt +0 -0
  23. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli.egg-info/entry_points.txt +0 -0
  24. {shafikul_cli-2.1.5 → shafikul_cli-2.2.6}/shafikul_cli.egg-info/requires.txt +0 -0
  25. {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.1.5
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
- ![Version](https://img.shields.io/badge/version-2.1.5-blue?style=flat-square)
24
+ ![Version](https://img.shields.io/badge/version-2.1.6-blue?style=flat-square)
25
25
  ![Python](https://img.shields.io/badge/python-3.9+-green?style=flat-square)
26
26
  ![License](https://img.shields.io/badge/license-MIT-purple?style=flat-square)
27
27
  [![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 +175,7 @@ your-project/
175
175
  │
176
176
  ├── 📂 static/ # Served static files
177
177
  │ └── images/
178
- │ └── logo.png
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
- ![Version](https://img.shields.io/badge/version-2.1.5-blue?style=flat-square)
7
+ ![Version](https://img.shields.io/badge/version-2.1.6-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.5"
7
+ version = "2.2.6"
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"
@@ -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
 
@@ -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.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
 
@@ -69,12 +93,14 @@ def requirements(file_nm: str):
69
93
  sqlalchemy
70
94
  fastapi
71
95
  jinja2
72
- uvicorn[standerd]
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, RedirectResponse, Request
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
- # 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
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="127.0.0.1", port=8000, reload=True)
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,HTTPException
135
- from fastapi.responses import RedirectResponse, JSONResponse, HTMLResponse
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
- DATABASE_URL = os.getenv("DATABASE_URL")
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={"check_same_thread": False} # sqlite connect er jonno eita lagbe
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/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
241
311
  # ==========================================
242
- def models(file_nm: str):
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
- 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)
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/logo.png file create
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("logo.png")
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,HTMLResponse,RedirectResponse
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, JSONResponse
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, HTMLResponse, Request
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 bg-indigo-500/20 mb-6">
533
- <svg class="w-10 h-10 text-indigo-600 dark:text-indigo-400" fill="none" stroke="currentColor"
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
- Get Started
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
- Documentation
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 %}v1.0.0{% endblock %}</title>
1015
- <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">
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 %}Auth Page{% endblock %}</title>
1068
- <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">
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("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")
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", os.getenv("DATABASE_URL"))\n\n')
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.1.5
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
- ![Version](https://img.shields.io/badge/version-2.1.5-blue?style=flat-square)
24
+ ![Version](https://img.shields.io/badge/version-2.1.6-blue?style=flat-square)
25
25
  ![Python](https://img.shields.io/badge/python-3.9+-green?style=flat-square)
26
26
  ![License](https://img.shields.io/badge/license-MIT-purple?style=flat-square)
27
27
  [![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 +175,7 @@ your-project/
175
175
  │
176
176
  ├── 📂 static/ # Served static files
177
177
  │ └── images/
178
- │ └── logo.png
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