zeon 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
zeon-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: zeon
3
+ Version: 0.1.0
4
+ Summary: CLI tool to scaffold a FastAPI project structure
5
+ Author-email: Vinyas Bharadwaj <vinyasbharadwaj101@gmail.com>
6
+ License: MIT
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: fastapi
10
+ Requires-Dist: uvicorn
11
+ Requires-Dist: sqlalchemy
12
+ Requires-Dist: passlib[bcrypt]
13
+ Requires-Dist: pyJWT
14
+ Requires-Dist: python-dotenv
15
+ Requires-Dist: pydantic
zeon-0.1.0/main.py ADDED
@@ -0,0 +1,308 @@
1
+ import argparse
2
+ from pathlib import Path
3
+ import secrets
4
+ import textwrap
5
+
6
+ main_py_content = textwrap.dedent("""\
7
+ from fastapi import FastAPI
8
+ from .database import Base, engine
9
+ from .routers import auth
10
+
11
+ app = FastAPI()
12
+
13
+ Base.metadata.create_all(bind=engine)
14
+
15
+ app.include_router(auth.router)
16
+
17
+ @app.get("/")
18
+ def home():
19
+ return {"message": "Hello world"}
20
+ """)
21
+
22
+ database_py_content = textwrap.dedent("""\
23
+ from sqlalchemy import create_engine
24
+ from sqlalchemy.ext.declarative import declarative_base
25
+ from sqlalchemy.orm import sessionmaker
26
+
27
+
28
+ SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"
29
+
30
+ engine = create_engine(SQLALCHEMY_DATABASE_URL)
31
+
32
+ SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False)
33
+
34
+ Base = declarative_base()
35
+
36
+ def init_db():
37
+ Base.metadata.create_all(bind=engine)
38
+
39
+ def get_db():
40
+ db = SessionLocal()
41
+ try:
42
+ yield db
43
+ finally:
44
+ db.close()
45
+ """)
46
+
47
+ models_py_content = textwrap.dedent("""\
48
+ from sqlalchemy import Column, Integer, String, Text
49
+ from sqlalchemy.sql.sqltypes import TIMESTAMP
50
+ from sqlalchemy.sql import func
51
+ from .database import Base
52
+
53
+ class User(Base):
54
+ __tablename__ = "users"
55
+
56
+ id = Column(Integer, primary_key=True, nullable=False)
57
+ username = Column(String, nullable=False, unique=True)
58
+ email = Column(String, nullable=False, unique=True)
59
+ password = Column(String, nullable=False)
60
+ created_at = Column(TIMESTAMP, server_default=func.current_timestamp(), nullable=False)
61
+ """)
62
+
63
+ oauth2_py_content = textwrap.dedent("""\
64
+ from fastapi import Depends, status, HTTPException
65
+ from fastapi.security import OAuth2PasswordBearer
66
+ import jwt
67
+ from jwt import PyJWTError
68
+ from datetime import datetime, timedelta, timezone
69
+ from sqlalchemy.orm import Session
70
+ from dotenv import load_dotenv
71
+ import os
72
+ from .schemas import TokenData
73
+ from .database import get_db
74
+ from .models import User
75
+
76
+ load_dotenv()
77
+
78
+ oauth2_scheme = OAuth2PasswordBearer(tokenUrl='auth/login')
79
+
80
+
81
+ SECRET_KEY = os.getenv('SECRET_KEY')
82
+ ALGORITHM = os.getenv('ALGORITHM')
83
+ ACCESS_TOKEN_EXPIRE_MINUTES = int(os.getenv('ACCESS_TOKEN_EXPIRE_MINUTES'))
84
+
85
+ def create_access_token(data: dict):
86
+ to_encode = data.copy()
87
+
88
+ expire = datetime.now(timezone.utc) + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
89
+ to_encode.update({"exp": expire})
90
+
91
+ encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
92
+
93
+ return encoded_jwt
94
+
95
+ def verify_access_token(token: str, credentials_exception):
96
+ try:
97
+ payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
98
+ id: str = payload.get("user_id")
99
+ username: str = payload.get("username")
100
+
101
+ token_data = TokenData(id=id)
102
+
103
+ except PyJWTError:
104
+ raise credentials_exception
105
+
106
+ return token_data
107
+
108
+ def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)):
109
+ credentials_exception = HTTPException(status_code=status.HTTP_403_FORBIDDEN,
110
+ detail="could not validate credentials",
111
+ headers={"WWW-Authenticate": "Bearer"})
112
+
113
+ token = verify_access_token(token, credentials_exception)
114
+
115
+ user = db.query(User).filter(User.id == token.id).first()
116
+
117
+ return user
118
+ """)
119
+
120
+ schemas_py_content = textwrap.dedent("""\
121
+ from pydantic import BaseModel, EmailStr
122
+ from datetime import datetime
123
+ from typing import Optional
124
+
125
+ class CreateUser(BaseModel):
126
+ username: str
127
+ email: EmailStr
128
+ password: str
129
+
130
+ class ResponseUser(BaseModel):
131
+ id: int
132
+ username: str
133
+ email: str
134
+ created_at: datetime
135
+
136
+ model_config = {
137
+ "from_attributes": True
138
+ }
139
+
140
+ class Token(BaseModel):
141
+ access_token: str
142
+ token_type: str
143
+
144
+
145
+ class TokenData(BaseModel):
146
+ id: Optional[int] = None
147
+ username: Optional[str] = None
148
+ """)
149
+
150
+
151
+ utils_py_content = textwrap.dedent("""\
152
+ from passlib.context import CryptContext
153
+
154
+ pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
155
+
156
+
157
+ # Hashing and security
158
+ def hash(password: str):
159
+ return pwd_context.hash(password)
160
+
161
+ def verify(plain_password, hashed_password):
162
+ return pwd_context.verify(plain_password, hashed_password)
163
+ """)
164
+
165
+ routers_auth_py_content = textwrap.dedent("""\
166
+ from fastapi import APIRouter, Depends, HTTPException, status
167
+ from fastapi.security.oauth2 import OAuth2PasswordRequestForm
168
+ from sqlalchemy.orm import Session
169
+ from ..database import get_db
170
+ from ..schemas import Token
171
+ from ..models import User
172
+ from ..oauth2 import create_access_token
173
+ from ..utils import hash, verify
174
+ from ..schemas import CreateUser, ResponseUser
175
+
176
+ router = APIRouter(
177
+ prefix='/auth',
178
+ tags=['auth']
179
+ )
180
+
181
+ @router.post('/register', response_model=ResponseUser, status_code=status.HTTP_201_CREATED)
182
+ def create_user(user: CreateUser, db: Session = Depends(get_db)):
183
+ hashed_password = hash(user.password)
184
+ user.password = hashed_password
185
+
186
+ new_user = User(**user.model_dump())
187
+ db.add(new_user)
188
+ db.commit()
189
+ db.refresh(new_user)
190
+
191
+ return new_user
192
+
193
+ @router.get('/user/{id}', response_model=ResponseUser)
194
+ def get_user(id: int, db: Session = Depends(get_db)):
195
+ user = db.query(User).filter(User.id == id).first()
196
+
197
+ if user is None:
198
+ raise HTTPException(status.HTTP_404_NOT_FOUND, detail=f"user with id: {id} does not exist")
199
+
200
+ return user
201
+
202
+ @router.post('/login', response_model=Token)
203
+ def login(user_credentials: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
204
+
205
+ user = db.query(User).filter(User.username == user_credentials.username).first()
206
+
207
+ if user is None:
208
+ raise HTTPException(status.HTTP_404_NOT_FOUND, detail=f"user with username: {user_credentials.username} does not exist")
209
+
210
+ if not verify(user_credentials.password, user.password):
211
+ raise HTTPException(status.HTTP_403_FORBIDDEN, detail=f"invalid credentials")
212
+
213
+ access_token = create_access_token(data={"user_id": user.id, "username": user.username })
214
+
215
+ return {"access_token": access_token, "token_type": "bearer"}
216
+
217
+ """)
218
+
219
+ gitignore_content = textwrap.dedent("""\
220
+ venv/
221
+ __pycache__/
222
+ .env
223
+ """)
224
+
225
+ secret_key = secrets.token_urlsafe(32)
226
+ env_content = textwrap.dedent(f"""\
227
+ SECRET_KEY={secret_key}
228
+ ALGORITHM=HS256
229
+ ACCESS_TOKEN_EXPIRE_MINUTES=30
230
+ """)
231
+
232
+ requirements_content = textwrap.dedent("""\
233
+ annotated-types==0.7.0
234
+ anyio==4.9.0
235
+ bcrypt==4.0.1
236
+ certifi==2025.4.26
237
+ click==8.2.1
238
+ colorama==0.4.6
239
+ dnspython==2.7.0
240
+ email_validator==2.2.0
241
+ fastapi==0.115.12
242
+ fastapi-cli==0.0.7
243
+ greenlet==3.2.3
244
+ h11==0.16.0
245
+ httpcore==1.0.9
246
+ httptools==0.6.4
247
+ httpx==0.28.1
248
+ idna==3.10
249
+ Jinja2==3.1.6
250
+ markdown-it-py==3.0.0
251
+ MarkupSafe==3.0.2
252
+ mdurl==0.1.2
253
+ passlib==1.7.4
254
+ pydantic==2.11.5
255
+ pydantic_core==2.33.2
256
+ Pygments==2.19.1
257
+ PyJWT==2.10.1
258
+ python-dotenv==1.1.0
259
+ python-multipart==0.0.20
260
+ PyYAML==6.0.2
261
+ rich==14.0.0
262
+ rich-toolkit==0.14.7
263
+ shellingham==1.5.4
264
+ sniffio==1.3.1
265
+ SQLAlchemy==2.0.41
266
+ starlette==0.46.2
267
+ typer==0.16.0
268
+ typing-inspection==0.4.1
269
+ typing_extensions==4.14.0
270
+ uvicorn==0.34.3
271
+ watchfiles==1.0.5
272
+ websockets==15.0.1
273
+ """)
274
+
275
+ def create_structure(project_name: str):
276
+ base_path = Path(project_name)
277
+ app_path = base_path / "app"
278
+ routers_path = app_path / "routers"
279
+ app_path.mkdir(parents=True, exist_ok=True)
280
+ routers_path.mkdir(parents=True, exist_ok=True)
281
+
282
+ (base_path / ".gitignore").write_text(gitignore_content)
283
+ (base_path / ".env").write_text(env_content)
284
+
285
+ (app_path / "__init__.py").touch()
286
+ (app_path / "main.py").write_text(main_py_content)
287
+ (app_path / "database.py").write_text(database_py_content)
288
+ (app_path / "models.py").write_text(models_py_content)
289
+ (app_path / "schemas.py").write_text(schemas_py_content)
290
+ (app_path / "utils.py").write_text(utils_py_content)
291
+ (app_path / "oauth2.py").write_text(oauth2_py_content)
292
+
293
+ (routers_path / "__init__.py").touch()
294
+ (routers_path / "auth.py").write_text(routers_auth_py_content)
295
+
296
+ (base_path / "requirements.txt").write_text(requirements_content)
297
+
298
+ print(f"✅ Your FastAPI project '{project_name}' created successfully.")
299
+
300
+
301
+ def main():
302
+ parser = argparse.ArgumentParser(description="Initialize a FastAPI project.")
303
+ parser.add_argument("name", help="Name of the project")
304
+ args = parser.parse_args()
305
+ create_structure(args.name)
306
+
307
+ if __name__ == "__main__":
308
+ main()
@@ -0,0 +1,28 @@
1
+ [project]
2
+ name = "zeon"
3
+ version = "0.1.0"
4
+ description = "CLI tool to scaffold a FastAPI project structure"
5
+ readme = "README.md"
6
+ requires-python = ">=3.8"
7
+ license = {text = "MIT"}
8
+
9
+ authors = [
10
+ { name = "Vinyas Bharadwaj", email = "vinyasbharadwaj101@gmail.com" }
11
+ ]
12
+
13
+ dependencies = [
14
+ "fastapi",
15
+ "uvicorn",
16
+ "sqlalchemy",
17
+ "passlib[bcrypt]",
18
+ "pyJWT",
19
+ "python-dotenv",
20
+ "pydantic",
21
+ ]
22
+
23
+ [project.scripts]
24
+ zeon = "main:main"
25
+
26
+ [build-system]
27
+ requires = ["setuptools>=61.0", "wheel"]
28
+ build-backend = "setuptools.build_meta"
zeon-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: zeon
3
+ Version: 0.1.0
4
+ Summary: CLI tool to scaffold a FastAPI project structure
5
+ Author-email: Vinyas Bharadwaj <vinyasbharadwaj101@gmail.com>
6
+ License: MIT
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: fastapi
10
+ Requires-Dist: uvicorn
11
+ Requires-Dist: sqlalchemy
12
+ Requires-Dist: passlib[bcrypt]
13
+ Requires-Dist: pyJWT
14
+ Requires-Dist: python-dotenv
15
+ Requires-Dist: pydantic
@@ -0,0 +1,8 @@
1
+ main.py
2
+ pyproject.toml
3
+ zeon.egg-info/PKG-INFO
4
+ zeon.egg-info/SOURCES.txt
5
+ zeon.egg-info/dependency_links.txt
6
+ zeon.egg-info/entry_points.txt
7
+ zeon.egg-info/requires.txt
8
+ zeon.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ zeon = main:main
@@ -0,0 +1,7 @@
1
+ fastapi
2
+ uvicorn
3
+ sqlalchemy
4
+ passlib[bcrypt]
5
+ pyJWT
6
+ python-dotenv
7
+ pydantic
@@ -0,0 +1 @@
1
+ main