commit f1186d7483f0670dbdca20ad2022993811e13208 Author: Ashibaya Date: Fri Jul 23 14:48:20 2021 +0900 Init files diff --git a/auth/auth.py b/auth/auth.py new file mode 100644 index 0000000..6bc2db8 --- /dev/null +++ b/auth/auth.py @@ -0,0 +1,13 @@ +from sqlalchemy.engine.base import RootTransaction +from auth.routers.authentication import login +from fastapi import APIRouter +from auth.routers import login, authentication, roles +from auth.login.models import login as models +from databases.pgsql.database import engine +router = APIRouter() + +models.Base.metadata.create_all(engine) + +router.include_router(login.router) +router.include_router(authentication.router) +router.include_router(roles.router) \ No newline at end of file diff --git a/auth/hashing.py b/auth/hashing.py new file mode 100644 index 0000000..d2e45c8 --- /dev/null +++ b/auth/hashing.py @@ -0,0 +1,10 @@ +from passlib.context import CryptContext + +pwd_cxt = CryptContext(schemes=["bcrypt"], deprecated="auto") + +class Hash(): + def bcrypt(password: str): + return pwd_cxt.hash(password) + + def verify(plain_password, hashed_password): + return pwd_cxt.verify(plain_password,hashed_password) \ No newline at end of file diff --git a/auth/login/__init__.py b/auth/login/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/auth/login/models/__init__.py b/auth/login/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/auth/login/models/login.py b/auth/login/models/login.py new file mode 100644 index 0000000..9bb7da2 --- /dev/null +++ b/auth/login/models/login.py @@ -0,0 +1,27 @@ +from sqlalchemy import Boolean, Column, ForeignKey, Integer, String +from sqlalchemy.orm import relationship + +from databases.pgsql.database import Base + +class User(Base): + __tablename__ = "users" + + id = Column(Integer, primary_key= True, index= True) + email = Column(String, unique= True, index=True) + phone = Column(String, unique= True, index=True) + login = Column(String, unique= True, index=True) + name = Column(String) + surname = Column(String) + hashed_password = Column(String) + role_id = Column(Integer, ForeignKey("roles.id")) + is_active = Column(Boolean, default=True) + +class Role(Base): + __tablename__ = "roles" + + id = Column(Integer, primary_key=True, index= True) + name = Column(String, index= True) + description = Column(String , index= True) + + + diff --git a/auth/login/repository/__init__.py b/auth/login/repository/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/auth/login/repository/login.py b/auth/login/repository/login.py new file mode 100644 index 0000000..c35f725 --- /dev/null +++ b/auth/login/repository/login.py @@ -0,0 +1,68 @@ +from sqlalchemy.orm import Session +from fastapi import HTTPException, status +from ..schemas import login as schemas +from ..models import login as models +from auth.hashing import Hash + +def get_user(db: Session, user_id: int): + return db.query(models.User).filter(models.User.id == user_id).first() + +def get_user_by_email(db: Session, email: str): + return db.query(models.User).filter(models.User.email == email).first() + +def get_user_by_login(db: Session, login:str): + return db.query(models.User).filter(models.User.login == login).first() + +def get_user_by_phone(db: Session, phone: str): + return db.query(models.User).filter(models.User.phone == phone).first() + +def delete_user(db: Session, id: int): + user = db.query(models.User).filter(models.User.id == id) + if not user.first(): + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, + detail=f"User with id {id} not found") + user.delete(synchronize_session=False) + db.commit() + return 'done' + + +def get_users(db: Session, skip: int = 0, limit: int =100): + return db.query(models.User).offset(skip).limit(limit).all() + +def create_user(db: Session, user: schemas.UserCreate): + now_hashed_password = Hash.bcrypt(user.password) + db_user = models.User( + email=user.email, + login = user.login, + phone = user.phone, + hashed_password = now_hashed_password, + role_id = user.role_id + ) + db.add(db_user) + db.commit() + db.refresh(db_user) + return db_user + +def get_roles(db: Session, skip: int =0, limit: int =100): + return db.query(models.Role).offset(skip).limit(limit).all() + + +def get_role(db: Session, id:int): + return db.query(models.Role).filter(models.Role.id == id).first() + +def delete_role(db: Session, id: int): + role = db.query(models.Role).filter(models.Role.id == id) + if not role.first(): + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, + detail=f"Role with id {id} not found") + role.delete(synchronize_session=False) + db.commit() + return 'done' + + +def create_roles(db: Session, role: schemas.RoleCreate): + db_role = models.Role(name = role.name, description= role.description) + db.add(db_role) + db.commit() + db.refresh(db_role) + return db_role \ No newline at end of file diff --git a/auth/login/schemas/__init__.py b/auth/login/schemas/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/auth/login/schemas/login.py b/auth/login/schemas/login.py new file mode 100644 index 0000000..b52d1ed --- /dev/null +++ b/auth/login/schemas/login.py @@ -0,0 +1,41 @@ +from typing import List, Optional + +from pydantic import BaseModel + +class RoleBase(BaseModel): + name: str + description: Optional[str] = None + +class RoleCreate(RoleBase): + pass + +class Role(RoleBase): + id: int + class Config: + orm_mode = True + + +class UserBase(BaseModel): + email: str + login: str + role_id: int + phone: Optional[str] = None + name: Optional[str] = None + surname: Optional[str] = None + +class UserCreate(UserBase): + password: str + +class User(UserBase): + id: int + is_active: bool + class Config: + orm_mode = True + +class Token(BaseModel): + access_token: str + token_type: str + + +class TokenData(BaseModel): + email: Optional[str] = None \ No newline at end of file diff --git a/auth/oauth2.py b/auth/oauth2.py new file mode 100644 index 0000000..7cd2836 --- /dev/null +++ b/auth/oauth2.py @@ -0,0 +1,15 @@ +from fastapi import Depends, HTTPException, status +from fastapi.security import OAuth2PasswordBearer +from auth import token + +oauth2_scheme = OAuth2PasswordBearer(tokenUrl="auth/login") + + +def get_current_user(data: str = Depends(oauth2_scheme)): + credentials_exception = HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Could not validate credentials", + headers={"WWW-Authenticate": "Bearer"}, + ) + + return token.verify_token(data, credentials_exception) diff --git a/auth/routers/__init__.py b/auth/routers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/auth/routers/authentication.py b/auth/routers/authentication.py new file mode 100644 index 0000000..5838b95 --- /dev/null +++ b/auth/routers/authentication.py @@ -0,0 +1,24 @@ +from fastapi import APIRouter, Depends, status, HTTPException +from fastapi.security import OAuth2PasswordRequestForm +from auth import token +from auth.login.models import login as models +from databases.pgsql import database +from auth.hashing import Hash +from sqlalchemy.orm import Session + +router = APIRouter(tags=['Authentication']) + + +@router.post('/login') +def login(request: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(database.get_db)): + user = db.query(models.User).filter( + models.User.email == request.username).first() + if not user: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, + detail=f"Invalid Credentials") + if not Hash.verify(request.password,user.hashed_password): + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, + detail=f"Incorrect password") + + access_token = token.create_access_token(data={"sub": user.email}) + return {"access_token": access_token, "token_type": "bearer"} diff --git a/auth/routers/login.py b/auth/routers/login.py new file mode 100644 index 0000000..9b0c245 --- /dev/null +++ b/auth/routers/login.py @@ -0,0 +1,34 @@ +from auth.login import models +from auth.auth import login +from fastapi import APIRouter +from auth.login.schemas import login as schemas +from auth.login.models import login as models +from sqlalchemy.orm import Session +from fastapi import APIRouter, Depends, status +from auth.login.repository import login as user +from databases.pgsql import database + +router = APIRouter( + prefix="/user", + tags=['Users'] +) + +get_db = database.get_db + + +@router.post('/', response_model=schemas.UserCreate) +def create_user(request: schemas.UserCreate, db: Session = Depends(get_db)): + print(request) + return user.create_user(db, request) + +@router.delete('/{id}') +def delete_user(id:int, db: Session = Depends(get_db)): + return user.delete_user(db, id) + +@router.get('/{id}', response_model=schemas.User) +def get_user(id: int, db: Session = Depends(get_db)): + return user.get_user(db, id) + +@router.get('/') +def get_users(db: Session = Depends(get_db)): + return user.get_users(db) \ No newline at end of file diff --git a/auth/routers/roles.py b/auth/routers/roles.py new file mode 100644 index 0000000..baa9cc1 --- /dev/null +++ b/auth/routers/roles.py @@ -0,0 +1,34 @@ +import re +from auth.login import models +from auth.auth import login +from fastapi import APIRouter +from auth.login.schemas import login as schemas +from auth.login.models import login as models +from sqlalchemy.orm import Session +from fastapi import APIRouter, Depends, status +from auth.login.repository import login as user +from databases.pgsql import database + +router = APIRouter( + prefix="/roles", + tags=['Roles'] +) + +get_db = database.get_db + + +@router.post('/', response_model=schemas.RoleCreate) +def create_role(request: schemas.RoleCreate, db: Session = Depends(get_db)): + return user.create_roles(db, request) + +@router.get('/{id}',response_model=schemas.Role) +def show_role(id :int, db: Session = Depends(get_db)): + return user.get_role(db, id) + +@router.get('/') +def show_roles(db: Session = Depends(get_db)): + return user.get_roles(db) + +@router.delete('/{id}') +def delete_role(id: int, db: Session = Depends(get_db)): + return user.delete_role(db, id) \ No newline at end of file diff --git a/auth/token.py b/auth/token.py new file mode 100644 index 0000000..7023aa0 --- /dev/null +++ b/auth/token.py @@ -0,0 +1,26 @@ +from jose import JWTError, jwt +from datetime import datetime, timedelta +from auth.login.schemas import login as schemas + +SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7" +ALGORITHM = "HS256" +ACCESS_TOKEN_EXPIRE_MINUTES = 30 + + +def create_access_token(data: dict): + to_encode = data.copy() + expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) + to_encode.update({"exp": expire}) + encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) + return encoded_jwt + + +def verify_token(token: str, credentials_exception): + try: + payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) + email: str = payload.get("sub") + if email is None: + raise credentials_exception + token_data = schemas.TokenData(email=email) + except JWTError: + raise credentials_exception diff --git a/databases/__init__.py b/databases/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/databases/mssql/__init__.py b/databases/mssql/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/databases/mssql/database.py b/databases/mssql/database.py new file mode 100644 index 0000000..11ec659 --- /dev/null +++ b/databases/mssql/database.py @@ -0,0 +1,23 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + + +SQL_ALCHEMY_DATABASE_URL_MSSQL = "" + + +engine = create_engine( + SQL_ALCHEMY_DATABASE_URL_MSSQL, connect_args = {"check_same_thread": False} +) + +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind= engine) + +Base = declarative_base() + + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() \ No newline at end of file diff --git a/databases/mssql/general.py b/databases/mssql/general.py new file mode 100644 index 0000000..eebfbdf --- /dev/null +++ b/databases/mssql/general.py @@ -0,0 +1,23 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + + +SQL_ALCHEMY_DATABASE_URL_MSSQL = "mssql+pyodbc://sa:gjghj,eqgjl,thb@172.16.4.58:1433/General?driver=SQL+Server" + + +engine = create_engine( + SQL_ALCHEMY_DATABASE_URL_MSSQL, connect_args = {"check_same_thread": False} +) + +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind= engine) + +Base = declarative_base() + + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() \ No newline at end of file diff --git a/databases/pgsql/__init__.py b/databases/pgsql/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/databases/pgsql/database.py b/databases/pgsql/database.py new file mode 100644 index 0000000..d93a891 --- /dev/null +++ b/databases/pgsql/database.py @@ -0,0 +1,20 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + + +SQL_ALCHEMY_DATABASE_URL_PGSQL = "postgresql+psycopg2://postgres:@localhost/general" +engine = create_engine( + SQL_ALCHEMY_DATABASE_URL_PGSQL #, connect_args = {"check_same_thread": False} +) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind= engine) + +Base = declarative_base() + + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() \ No newline at end of file diff --git a/databases/pgsql/general.py b/databases/pgsql/general.py new file mode 100644 index 0000000..daa0d3f --- /dev/null +++ b/databases/pgsql/general.py @@ -0,0 +1,20 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + + +SQL_ALCHEMY_DATABASE_URL_PGSQL = "postgresql+psycopg2://postgres:159357@localhost/general" +engine = create_engine( + SQL_ALCHEMY_DATABASE_URL_PGSQL #, connect_args = {"check_same_thread": False} +) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind= engine) + +Base = declarative_base() + + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() \ No newline at end of file diff --git a/default/__init__.py b/default/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/default/models/__init__.py b/default/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/default/repository/__init__.py b/default/repository/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/default/schemas/__init__.py b/default/schemas/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kv/__init__.py b/kv/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kv/general/__init__.py b/kv/general/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kv/general/models/__init__.py b/kv/general/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kv/general/models/general.py b/kv/general/models/general.py new file mode 100644 index 0000000..a42635f --- /dev/null +++ b/kv/general/models/general.py @@ -0,0 +1,24 @@ +from sqlalchemy import Boolean, Column, ForeignKey, Integer, String +from sqlalchemy.orm import relationship + +from databases.mssql.database import Base + + +class Region(Base): + __tablename__ = "regions" + + id = Column( Integer, primary_key= True, index= True) + name = Column(String(255)) + active = Column(Boolean, default=True) + + + +class City(Base): + __tablename__ = "cities" + + id = Column(Integer, autoincrement= True, primary_key= True, index= True) + name = Column(String(255)) + region_id = Column(Integer, ForeignKey(regions.id)) + active = Column(Boolean, default= True) + + diff --git a/kv/general/repository/__init__.py b/kv/general/repository/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kv/general/schemas/__init__.py b/kv/general/schemas/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kv/general/schemas/general.py b/kv/general/schemas/general.py new file mode 100644 index 0000000..5602726 --- /dev/null +++ b/kv/general/schemas/general.py @@ -0,0 +1,3 @@ +from typing import List, Optional + +from pydantic import BaseModel \ No newline at end of file diff --git a/kv/kv.py b/kv/kv.py new file mode 100644 index 0000000..6443189 --- /dev/null +++ b/kv/kv.py @@ -0,0 +1,9 @@ +from fastapi import APIRouter, Depends +from auth import oauth2 +from auth.login.schemas import login as schemas +from kv.routers import general + +router = APIRouter() + +router.include_router(general.router) + diff --git a/kv/routers/__init__.py b/kv/routers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kv/routers/general.py b/kv/routers/general.py new file mode 100644 index 0000000..03a3121 --- /dev/null +++ b/kv/routers/general.py @@ -0,0 +1,27 @@ +from fastapi import APIRouter, Depends +from auth.login.schemas import login as models +from auth import oauth2 +from databases.mssql import general + +router = APIRouter(dependencies= [Depends(oauth2.get_current_user) ]) + +@router.get('/') +async def test_router(): + return {"msg":"router!"} + +@router.get('/getCities') +def get_cities(db = Depends(general.get_db)): + return db.execute("SELECT id,name FROM General.dbo.vCities").fetchall() + +@router.get('/getCities/region-id={region_id}') +def get_city(region_id: int,db = Depends(general.get_db)): + return db.execute(f"SELECT id, name FROM General.dbo.vCities WHERE id_region = {region_id}").fetchall() + + +@router.get('/getAddress/city-id={city_id}') +def get_address(city_id: int, db= Depends(general.get_db)): + return db.execute(f"EXEC General.dbo.getAddress @city_id = {city_id}").fetchall() + +@router.get('/getRegions') +def get_region(db = Depends(general.get_db)): + return db.execute("SELECT id, name FROM General.dbo.vRegions").fetchall() \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..2737cf5 --- /dev/null +++ b/main.py @@ -0,0 +1,32 @@ +from fastapi import FastAPI, Depends, HTTPException, status +from fastapi.middleware import Middleware +from fastapi.middleware.cors import CORSMiddleware +from kv import kv +from auth import auth +import uvicorn + +middleware = [Middleware( + CORSMiddleware, + allow_origins = ['*'], + allow_credentials = True, + allow_methods = ['*'], + allow_headers = ['*'], +)] + +app = FastAPI(middleware=middleware) + + +@app.get('/hello') +async def say_hello(): + return { "text": "Hello!" } + +app.include_router( + router = auth.router, + prefix= '/auth', + responses={404: {"description": "Not found"}} +) +app.include_router(router = kv.router, prefix = '/kv',tags=['Кварплата'], responses={404: {"description": "Not found"}}) + + +if __name__ == "__main__": + uvicorn.run("main:app", host = "0.0.0.0", port = 5000) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4c74f17 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +fastapi +sqlalchemy +python-multipart +pyodbc +python-jose +psycopg2 +bcrypt \ No newline at end of file