├── .github ├── FUNDING.yml ├── Secrets Sync Action.yml ├── workflows │ └── docker-image.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── header.svg ├── schemas ├── __init__.py └── schemas.py ├── models ├── __init__.py └── models.py ├── core ├── items │ ├── __init__.py │ ├── get_item.py │ ├── add_item.py │ └── delete_item.py ├── users │ ├── __init__.py │ ├── get_user.py │ ├── login.py │ └── register.py └── cart │ ├── __init__.py │ ├── delete_cart_item.py │ ├── add_to_cart.py │ └── payment.py ├── data ├── __init__.py ├── database.py └── alembic.ini ├── api ├── __init__.py └── crud.py ├── docker-compose.yaml ├── Dockerfile ├── templates └── index.html ├── requirements.txt ├── LICENSE ├── main.py ├── static └── style.css ├── .dockerignore ├── .gitignore └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: ["https://paypal.me/yassertahiri"] -------------------------------------------------------------------------------- /schemas/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from pydantic import BaseModel -------------------------------------------------------------------------------- /.github/Secrets Sync Action.yml: -------------------------------------------------------------------------------- 1 | - name: Secrets Sync Action 2 | uses: jpoehnelt/secrets-sync-action@v1.4.1 -------------------------------------------------------------------------------- /models/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from sqlalchemy import Column, Integer, String 3 | from data.database import Base -------------------------------------------------------------------------------- /core/items/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from sqlalchemy.orm import Session 3 | from fastapi import APIRouter, Depends, HTTPException 4 | from data import database -------------------------------------------------------------------------------- /core/users/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from sqlalchemy.orm import Session 3 | from fastapi import APIRouter, Depends, HTTPException 4 | from data import database -------------------------------------------------------------------------------- /data/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from sqlalchemy import create_engine 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy.orm import sessionmaker -------------------------------------------------------------------------------- /core/cart/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from sqlalchemy.orm import Session 3 | from fastapi import APIRouter, Depends, HTTPException 4 | from data import database 5 | from schemas import schemas 6 | from api import crud 7 | from data.database import get_db 8 | -------------------------------------------------------------------------------- /api/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from sqlalchemy.orm import Session 3 | from models import models 4 | from schemas import schemas 5 | import bcrypt 6 | import requests 7 | from requests.auth import HTTPBasicAuth 8 | import json 9 | from datetime import datetime 10 | import base64 -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3.8" 2 | 3 | services: 4 | web: 5 | build: ./DogeAPI 6 | command: uvicorn app.main:app --reload --workers 1 --host 0.0.0.0 --port 8000 7 | volumes: 8 | - ./DogeAPI:/usr/src/app 9 | ports: 10 | - 8004:8000 11 | environment: 12 | - ENVIRONMENT=dev 13 | - TESTING=0 -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Build the Docker image 18 | run: docker build . --file Dockerfile --tag my-image-name:$(date +%s) 19 | -------------------------------------------------------------------------------- /data/database.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from sqlalchemy import create_engine 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy.orm import sessionmaker 5 | 6 | SQLALCHEMY_DATABASE_URL = 'sqlite:///apollo.db' 7 | engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={ 8 | "check_same_thread": False}) 9 | 10 | SessionLocal = sessionmaker(bind=engine, autocommit=False, autoflush=False) 11 | Base = declarative_base() 12 | 13 | 14 | def get_db(): 15 | db = SessionLocal() 16 | try: 17 | yield db 18 | finally: 19 | db.close() 20 | -------------------------------------------------------------------------------- /core/users/get_user.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from sqlalchemy.orm import Session 3 | from fastapi import APIRouter, Depends 4 | from data import database 5 | 6 | # import locale files 7 | from schemas import schemas 8 | from api import crud 9 | from data.database import get_db 10 | 11 | router = APIRouter( 12 | tags=["Users"], 13 | prefix="/user" 14 | ) 15 | get_db = database.get_db 16 | 17 | # get user by username API 18 | 19 | 20 | @router.get("/get_user/{username}", response_model=schemas.UserInfo) 21 | def get_user(username, db: Session = Depends(get_db)): 22 | db_user = crud.get_user_by_username(db, username=username) 23 | return db_user 24 | -------------------------------------------------------------------------------- /data/alembic.ini: -------------------------------------------------------------------------------- 1 | # initial file for apollo.db 2 | 3 | [loggers] 4 | keys = root,sqlalchemy,alembic 5 | 6 | [handlers] 7 | keys = console 8 | 9 | [formatters] 10 | keys = generic 11 | 12 | [logger_root] 13 | level = WARN 14 | handlers = console 15 | qualname = 16 | 17 | [logger_sqlalchemy] 18 | level = WARN 19 | handlers = 20 | qualname = sqlalchemy.engine 21 | 22 | [logger_alembic] 23 | level = INFO 24 | handlers = 25 | qualname = alembic 26 | 27 | [handler_console] 28 | class = StreamHandler 29 | args = (sys.stderr,) 30 | level = NOTSET 31 | formatter = generic 32 | 33 | [formatter_generic] 34 | format = %(levelname)-5.5s [%(name)s] %(message)s 35 | datefmt = %H:%M:%S -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | FROM tiangolo/uvicorn-gunicorn-fastapi:python3.8 3 | RUN apt update && apt upgrade -y 4 | 5 | RUN apt install -y -q build-essential python3-pip python3-dev 6 | RUN pip3 install -U pip setuptools wheel 7 | RUN pip3 install gunicorn uvloop httptools 8 | 9 | COPY requirements.txt /app/requirements.txt 10 | RUN pip3 install -r /app/requirements.txt 11 | 12 | COPY ./ /app 13 | 14 | ENV ACCESS_LOG=${ACCESS_LOG:-/proc/1/fd/1} 15 | ENV ERROR_LOG=${ERROR_LOG:-/proc/1/fd/2} 16 | 17 | ENTRYPOINT /usr/local/bin/gunicorn \ 18 | -b 0.0.0.0:80 \ 19 | -w 4 \ 20 | -k uvicorn.workers.UvicornWorker main:app \ 21 | --chdir /app \ 22 | --access-logfile "$ACCESS_LOG" \ 23 | --error-logfile "$ERROR_LOG" -------------------------------------------------------------------------------- /core/items/get_item.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from sqlalchemy.orm import Session 3 | from fastapi import APIRouter, Depends, HTTPException 4 | from data import database 5 | 6 | # import locale files 7 | from schemas import schemas 8 | from api import crud 9 | from data.database import get_db 10 | 11 | router = APIRouter( 12 | tags=["Items"], 13 | prefix="/item" 14 | ) 15 | get_db = database.get_db 16 | 17 | # get item by id API 18 | 19 | 20 | @router.get("/get_item/{id}", response_model=schemas.ItemAInfo) 21 | def get_item(id, db: Session = Depends(get_db)): 22 | db_item = crud.get_item_by_id(db, id=id) 23 | if db_item is None: 24 | raise HTTPException(status_code=400, detail="No item found") 25 | return db_item 26 | -------------------------------------------------------------------------------- /core/items/add_item.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from sqlalchemy.orm import Session 3 | from fastapi import APIRouter, Depends, HTTPException 4 | from data import database 5 | 6 | # import locale files 7 | from schemas import schemas 8 | from api import crud 9 | from data.database import get_db 10 | 11 | router = APIRouter( 12 | tags=["Items"], 13 | prefix="/item" 14 | ) 15 | get_db = database.get_db 16 | 17 | 18 | # add items to DB API 19 | 20 | 21 | @router.post("/add_item", response_model=schemas.ItemInfo) 22 | def add_item(item: schemas.ItemInfo, db: Session = Depends(get_db)): 23 | db_item = crud.add_table(db=db, item=item) 24 | if db_item: 25 | raise HTTPException(status_code=200, detail="item registered") 26 | return {"Item": "Not Foundfound"} 27 | -------------------------------------------------------------------------------- /core/users/login.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from sqlalchemy.orm import Session 3 | from fastapi import APIRouter, Depends, HTTPException 4 | from data import database 5 | 6 | # import locale files 7 | from schemas import schemas 8 | from api import crud 9 | from data.database import get_db 10 | 11 | router = APIRouter( 12 | tags=["Users"], 13 | prefix="/user" 14 | ) 15 | get_db = database.get_db 16 | 17 | 18 | # login API 19 | 20 | 21 | @router.post("/login") 22 | def login_user(user: schemas.UserLogin, db: Session = Depends(get_db)): 23 | db_user = crud.get_Login( 24 | db, username=user.username, password=user.password) 25 | if db_user == False: 26 | raise HTTPException(status_code=400, detail="Wrong username or password") 27 | return {"message": "User found"} 28 | -------------------------------------------------------------------------------- /core/users/register.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from sqlalchemy.orm import Session 3 | from fastapi import APIRouter, Depends, HTTPException 4 | from data import database 5 | 6 | # import locale files 7 | from schemas import schemas 8 | from api import crud 9 | from data.database import get_db 10 | 11 | router = APIRouter( 12 | tags=["Users"], 13 | prefix="/user" 14 | ) 15 | get_db = database.get_db 16 | 17 | # register API 18 | 19 | 20 | @router.post("/register", response_model=schemas.UserInfo) 21 | def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)): 22 | db_user = crud.get_user_by_username(db, username=user.username) 23 | if db_user: 24 | raise HTTPException( 25 | status_code=400, detail="Username already registered") 26 | return crud.create_user(db=db, user=user) 27 | -------------------------------------------------------------------------------- /core/items/delete_item.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from sqlalchemy.orm import Session 3 | from fastapi import APIRouter, Depends, HTTPException 4 | from data import database 5 | 6 | # import locale files 7 | from schemas import schemas 8 | from api import crud 9 | from data.database import get_db 10 | 11 | router = APIRouter( 12 | tags=["Items"], 13 | prefix="/item" 14 | ) 15 | get_db = database.get_db 16 | 17 | # delete item by id API 18 | 19 | 20 | @router.delete("/del_item/{id}", response_model=schemas.ItemAInfo) 21 | def del_user(id, db: Session = Depends(get_db)): 22 | db_item = crud.delete_item_by_id(db, id=id) 23 | if db_item: 24 | raise HTTPException(status_code=200, detail="Item found to delete") 25 | else: 26 | raise HTTPException(status_code=400, detail="Item Not found to delete") 27 | return 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | 1. Go to '...' 17 | 2. Click on '....' 18 | 3. Scroll down to '....' 19 | 4. See error 20 | 21 | **Expected behavior** 22 | A clear and concise description of what you expected to happen. 23 | 24 | **Screenshots** 25 | If applicable, add screenshots to help explain your problem. 26 | 27 | **Desktop (please complete the following information):** 28 | 29 | - OS: [e.g. Ubuntu] 30 | - Browser [e.g. chrome, safari] 31 | - Version [e.g. 22] 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /core/cart/delete_cart_item.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from sqlalchemy.orm import Session 3 | from fastapi import APIRouter, Depends, HTTPException 4 | from data import database 5 | 6 | # import locale files 7 | from schemas import schemas 8 | from api import crud 9 | from data.database import get_db 10 | 11 | # Create the Payment Router 12 | router = APIRouter( 13 | tags=["Payment"], 14 | prefix="/cart" 15 | ) 16 | get_db = database.get_db 17 | 18 | # delete items in the cart by id API 19 | @router.delete("/delete_cart_item/{id}", response_model=schemas.CartItemAInfo) 20 | def del_user(id, db: Session = Depends(get_db)): 21 | db_item = crud.delete_cart_item_by_id(db, id=id) 22 | if db_item: 23 | raise HTTPException(status_code=200, detail="Item deleted") 24 | else: 25 | raise HTTPException(status_code=400, detail="Item Not found!") 26 | return 27 | -------------------------------------------------------------------------------- /core/cart/add_to_cart.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from sqlalchemy.orm import Session 3 | from fastapi import APIRouter, Depends, HTTPException 4 | from data import database 5 | 6 | # import locale files 7 | from schemas import schemas 8 | from api import crud 9 | from data.database import get_db 10 | 11 | # Create the Payment Router 12 | router = APIRouter( 13 | tags=["Payment"], 14 | prefix="/cart" 15 | ) 16 | get_db = database.get_db 17 | 18 | # add to cart by username and the items to be added API 19 | @router.post("/add_to_cart/{username}", response_model=schemas.CartOwnerInfo) 20 | def add_item(username, items: schemas.CartInfo, db: Session = Depends(get_db)): 21 | db_cart = crud.add_to_cart(db=db, username=username, items=items) 22 | if db_cart: 23 | raise HTTPException(status_code=200, detail="Registered to The Cart") 24 | return {"Cart": "Item Not Registered to the Cart!"} 25 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | Apollo-Auth 14 | 15 | 16 | 17 |
18 |
19 |

Apollo-Auth

20 |

Get Started

21 | FastAPI 24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /core/cart/payment.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from sqlalchemy.orm import Session 3 | from fastapi import APIRouter, Depends, HTTPException 4 | from data import database 5 | 6 | # import locale files 7 | from schemas import schemas 8 | from api import crud 9 | from data.database import get_db 10 | 11 | # Create the Payment Router 12 | router = APIRouter( 13 | tags=["Payment"], 14 | prefix="/cart" 15 | ) 16 | get_db = database.get_db 17 | 18 | # payment API 19 | @router.post("/payment") 20 | def add_item(userphone: schemas.UserPayment, db: Session = Depends(get_db)): 21 | user_payment = crud.payment( 22 | db=db, phone_number=userphone.phonenumber, total=userphone.total) 23 | if user_payment: 24 | raise HTTPException(status_code=200, detail="payment Started") 25 | return 26 | 27 | # Callback API 28 | @router.post("/callback") 29 | def money_callback(db: Session = Depends(get_db)): 30 | return {'success': "Payment was made successfully"} 31 | -------------------------------------------------------------------------------- /models/models.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from sqlalchemy import Column, Integer, String 3 | from data.database import Base 4 | 5 | # User Database Model 6 | 7 | 8 | class UserInfo(Base): 9 | __tablename__ = "user_info" 10 | 11 | id = Column(Integer, primary_key=True, index=True) 12 | username = Column(String, unique=True) 13 | password = Column(String) 14 | fullname = Column(String, unique=True) 15 | 16 | # Items Database Model 17 | 18 | 19 | class ItemInfo(Base): 20 | __tablename__ = "item_info" 21 | 22 | id = Column(Integer, primary_key=True, index=True) 23 | itemname = Column(String, unique=True) 24 | itemprice = Column(Integer) 25 | 26 | 27 | # Cart Database Model 28 | 29 | 30 | class CartInfo(Base): 31 | __tablename__ = "cart_info" 32 | 33 | id = Column(Integer, primary_key=True, index=True) 34 | ownername = Column(Integer, unique=True) 35 | itemname = Column(String, unique=True) 36 | itemprice = Column(Integer) 37 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiofiles==0.7.0 2 | asgiref==3.4.1 3 | astroid==2.6.2 4 | atomicwrites==1.4.0 5 | attrs==21.2.0 6 | autopep8==1.5.7 7 | backports.entry-points-selectable==1.1.0 8 | bcrypt==3.2.0 9 | certifi==2021.5.30 10 | cffi==1.14.6 11 | charset-normalizer==2.0.1 12 | click==8.0.1 13 | colorama==0.4.4 14 | distlib==0.3.2 15 | ecdsa==0.17.0 16 | fastapi==0.66.0 17 | filelock==3.0.12 18 | greenlet==1.1.0 19 | h11==0.12.0 20 | idna==3.2 21 | iniconfig==1.1.1 22 | isort==5.9.2 23 | Jinja2==3.0.1 24 | lazy-object-proxy==1.6.0 25 | MarkupSafe==2.0.1 26 | mccabe==0.6.1 27 | packaging==21.0 28 | passlib==1.7.4 29 | platformdirs==2.0.2 30 | pluggy==0.13.1 31 | py==1.10.0 32 | py-cpuinfo==8.0.0 33 | pyasn1==0.4.8 34 | pycodestyle==2.7.0 35 | pycparser==2.20 36 | pydantic==1.8.2 37 | pylint==2.9.3 38 | pyparsing==2.4.7 39 | pytest==6.2.4 40 | pytest-benchmark==3.4.1 41 | python-jose==3.3.0 42 | requests==2.26.0 43 | rsa==4.7.2 44 | six==1.16.0 45 | SQLAlchemy==1.4.20 46 | starlette==0.14.2 47 | toml==0.10.2 48 | tox==3.24.0 49 | typing-extensions==3.10.0.0 50 | urllib3==1.26.6 51 | uvicorn==0.14.0 52 | virtualenv==20.6.0 53 | wrapt==1.12.1 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Yasser Tahiri 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /schemas/schemas.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from pydantic import BaseModel 3 | 4 | # base schema for user data 5 | 6 | 7 | class UserInfoBase(BaseModel): 8 | username: str 9 | fullname: str 10 | 11 | # schema for user creation(registration) 12 | 13 | 14 | class UserCreate(UserInfoBase): 15 | password: str 16 | 17 | # inherits from user data schema 18 | 19 | 20 | class UserInfo(UserInfoBase): 21 | id: int 22 | 23 | class Config: 24 | orm_mode = True 25 | 26 | # base schema for user login 27 | 28 | 29 | class UserLogin(BaseModel): 30 | username: str 31 | password: str 32 | 33 | # base schema for items 34 | 35 | 36 | class ItemInfo(BaseModel): 37 | itemname: str 38 | itemprice: int 39 | 40 | # inherits from item data schema used for getting item by id 41 | 42 | 43 | class ItemAInfo(ItemInfo): 44 | id: int 45 | 46 | class Config: 47 | orm_mode = True 48 | 49 | # base schema for relating a cart to it's user 50 | 51 | 52 | class CartOwnerInfo(BaseModel): 53 | username: str 54 | 55 | # base schema for adding items to cart 56 | 57 | 58 | class CartInfo(BaseModel): 59 | itemname: str 60 | itemprice: int 61 | 62 | # base schema for getting items in the cart by id 63 | 64 | 65 | class CartItemAInfo(CartInfo): 66 | id: int 67 | 68 | class Config: 69 | orm_mode = True 70 | 71 | # base schema for the payment api 72 | 73 | 74 | class UserPayment(BaseModel): 75 | phonenumber: int 76 | total: int 77 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | from fastapi import FastAPI, Request 4 | from starlette.responses import HTMLResponse 5 | from fastapi.staticfiles import StaticFiles 6 | from fastapi.templating import Jinja2Templates 7 | 8 | # import locale files 9 | from models import models 10 | from data.database import engine 11 | 12 | # import router files 13 | from core.cart import add_to_cart, delete_cart_item, payment 14 | from core.items import add_item, delete_item, get_item 15 | from core.users import login, register, get_user 16 | 17 | # Create the database tables 18 | models.Base.metadata.create_all(bind=engine) 19 | 20 | # Create the instance 21 | app = FastAPI( 22 | title="Apollo - Auth", 23 | description="A basic Application with multiple functionality", 24 | version="1.0.0", 25 | ) 26 | 27 | app.mount("/static", StaticFiles(directory="static"), name="static") 28 | templates = Jinja2Templates(directory="templates") 29 | 30 | # includes all users routes 31 | app.include_router(login.router) 32 | app.include_router(register.router) 33 | app.include_router(get_user.router) 34 | 35 | # includes all items routes 36 | app.include_router(add_item.router) 37 | app.include_router(get_item.router) 38 | app.include_router(delete_item.router) 39 | 40 | # includes all cart 41 | app.include_router(add_to_cart.router) 42 | app.include_router(payment.router) 43 | app.include_router(delete_cart_item.router) 44 | 45 | # By default FastAPI return the response using JSONResponse, 46 | # but we will Custom our Response using the HTMLResponse 47 | @app.get("/", response_class=HTMLResponse) 48 | async def index(request: Request): 49 | return templates.TemplateResponse("index.html", {"request": request}) 50 | -------------------------------------------------------------------------------- /.github/header.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | APOLLO 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /static/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, 7 | body { 8 | box-sizing: border-box; 9 | height: 100%; 10 | width: 100%; 11 | } 12 | 13 | body { 14 | background: #FFF; 15 | font-family: 'fantasy', emoji; 16 | font-weight: 400; 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | flex-direction: row; 22 | flex-wrap: wrap; 23 | justify-content: center; 24 | text-align: center; 25 | width: 100%; 26 | height: 100%; 27 | margin: 0 auto; 28 | /* padding: 2em 0em; */ 29 | } 30 | 31 | .container { 32 | align-items: center; 33 | display: flex; 34 | flex-direction: column; 35 | justify-content: center; 36 | text-align: center; 37 | background-color: #FFF; 38 | padding: 40px 0px; 39 | width: 240px; 40 | } 41 | 42 | h1 { 43 | text-align: left; 44 | color: #444; 45 | letter-spacing: 0.05em; 46 | margin: 0 0 0.4em; 47 | font-size: 1em; 48 | } 49 | 50 | p { 51 | text-align: left; 52 | color: #444; 53 | letter-spacing: 0.05em; 54 | font-size: 0.8em; 55 | margin: 0 0 2em; 56 | } 57 | 58 | 59 | .btn { 60 | letter-spacing: 0.1em; 61 | cursor: pointer; 62 | font-size: 14px; 63 | font-weight: 400; 64 | line-height: 45px; 65 | max-width: 160px; 66 | position: relative; 67 | text-decoration: none; 68 | text-transform: uppercase; 69 | width: 100%; 70 | } 71 | 72 | .btn:hover { 73 | text-decoration: none; 74 | } 75 | 76 | /*btn_background*/ 77 | .effect04 { 78 | --uismLinkDisplay: var(--smLinkDisplay, inline-flex); 79 | display: var(--uismLinkDisplay); 80 | color: #000; 81 | outline: solid 2px #000; 82 | position: relative; 83 | transition-duration: 0.4s; 84 | overflow: hidden; 85 | } 86 | 87 | .effect04::before, 88 | .effect04 span { 89 | margin: 0 auto; 90 | transition-timing-function: cubic-bezier(0.86, 0, 0.07, 1); 91 | transition-duration: 0.4s; 92 | } 93 | 94 | .effect04:hover { 95 | 96 | background-color: #000; 97 | } 98 | 99 | .effect04:hover span { 100 | -webkit-transform: translateY(-400%) scale(-0.1, 20); 101 | transform: translateY(-400%) scale(-0.1, 20); 102 | } 103 | 104 | 105 | .effect04::before { 106 | content: attr(data-sm-link-text); 107 | color: #FFF; 108 | position: absolute; 109 | left: 0; 110 | right: 0; 111 | margin: auto; 112 | -webkit-transform: translateY(500%) scale(-0.1, 20); 113 | transform: translateY(500%) scale(-0.1, 20); 114 | } 115 | 116 | .effect04:hover::before { 117 | letter-spacing: 0.05em; 118 | -webkit-transform: translateY(0) scale(1, 1); 119 | transform: translateY(0) scale(1, 1); 120 | } -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | apollo.db 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | .pybuilder/ 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | # For a library or package, you might want to ignore these files since the code is 88 | # intended to run in multiple environments; otherwise, check them in: 89 | # .python-version 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | #Pipfile.lock 97 | 98 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 99 | __pypackages__/ 100 | 101 | # Celery stuff 102 | celerybeat-schedule 103 | celerybeat.pid 104 | 105 | # SageMath parsed files 106 | *.sage.py 107 | 108 | # Environments 109 | .env 110 | .venv 111 | env/ 112 | venv/ 113 | ENV/ 114 | env.bak/ 115 | venv.bak/ 116 | 117 | # Spyder project settings 118 | .spyderproject 119 | .spyproject 120 | 121 | # Rope project settings 122 | .ropeproject 123 | 124 | # mkdocs documentation 125 | /site 126 | 127 | # mypy 128 | .mypy_cache/ 129 | .dmypy.json 130 | dmypy.json 131 | 132 | # Pyre type checker 133 | .pyre/ 134 | 135 | # pytype static type analyzer 136 | .pytype/ 137 | 138 | # Cython debug symbols 139 | cython_debug/ 140 | 141 | .DS_Store 142 | .env -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | apollo.db 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | .pybuilder/ 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | # For a library or package, you might want to ignore these files since the code is 88 | # intended to run in multiple environments; otherwise, check them in: 89 | # .python-version 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | #Pipfile.lock 97 | 98 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 99 | __pypackages__/ 100 | 101 | # Celery stuff 102 | celerybeat-schedule 103 | celerybeat.pid 104 | 105 | # SageMath parsed files 106 | *.sage.py 107 | 108 | # Environments 109 | .env 110 | .venv 111 | env/ 112 | venv/ 113 | ENV/ 114 | env.bak/ 115 | venv.bak/ 116 | 117 | # Spyder project settings 118 | .spyderproject 119 | .spyproject 120 | 121 | # Rope project settings 122 | .ropeproject 123 | 124 | # mkdocs documentation 125 | /site 126 | 127 | # mypy 128 | .mypy_cache/ 129 | .dmypy.json 130 | dmypy.json 131 | 132 | # Pyre type checker 133 | .pyre/ 134 | 135 | # pytype static type analyzer 136 | .pytype/ 137 | 138 | # Cython debug symbols 139 | cython_debug/ 140 | 141 | .DS_Store 142 | .env 143 | .idea/ 144 | .vim/ 145 | token_examples.md 146 | -------------------------------------------------------------------------------- /api/crud.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from sqlalchemy.orm import Session 3 | from models import models 4 | from schemas import schemas 5 | import bcrypt 6 | import requests 7 | from requests.auth import HTTPBasicAuth 8 | import json 9 | from datetime import datetime 10 | import base64 11 | 12 | 13 | # Get user by username function 14 | 15 | 16 | def get_user_by_username(db: Session, username: str): 17 | return db.query(models.UserInfo).filter(models.UserInfo.username == username).first() 18 | 19 | # User registration function 20 | 21 | 22 | def create_user(db: Session, user: schemas.UserCreate): 23 | hashed_password = bcrypt.hashpw( 24 | user.password.encode('utf-8'), bcrypt.gensalt()) 25 | db_user = models.UserInfo(username=user.username, 26 | password=hashed_password, fullname=user.fullname) 27 | db.add(db_user) 28 | db.commit() 29 | db.refresh(db_user) 30 | return db_user 31 | 32 | # Login Function 33 | 34 | 35 | def get_Login(db: Session, username: str, password: str): 36 | db_user = db.query(models.UserInfo).filter( 37 | models.UserInfo.username == username).first() 38 | print(username, password) 39 | passw = bcrypt.checkpw(password.encode('utf-8'), db_user.password) 40 | return passw 41 | 42 | # Get item by id function 43 | 44 | 45 | def get_item_by_id(db: Session, id: int): 46 | return db.query(models.ItemInfo).filter(models.ItemInfo.id == id).first() 47 | 48 | # Add items to DB function 49 | 50 | 51 | def add_table(db: Session, item: schemas.ItemInfo): 52 | db_item = models.ItemInfo(itemname=item.itemname, itemprice=item.itemprice) 53 | db.add(db_item) 54 | db.commit() 55 | db.refresh(db_item) 56 | return db_item 57 | 58 | # Delete item from DB by id function 59 | 60 | 61 | def delete_item_by_id(db: Session, id: int): 62 | delitem = db.query(models.ItemInfo).filter( 63 | models.ItemInfo.id == id).first() 64 | if delitem is None: 65 | return 66 | db.delete(delitem) 67 | db.commit() 68 | return delitem 69 | 70 | # Add to cart function 71 | 72 | 73 | def add_to_cart(db: Session, username: str, items: models.CartInfo): 74 | user = db.query(models.UserInfo).filter( 75 | models.UserInfo.username == username).first() 76 | db_cart = models.CartInfo( 77 | ownername=user.id, itemname=items.itemname, itemprice=items.itemprice) 78 | db.add(db_cart) 79 | db.commit() 80 | db.refresh(db_cart) 81 | return db_cart 82 | 83 | # Delete item in the cart by id 84 | 85 | 86 | def delete_cart_item_by_id(db: Session, id: int): 87 | delitem = db.query(models.CartInfo).filter( 88 | models.CartInfo.id == id).first() 89 | if delitem is None: 90 | return 91 | db.delete(delitem) 92 | db.commit() 93 | return delitem 94 | 95 | # money processing function(Not Complete Yet) 96 | 97 | 98 | def payment(db: Session, phone_number: int, total: int): 99 | consumer_key = 'consumer_key' 100 | consumer_secret = 'consumer_secret' 101 | api_URL = 'https://api-m.sandbox.paypal.com/v1/payments' 102 | 103 | req = requests.get(api_URL, auth=HTTPBasicAuth( 104 | consumer_key, consumer_secret)) 105 | money_access_token = json.loads(req.text) 106 | validated_money_access_token = money_access_token['access_token'] 107 | 108 | time = datetime.now().strftime('%Y%m%d%H%M%S') 109 | Business_code = 'short_code' # replace with the business short code 110 | passkey = "pass_key" 111 | data_to_encode = Business_code + passkey + time 112 | online_password = base64.b64encode(data_to_encode.encode()) 113 | decode_password = online_password.decode('utf-8') 114 | 115 | access_token = validated_money_access_token 116 | api_url = "https://api-m.sandbox.paypal.com/v1/payments/payment?count=10&start_index=0&sort_by=create_time&sort_order=desc" 117 | headers = {"Authorization": "Bearer %s" % access_token} 118 | request = { 119 | "BusinessShortCode": Business_code, 120 | "Password": decode_password, 121 | "Timestamp": time, 122 | "TransactionType": "CustomerPayBillOnline", 123 | "Amount": total, 124 | "PhoneNumber": phone_number, 125 | "CallBackURL": "https://127.0.0.1:8000/callback", # Money Callback 126 | "AccountReference": "User Payment", 127 | "TransactionDesc": "Testing stk push" 128 | } 129 | response = requests.post(api_url, json=request, headers=headers) 130 | return response.text 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![apollo](.github/header.svg) 2 | 3 | # Apollo 4 | 5 | A basic Application with multiple functionalities built with FastAPI aim to help Users Buy New Items Provided by PaypalAPI to Complete the Payment and Check it. 6 | 7 | ## Getting Started 8 | 9 | Apollo provide a Basic API Compose : 10 | 11 |
    12 |
  1. Users
  2. 13 |
      14 |
    1. login : http://localhost:8000 user/login
    2. 15 |
    3. Register : http://localhost:8000/user/register
    4. 16 |
    5. Get User : http://localhost:8000/user/get_user/{username}
    6. 17 |
    18 |
  3. Items
  4. 19 |
      20 |
    1. Add Item : http://localhost:8000/item/add_item
    2. 21 |
    3. Get Item : http://localhost:8000/item/get_item/{id}
    4. 22 |
    5. Delete Item : http://localhost:8000/item/get_item/{id}
    6. 23 |
    24 |
  5. Payment
  6. 25 |
      26 |
    1. Add Item to Cart : http://localhost:8000/cart/add_to_cart/{username}
    2. 27 |
    3. Provide Item to Payments : http://localhost:8000/cart/payment
    4. 28 |
    5. Money Callback : http://localhost:8000/cart/callback
    6. 29 |
    7. Delete Cart Item : http://localhost:8000/cart/delete_cart_item/{id}
    8. 30 |
    31 |
32 | 33 | > I pre-configured the Cruds with the payment process based on `PaypalAPI`, you can read the Official docs here [REST APIs / API Requests](https://developer.paypal.com/docs/api/reference/api-requests/) 34 | 35 | ### Prerequisites 36 | 37 | - Python 3.9.2 or higher 38 | - FastAPI 39 | - Docker 40 | 41 | ### Project setup 42 | 43 | ```sh 44 | # clone the repo 45 | $ git clone https://github.com/yezz123/Apollo.git 46 | 47 | # move to the project folder 48 | $ cd Apollo 49 | ``` 50 | 51 | ### Creating virtual environment 52 | 53 | - Install `pipenv` a global python project `pip install pipenv` 54 | - Create a `virtual environment` for this project 55 | 56 | ```shell 57 | # creating pipenv environment for python 3 58 | $ pipenv --three 59 | 60 | # activating the pipenv environment 61 | $ pipenv shell 62 | 63 | # if you have multiple python 3 versions installed then 64 | $ pipenv install -d --python 3.8 65 | 66 | # install all dependencies (include -d for installing dev dependencies) 67 | $ pipenv install -d 68 | ``` 69 | 70 | ### Running the Application 71 | 72 | - To run the [Main](main.py) we need to use [uvicorn](https://www.uvicorn.org/) a lightning-fast ASGI server implementation, using uvloop and httptools. 73 | 74 | ```sh 75 | # Running the application using uvicorn 76 | $ uvicorn main:app 77 | 78 | ## To run the Application under a reload enviromment use -- reload 79 | $ uvicorn main:app --reload 80 | ``` 81 | 82 | - Here we can Switch Between using [SWAGGER UI](https://swagger.io/tools/swagger-ui/) or [Redoc](https://redocly.github.io/redoc/) to Play around the API. 83 | 84 | - You can Now Start using the Application, i use a simple Template for the `index` file to simply launch `/docs` : 85 | 86 | ### Configured Enviromment 87 | 88 | - To Provide a good work, i choose a `SQLite` Database using `SQLAlchemy`. 89 | - If you want to configure the Database with an other Provider like `MySQL` or `PostgreSQL` you can change the `Database_URL` here : 90 | 91 | - [Database.py](data/database.py) : 92 | 93 | ```py 94 | # here you need to insert the Connection URL. 95 | SQLALCHEMY_DATABASE_URL = 'sqlite:///apollo.db' 96 | ``` 97 | 98 | - For Example 99 | 100 | ```py 101 | SQLALCHEMY_DATABASE_URL = 'mysql://username:password@server/apollo' 102 | ``` 103 | 104 | ## Running the Docker Container 105 | 106 | - We have the Dockerfile created in above section. Now, we will use the Dockerfile to create the image of the FastAPI app and then start the FastAPI app container. 107 | 108 | ```sh 109 | $ docker build 110 | ``` 111 | 112 | - list all the docker images and you can also see the image `apollo:latest` in the list. 113 | 114 | ```sh 115 | $ docker images 116 | ``` 117 | 118 | - run the application at port 5000. The various options used are: 119 | 120 | > - `-p`: publish the container's port to the host port. 121 | > - `-d`: run the container in the background. 122 | > - `-i`: run the container in interactive mode. 123 | > - `-t`: to allocate pseudo-TTY. 124 | > - `--name`: name of the container 125 | 126 | ```sh 127 | $ docker container run -p 5000:5000 -dit --name Apollo apollo:latest 128 | ``` 129 | 130 | - Check the status of the docker container 131 | 132 | ```sh 133 | $ docker container ps 134 | ``` 135 | 136 | ## Preconfigured Packages 137 | 138 | Includes preconfigured packages to kick start Apollo API by just setting appropriate configuration. 139 | 140 | | Package | Usage | 141 | | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | 142 | | [uvicorn](https://www.uvicorn.org/) | a lightning-fast ASGI server implementation, using uvloop and httptools. | 143 | | [PaypalAPI](https://developer.paypal.com/docs/api/overview/) | exchange these credentials for an access token that authorizes your REST API calls. To test your web and mobile apps. | 144 | | [SQLAlchemy](https://www.sqlalchemy.org/) | is the Python SQL toolkit and Object Relational Mapper that gives application developers the full power and flexibility of SQL. | 145 | | [starlette](https://www.starlette.io/) | a lightweight ASGI framework/toolkit, which is ideal for building high performance asyncio services. | 146 | 147 | `yapf` packages for `linting and formatting` 148 | 149 | ## Contributing 150 | 151 | - Join the Apollo Creator and Contribute to the Project if you have any enhancement or add-ons to create a good and Secure Project, Help any User to Use it in a good and simple way. 152 | 153 | ## License 154 | 155 | This program is free software under MIT license. Please see the [LICENSE](LICENSE) file in our repository for the full text. 156 | --------------------------------------------------------------------------------