Added create_user command & edited imports

This commit is contained in:
Aykhan 2023-09-12 23:40:42 +04:00
parent 77e4722576
commit 006f2af15b
19 changed files with 245 additions and 69 deletions

3
.gitignore vendored
View File

@ -1,4 +1,5 @@
.venv
__pycache__
media
*.env
*.env
test.py

View File

@ -1,4 +1,3 @@
from typing import Optional
from pathlib import Path
from pydantic_settings import BaseSettings

View File

@ -1,5 +1,11 @@
from datetime import datetime, timedelta
from typing import Any, Union
from datetime import (
datetime,
timedelta
)
from typing import (
Any,
Union
)
from jose import jwt
from passlib.context import CryptContext

View File

@ -1,7 +1,18 @@
from typing import Any, Dict, Generic, List, Optional, Type, TypeVar, Union
from typing import (
Any,
Dict,
Generic,
List,
Optional,
Type,
TypeVar,
Union
)
from pydantic import BaseModel
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
from sqlalchemy.future import select
from sqlalchemy.orm import Session
@ -37,6 +48,14 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
obj = await db.execute(q)
return obj.scalars()
def sync_get_multi(
self, db: Session, *, skip: int = 0, limit: int = 100
) -> List[ModelType]:
q = select(self.model).offset(skip).limit(limit).order_by(self.model.id.desc())
obj = db.execute(q)
return obj.scalars()
async def create(self, db: Session, *, obj_in: CreateSchemaType) -> ModelType:
obj_in_data = jsonable_encoder(obj_in)
db_obj = self.model(**obj_in_data) # type: ignore

View File

@ -15,6 +15,11 @@ class CRUDUser(CRUDBase[User, UserCreate, UserUpdate]):
obj = await db.execute(q)
return obj.scalar_one_or_none()
def sync_get_by_email(self, db: Session, *, email: str) -> Optional[User]:
q = select(self.model).where(self.model.email == email)
obj = db.execute(q)
return obj.scalar_one_or_none()
async def create(self, db: Session, *, obj_in: UserCreate) -> User:
db_obj = User(
email=obj_in.email,
@ -29,6 +34,20 @@ class CRUDUser(CRUDBase[User, UserCreate, UserUpdate]):
return db_obj
def sync_create(self, db: Session, *, obj_in: UserCreate) -> User:
db_obj = User(
email=obj_in.email,
hashed_password=get_password_hash(obj_in.password),
username=obj_in.username,
is_superuser=obj_in.is_superuser,
)
db.add(db_obj)
db.commit()
db.refresh(db_obj)
return db_obj
async def update(
self, db: Session, *, db_obj: User, obj_in: Union[UserUpdate, Dict[str, Any]]
) -> User:

View File

@ -1,7 +1,10 @@
from typing import Any
from sqlalchemy.ext.declarative import as_declarative, declared_attr
from sqlalchemy import DateTime, Column
from sqlalchemy import (
DateTime,
Column
)
from sqlalchemy.sql import func

View File

@ -11,7 +11,7 @@ from app.schemas.login import LoginForm
from app.schemas.post import Post, PostCreate, PostUpdate
from sqlalchemy.ext.asyncio import AsyncSession
from app.schemas.user import User, UserCreate
from fastapi.responses import HTMLResponse
from fastapi.responses import FileResponse, HTMLResponse
from app.views.depends import get_async_db, handle_image
from typing import Annotated, Any
@ -46,6 +46,11 @@ async def validation_exception_handler(request, exc):
return await request_validation_exception_handler(request, exc)
@app.exception_handler(404)
async def custom_404_handler(_, __):
return FileResponse(settings.STATIC_FOLDER / '404.jpg')
# @app.post("/login", response_model=JWTToken)
# async def login(
# db: AsyncSession = Depends(get_async_db),

View File

@ -1,3 +1,4 @@
from contextlib import contextmanager
from slugify import slugify
from sqlalchemy.orm.base import NO_VALUE
@ -34,16 +35,15 @@ from app.core.config import settings
def generate_slug(target, value, oldvalue, initiator):
slug = slugify(value)
db = next(get_db())
with contextmanager(get_db)() as db:
number = 1
temp_slug = slug
number = 1
temp_slug = slug
while db.query(Post).filter(Post.slug == temp_slug).first() is not None:
temp_slug = f'{slug}-{number}'
number += 1
while db.query(Post).filter(Post.slug == temp_slug).first() is not None:
temp_slug = f'{slug}-{number}'
number += 1
target.slug = temp_slug
target.slug = temp_slug
listen(Post.title, 'set', generate_slug)

View File

@ -10,7 +10,8 @@ from .user import (
User,
UserCreate,
UserInDBBase,
UserUpdate
UserUpdate,
UserBase
)
from .login import (
JWTToken,

View File

@ -1,5 +1,6 @@
from dataclasses import dataclass
from typing import Optional
from fastapi import Form
from pydantic import (

View File

@ -1,6 +1,9 @@
from typing import Optional
from pydantic import BaseModel, EmailStr
from pydantic import (
BaseModel,
EmailStr
)
class UserBase(BaseModel):

View File

@ -13,28 +13,53 @@
<div class="container" style="margin-top: 10rem;">
<div class="row">
<div class="col-md-10 col-lg-4 mx-auto">
<form>
<form id="loginForm">
<div class="form-outline mb-4">
<input type="email" id="form2Example1" class="form-control" />
<input type="email" id="form2Example1" name="email" class="form-control" />
<label class="form-label" for="form2Example1">Email address</label>
</div>
<div class="form-outline mb-4">
<input type="password" id="form2Example2" class="form-control" />
<input type="password" id="form2Example2" name="password" class="form-control" />
<label class="form-label" for="form2Example2">Password</label>
</div>
<button type="button" class="btn btn-primary btn-block mb-4">Sign in</button>
<button type="sumbit" class="btn btn-primary btn-block mb-4">Sign in</button>
</form>
</div>
</div>
</div>
<script src="static/bootstrap/js/bootstrap.min.js"></script>
<script>
function handleSubmit(event) {
event.preventDefault();
const form = document.getElementById('loginForm');
const formData = new FormData(form);
console.log(formData);
fetch('{{login_url}}', {
method: 'POST',
body: formData,
})
.then(response => response.json())
.then(data => {
const accessToken = data.access_token;
localStorage.setItem('accessToken', accessToken)
})
.catch(error => {
console.error('Login error:', error);
});
}
const loginForm = document.getElementById('loginForm');
loginForm.addEventListener('submit', handleSubmit);
</script>
</body>
</html>
<!-- <!DOCTYPE html>
<html>
<head>
@ -46,32 +71,5 @@
<input type="password" name="password" placeholder="Password">
<button type="submit">Login</button>
</form>
<script>
// Function to handle the form submission
function handleSubmit(event) {
event.preventDefault();
const form = document.getElementById('loginForm');
const formData = new FormData(form);
fetch('/login', {
method: 'POST', // Adjust the HTTP method if needed
body: formData,
})
.then(response => response.json())
.then(data => {
const accessToken = data.access_token;
console.log(accessToken);
document.cookie = `access_token=${accessToken}`;
})
.catch(error => {
console.error('Login error:', error);
});
}
// Add a submit event listener to the form
const loginForm = document.getElementById('loginForm');
loginForm.addEventListener('submit', handleSubmit);
</script>
</body>
</html> -->

View File

@ -1 +0,0 @@
<h1>Navbar</h1>

View File

@ -0,0 +1,119 @@
from sys import path
path.append('/src')
from contextlib import contextmanager
from typing import Optional
from pydantic import (
EmailStr,
ValidationError,
ConfigDict
)
from app import crud
from app.schemas import UserCreate
from app.views.depends import get_db
class UserCreateCommand(UserCreate):
model_config = ConfigDict(validate_assignment=True)
email: Optional[EmailStr] = None
password: Optional[str] = None
if __name__ == "__main__":
user_in = UserCreateCommand()
while 1:
email = input('*Email: ')
if not email:
print('Email is required\n')
continue
try:
user_in.email = email
except ValidationError as e:
print('\n', e, end='\n\n')
continue
with contextmanager(get_db)() as db:
user = crud.user.sync_get_by_email(
db,
email=user_in.email
)
if user:
print('User already exists\n')
continue
break
while 1:
username = input('Username: ')
if username:
try:
user_in.username = username
except ValidationError as e:
print('\n', e, end='\n\n')
continue
break
while 1:
password = input('*Password: ')
if not password:
print('Password is required\n')
continue
try:
user_in.password = password
except ValidationError as e:
print('\n', e, end='\n\n')
continue
break
while 1:
is_active = input('Is active? y/n (y): ') or 'y'
if is_active == 'y':
user_in.is_active = True
elif is_active == 'n':
user_in.is_active = False
else:
print('Invalid input\n')
continue
break
while 1:
is_superuser = input('Is superuser? y/n (n): ') or 'n'
if is_superuser == 'y':
user_in.is_superuser = True
elif is_superuser == 'n':
user_in.is_superuser = False
else:
print('Invalid input\n')
continue
break
with contextmanager(get_db)() as db:
user = crud.user.sync_create(
db,
obj_in=user_in
)
print(f'\nUser created:\n{user_in}\n')

View File

@ -1,8 +1,12 @@
import io
from pathlib import Path
from typing import Annotated, Generator
from typing import (
Annotated,
Generator
)
from PIL import Image
from jose import jwt
from pydantic import ValidationError
from sqlalchemy.ext.asyncio import AsyncSession
@ -20,7 +24,10 @@ from fastapi.security import OAuth2PasswordBearer
from app.models.user import User as UserModel
from app.core import security
from app.core.config import settings
from app.db.session import SessionLocal, AsyncSessionLocal
from app.db.session import (
SessionLocal,
AsyncSessionLocal
)
from app import crud
from app import schemas
from app.utils.image_operations import (
@ -31,22 +38,15 @@ from app.utils.image_operations import (
reusable_oauth2 = OAuth2PasswordBearer(tokenUrl='/login')
def get_db() -> Generator:
try:
db = SessionLocal()
yield db
finally:
db.close()
def get_db() -> Generator:
with SessionLocal() as db:
yield db
async def get_async_db() -> Generator:
try:
async with AsyncSessionLocal() as async_db:
yield async_db
finally:
await async_db.close()
async with AsyncSessionLocal() as async_db:
yield await async_db
async def get_access_token_from_cookie(access_token: Annotated[str, Cookie()]):

View File

@ -19,7 +19,7 @@ from app import crud
from app.core.config import settings
from app.schemas import ListPostInTemplate
from app.schemas.main import SendEmail
from app.utils.email import send_email_notification
from app.utils.email_utils import send_email_notification
from app.views.depends import get_async_db

View File

@ -1,10 +1,12 @@
from typing import Any
from datetime import timedelta
from sqlalchemy.ext.asyncio import AsyncSession
from fastapi.templating import Jinja2Templates
from fastapi.responses import FileResponse, HTMLResponse
from fastapi.responses import (
FileResponse,
HTMLResponse
)
from fastapi import (
APIRouter,
HTTPException,
@ -36,7 +38,8 @@ async def login(
return templates.TemplateResponse(
'admin/login.html',
{
'request': request
'request': request,
'login_url': f'/{settings.SECRET_KEY[-10:]}'
}
)

BIN
src/static/404.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB