From f76265d9144efcfa5c64e05792fd812a2cdeaeb7 Mon Sep 17 00:00:00 2001 From: Yasser Tahiri Date: Sat, 4 May 2024 23:47:34 +0100 Subject: [PATCH 1/5] Remove unnecessary files and code --- .dockerignore | 130 ---------------------- .editorconfig | 7 -- .env.sample | 3 - .github/ISSUE_TEMPLATE/bug_report.md | 32 ------ .github/ISSUE_TEMPLATE/feature_request.md | 20 ---- .github/Secrets Sync Action.yml | 2 - .github/workflows/build.yml | 19 ---- .github/workflows/docker-publish.yml | 80 ------------- .pre-commit-config.yaml | 41 ------- Dockerfile | 15 --- Makefile | 22 ---- Procfile | 1 - api/blog.py | 113 ------------------- api/user.py | 62 ----------- core/auth.py | 53 --------- core/blog.py | 116 ------------------- core/user.py | 57 ---------- database/configuration.py | 30 ----- docker-compose.yaml | 18 --- docs/CODE_OF_CONDUCT.md | 128 --------------------- docs/CONTRIBUTING.md | 23 ---- docs/Images/cover.png | Bin 54662 -> 0 bytes docs/Images/header.svg | 63 ----------- main.py | 38 ------- models/models.py | 38 ------- requirements.txt | 26 ----- schema/hash.py | 33 ------ schema/oa2.py | 26 ----- schema/schemas.py | 53 --------- schema/token.py | 59 ---------- static/main.js | 46 -------- static/style.css | 120 -------------------- templates/index.html | 30 ----- 33 files changed, 1504 deletions(-) delete mode 100644 .dockerignore delete mode 100644 .editorconfig delete mode 100644 .env.sample delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md delete mode 100644 .github/Secrets Sync Action.yml delete mode 100644 .github/workflows/build.yml delete mode 100644 .github/workflows/docker-publish.yml delete mode 100644 .pre-commit-config.yaml delete mode 100644 Dockerfile delete mode 100644 Makefile delete mode 100644 Procfile delete mode 100644 api/blog.py delete mode 100644 api/user.py delete mode 100644 core/auth.py delete mode 100644 core/blog.py delete mode 100644 core/user.py delete mode 100644 database/configuration.py delete mode 100644 docker-compose.yaml delete mode 100644 docs/CODE_OF_CONDUCT.md delete mode 100644 docs/CONTRIBUTING.md delete mode 100644 docs/Images/cover.png delete mode 100644 docs/Images/header.svg delete mode 100644 main.py delete mode 100644 models/models.py delete mode 100644 requirements.txt delete mode 100644 schema/hash.py delete mode 100644 schema/oa2.py delete mode 100644 schema/schemas.py delete mode 100644 schema/token.py delete mode 100644 static/main.js delete mode 100644 static/style.css delete mode 100644 templates/index.html diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index f52fa55..0000000 --- a/.dockerignore +++ /dev/null @@ -1,130 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -pip-wheel-metadata/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal -blog.db - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -.python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 56e58fd..0000000 --- a/.editorconfig +++ /dev/null @@ -1,7 +0,0 @@ -[*] -indent_style = space -indent_size = 4 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = false -insert_final_newline = false \ No newline at end of file diff --git a/.env.sample b/.env.sample deleted file mode 100644 index 2a074b0..0000000 --- a/.env.sample +++ /dev/null @@ -1,3 +0,0 @@ -DATABASE_URL = '' -SECRET_KEY = -ACCESS_TOKEN_EXPIRE_MINUTES = \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 5a31af4..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Desktop (please complete the following information):** - - OS: [e.g. Ubuntu] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index bbcbbe7..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: '' -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/Secrets Sync Action.yml b/.github/Secrets Sync Action.yml deleted file mode 100644 index d039ef9..0000000 --- a/.github/Secrets Sync Action.yml +++ /dev/null @@ -1,2 +0,0 @@ -- name: Secrets Sync Action - uses: jpoehnelt/secrets-sync-action@v1.4.1 \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 9dc53e5..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Build and lint - -on: - push: - branches-ignore: - - master - -jobs: - build: - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - uses: actions/checkout@v1 - - name: pull latest - run: make pull - - name: Build image - run: make build - - name: Test code format -- pre-commit - run: make lint \ No newline at end of file diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml deleted file mode 100644 index 9bcfd22..0000000 --- a/.github/workflows/docker-publish.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: Docker Publish - -on: - push: - # Publish `main` as Docker `latest` image. - branches: - - main - - # Publish `v1.2.3` tags as releases. - tags: - - v* - - # Run tests for any PRs. - pull_request: - -env: - # TODO: Change variable to your image's name. - IMAGE_NAME: dogeapi - -jobs: - # Run tests. - # See also https://docs.docker.com/docker-hub/builds/automated-testing/ - test: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Run tests - run: | - if [ -f docker-compose.test.yml ]; then - docker-compose --file docker-compose.test.yml build - docker-compose --file docker-compose.test.yml run sut - else - docker build . --file Dockerfile - fi - - # Push image to GitHub Packages. - # See also https://docs.docker.com/docker-hub/builds/ - push: - # Ensure test job passes before pushing image. - needs: test - - runs-on: ubuntu-latest - if: github.event_name == 'push' - - permissions: - contents: read - packages: write - - steps: - - uses: actions/checkout@v2 - - - name: Build image - run: docker build . --file Dockerfile --tag $IMAGE_NAME - - - name: Log into registry - run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin - - - name: Push image - run: | - IMAGE_ID=docker.pkg.github.com/${{ github.repository }}/$IMAGE_NAME - - # Change all uppercase to lowercase - IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]') - - # Strip git ref prefix from version - VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') - - # Strip "v" prefix from tag name - [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//') - - # Use Docker `latest` tag convention - [ "$VERSION" == "main" ] && VERSION=latest - - echo IMAGE_ID=$IMAGE_ID - echo VERSION=$VERSION - - docker tag $IMAGE_NAME $IMAGE_ID:$VERSION - docker push $IMAGE_ID:$VERSION diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index a62b5da..0000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,41 +0,0 @@ -default_language_version: - python: python3.8 -repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.2.3 - hooks: - - id: check-merge-conflict - - id: check-added-large-files - - id: check-ast - - id: check-symlinks - - id: check-yaml - - id: trailing-whitespace - - id: check-json - - id: debug-statements - - id: pretty-format-json - args: ["--autofix", "--allow-missing-credentials"] - - repo: https://github.com/PyCQA/isort - rev: 5.6.4 - hooks: - - id: isort - args: ["--profile", "black"] - - repo: https://gitlab.com/pycqa/flake8 - rev: "8f9b4931b9a28896fb43edccb23016a7540f5b82" - hooks: - - id: flake8 - additional_dependencies: [flake8-print] - files: '\.py$' - args: - - --select=F401,F403,F406,F821,T001,T003 - - repo: https://github.com/humitos/mirrors-autoflake - rev: v1.3 - hooks: - - id: autoflake - files: '\.py$' - exclude: '^\..*' - args: ["--in-place", "--remove-all-unused-imports"] - - repo: https://github.com/psf/black - rev: 19.10b0 - hooks: - - id: black - args: ["--target-version", "py38"] diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 85b54a8..0000000 --- a/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM tiangolo/uvicorn-gunicorn-fastapi:python3.8 - -RUN apt update && apt upgrade -y - -RUN apt install -y -q build-essential python3-pip python3-dev -RUN pip3 install -U pip setuptools wheel -RUN pip3 install gunicorn uvloop httptools - -COPY requirements.txt /app/requirements.txt -RUN pip3 install -r /app/requirements.txt - -COPY ./ /app - -ENV ACCESS_LOG=${ACCESS_LOG:-/proc/1/fd/1} -ENV ERROR_LOG=${ERROR_LOG:-/proc/1/fd/2} \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index 5d7006e..0000000 --- a/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -help: - @echo "Targets:" - @echo " make start" - @echo " make down" - @echo " make pull" - @echo " make build" - @echo " make lint" - -start: - docker-compose up -d - -down: - docker-compose down - -pull: - docker-compose pull - -build: - docker-compose build - -lint: - docker-compose run --rm backend pre-commit run --all-files \ No newline at end of file diff --git a/Procfile b/Procfile deleted file mode 100644 index 81a08ac..0000000 --- a/Procfile +++ /dev/null @@ -1 +0,0 @@ -web: uvicorn main:app --host=0.0.0.0 --port=${PORT:-5000} \ No newline at end of file diff --git a/api/blog.py b/api/blog.py deleted file mode 100644 index 1c331d9..0000000 --- a/api/blog.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/python3 - -from fastapi import HTTPException, status -from sqlalchemy.orm import Session - -from models import models -from schema import schemas - - -def get_all(db: Session): - """ - Get all blogs - - Args: - db (Session): Database session - - Returns: - List[models.Blog]: List of blogs - """ - return db.query(models.Blog).all() - - -def create(request: schemas.Blog, db: Session): - """ - Create a new blog - - Args: - request (schemas.Blog): Blog object - db (Session): Database session - - Returns: - models.Blog: Blog object - """ - new_blog = models.Blog(title=request.title, body=request.body, user_id=1) - db.add(new_blog) - db.commit() - db.refresh(new_blog) - return new_blog - - -def destroy(id: int, db: Session): - """ - Delete a blog - - Args: - id (int): Blog id - db (Session): Database session - - Raises: - HTTPException: 404 not found - - Returns: - str: Success message - """ - blog_to_delete = db.query(models.Blog).filter(models.Blog.id == id) - - if not blog_to_delete.first(): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=f"Blog with id {id} not found.", - ) - blog_to_delete.delete(synchronize_session=False) - db.commit() - return {"done"} - - -def update(id: int, request: schemas.Blog, db: Session): - """ - Update a blog - - Args: - id (int): Blog id - request (schemas.Blog): Blog object - db (Session): Database session - - Raises: - HTTPException: 404 not found - - Returns: - models.Blog: Blog object - """ - blog = db.query(models.Blog).filter(models.Blog.id == id) - if not blog.first(): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail=f"Blog with id {id} not found" - ) - blog.update(request.__dict__) - db.commit() - return "updated" - - -def show(id: int, db: Session): - """ - Get a blog - - Args: - id (int): Blog id - db (Session): Database session - - Raises: - HTTPException: 404 not found - - Returns: - models.Blog: Blog object - """ - blog = db.query(models.Blog).filter(models.Blog.id == id).first() - if blog: - return blog - else: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=f"Blog with the id {id} is not available", - ) diff --git a/api/user.py b/api/user.py deleted file mode 100644 index b472ed3..0000000 --- a/api/user.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/python3 - -from fastapi import HTTPException, status -from sqlalchemy.orm import Session - -from models import models -from schema import schemas -from schema.hash import Hash - - -def create(request: schemas.User, db: Session): - """ - Create a new user - - Args: - request (schemas.User): User data - db (Session): Database session - - Returns: - models.User: User created - """ - hashedPassword = Hash.bcrypt(request.password) - user = models.User(name=request.name, email=request.email, password=hashedPassword) - db.add(user) - db.commit() - db.refresh(user) - return user - - -def show(id: int, db: Session): - """ - Show a user - - Args: - id (int): User id - db (Session): Database session - - Raises: - HTTPException: User not found - - Returns: - models.User: User found - """ - user = db.query(models.User).filter(models.User.id == id).first() - if not user: - raise HTTPException( - status.HTTP_404_NOT_FOUND, detail=f"User with id {id} not found" - ) - return user - - -def get_all(db: Session): - """ - Get all users - - Args: - db (Session): Database session - - Returns: - list: List of users - """ - return db.query(models.User).all() diff --git a/core/auth.py b/core/auth.py deleted file mode 100644 index 9892da8..0000000 --- a/core/auth.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/python3 - - -from fastapi import APIRouter, Depends, HTTPException, status -from fastapi.security.oauth2 import OAuth2PasswordRequestForm -from sqlalchemy.orm import Session - -from database import configuration -from models import models -from schema import schemas -from schema.hash import Hash -from schema.token import create_access_token - -router = APIRouter(prefix="/login", tags=["Authentication"],) - - -@router.post("/") -def login( - request: OAuth2PasswordRequestForm = Depends(), - db: Session = Depends(configuration.get_db), -): - """ - Login user - - Args: - request (OAuth2PasswordRequestForm, optional): OAuth2PasswordRequestForm. - db (Session, optional): Session. Defaults to Depends(configuration.get_db). - - Raises: - HTTPException: 401 Unauthorized - HTTPException: 404 Not Found - - Returns: - Hash: Hash - """ - user: schemas.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="Invalid Credentials" - ) - - if not Hash.verify(user.password, request.password): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Incorrect password" - ) - - # access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) - access_token = create_access_token(data={"sub": user.email}) - - # generate JWT token and return - return {"access_token": access_token, "token_type": "bearer"} diff --git a/core/blog.py b/core/blog.py deleted file mode 100644 index f8fb1c8..0000000 --- a/core/blog.py +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/python3 - -from typing import List - -from fastapi import APIRouter, Depends, Response, status -from sqlalchemy.orm import Session - -from api import blog -from database import configuration -from schema import schemas -from schema.oa2 import get_current_user - -router = APIRouter(tags=["Blogs"], prefix="/blog") -get_db = configuration.get_db - - -@router.get("/", response_model=List[schemas.ShowBlog]) -def get_all_blogs( - db: Session = Depends(get_db), - current_user: schemas.User = Depends(get_current_user), -): - """ - Get all blogs - - Args: - db (Session, optional): Database session. Defaults to None. - current_user (schemas.User, optional): Current user. Defaults to None. - - Returns: - List[schemas.ShowBlog]: List of blogs - """ - return blog.get_all(db) - - -@router.post("/", status_code=status.HTTP_201_CREATED) -def create( - request: schemas.Blog, - db: Session = Depends(get_db), - current_user: schemas.User = Depends(get_current_user), -): - """ - Create a blog - - Args: - request (schemas.Blog): Blog to create - db (Session, optional): Database session. Defaults to None. - current_user (schemas.User, optional): Current user. Defaults to None. - - Returns: - schemas.Blog: Created blog - """ - return blog.create(request, db) - - -@router.get("/{id}", status_code=status.HTTP_200_OK, response_model=schemas.ShowBlog) -def get_blog_by_id( - id: int, - response: Response, - db: Session = Depends(get_db), - current_user: schemas.User = Depends(get_current_user), -): - """ - Get a blog by id - - Args: - id (int): Blog id - response (Response): FastAPI response - db (Session, optional): Database session. Defaults to None. - current_user (schemas.User, optional): Current user. Defaults to None. - - Returns: - schemas.ShowBlog: Blog - """ - return blog.show(id, db) - - -@router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT) -def delete_blog( - id: int, - db: Session = Depends(get_db), - current_user: schemas.User = Depends(get_current_user), -): - """ - Delete a blog by id - - Args: - id (int): Blog id - db (Session, optional): Database session. Defaults to None. - current_user (schemas.User, optional): Current user. Defaults to None. - - Returns: - None: None - """ - return blog.destroy(id, db) - - -@router.put("/{id}", status_code=status.HTTP_202_ACCEPTED) -def update_blog( - id: int, - request: schemas.Blog, - db: Session = Depends(get_db), - current_user: schemas.User = Depends(get_current_user), -): - """ - Update a blog by id - - Args: - id (int): Blog id - request (schemas.Blog): Blog to update - db (Session, optional): Database session. Defaults to Depends(get_db). - current_user (schemas.User, optional): Current user. Defaults to Depends(get_current_user). - - Returns: - schemas.Blog: Updated blog - """ - return blog.update(id, request, db) diff --git a/core/user.py b/core/user.py deleted file mode 100644 index 3dbca90..0000000 --- a/core/user.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/python3 - -from typing import List - -from fastapi import APIRouter, Depends, status -from sqlalchemy.orm import Session - -from api import user -from database import configuration -from schema import schemas - -router = APIRouter(tags=["Users"], prefix="/users") -get_db = configuration.get_db - - -@router.post("/", status_code=status.HTTP_201_CREATED, response_model=schemas.ShowUser) -def create_user(request: schemas.User, db: Session = Depends(get_db)): - """ - Create a new user - - Args: - request (schemas.User): User to create - db (Session, optional): Database session. - - Returns: - schemas.ShowUser: User created - """ - return user.create(request, db) - - -@router.get("/", status_code=status.HTTP_200_OK, response_model=List[schemas.ShowUser]) -def get_users(db: Session = Depends(get_db)): - """ - Get all users - - Args: - db (Session, optional): Database session. Defaults to Depends(get_db). - - Returns: - List[schemas.ShowUser]: List of users - """ - return user.get_all(db) - - -@router.get("/{id}", status_code=status.HTTP_200_OK, response_model=schemas.ShowUser) -def get_user_by_id(id: int, db: Session = Depends(get_db)): - """ - Get a user by id - - Args: - id (int): User id - db (Session, optional): Database session. Defaults to Depends(get_db). - - Returns: - schemas.ShowUser: User - """ - return user.show(id, db) diff --git a/database/configuration.py b/database/configuration.py deleted file mode 100644 index 1d968d3..0000000 --- a/database/configuration.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/python3 - -from decouple import config -from sqlalchemy import create_engine -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import sessionmaker - -SQLALCHEMY_DATABASE_URL = config( - "DATABASE_URL", default="sqlite:///./database/dogeapi.sqlite" -) -engine = create_engine( - SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} -) - -SessionLocal = sessionmaker(bind=engine, autocommit=False, autoflush=False) -Base = declarative_base() - - -def get_db(): - """ - Get the database session - - Yields: - Session: The database session - """ - db = SessionLocal() - try: - yield db - finally: - db.close() diff --git a/docker-compose.yaml b/docker-compose.yaml deleted file mode 100644 index 82c8158..0000000 --- a/docker-compose.yaml +++ /dev/null @@ -1,18 +0,0 @@ -version: "3.8" - -services: - backend: - build: - context: . - dockerfile: Dockerfile - command: uvicorn main:app --reload --workers 1 --host 0.0.0.0 --port 8000 - volumes: - - ./DogeAPI:/usr/src/app - ports: - - 8000:8000 - environment: - - ENVIRONMENT=dev - - TESTING=0 - - DATABASE_URL=sqlite:///test.db - - SECRET_KEY=dev - - ACCESS_TOKEN_EXPIRE_MINUTES=30 \ No newline at end of file diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md deleted file mode 100644 index cd9a9b0..0000000 --- a/docs/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,128 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the - overall community - -Examples of unacceptable behavior include: - -* The use of sexualized language or imagery, and sexual attention or - advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email - address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -yasserth19@protonmail.com. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.0, available at -. - -Community Impact Guidelines were inspired by [Mozilla's code of conduct -enforcement ladder](https://github.com/mozilla/diversity). - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see the FAQ at -. Translations are available at -. diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md deleted file mode 100644 index 968991d..0000000 --- a/docs/CONTRIBUTING.md +++ /dev/null @@ -1,23 +0,0 @@ -# Contributing to DogeAPI - -## Open Source License - -DogeAPI is different than most projects on GitHub. It is licensed using the "MIT license" MIT. However, the coding and development of the project is an "open source". - -This project does accept user submitted code. - -### Write Applications, Use DogeAPI, Write Tutorials, Teach Others - -These are a few of the ways you can directly contribute to DogeAPI. Using the package to make cool stuff and helping others learn how to use it to make cool stuff and a big help to DogeAPI. - -#### Pull Requests - -Pull requests are _accepted_ for the project. This includes sending code changes via other means than "pull requests". Plainly put, core code you send will not be used. - -#### Bug Fixes - -If you file an Issue for a bug, have located the bug, and found a fix in 10 lines of code or less.... and you wish to share your fix with the community, then feel free to include it with the filed Issue. If it's longer than 10 lines and wish to discuss it, then send an email to yasserth19@protonmail.com. - -## Thank You - -The support from the user community has been amazing. Your passion for creating DogeAPI web applications is infectious. Every "thank you" is noticed and appreciated! Your passion for wanting to see DogeAPI improve is neither ignored nor unappreciated. diff --git a/docs/Images/cover.png b/docs/Images/cover.png deleted file mode 100644 index 2d18f18c0c87576c62889d7b22cfad0776ea17ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54662 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 diff --git a/docs/Images/header.svg b/docs/Images/header.svg deleted file mode 100644 index 5efba3a..0000000 --- a/docs/Images/header.svg +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - -DOGEAPI - - - - - - - - - diff --git a/main.py b/main.py deleted file mode 100644 index 8239962..0000000 --- a/main.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/python3 - -from fastapi import FastAPI, Request -from fastapi.staticfiles import StaticFiles -from fastapi.templating import Jinja2Templates -from starlette.responses import HTMLResponse - -from core import auth, blog, user -from database.configuration import engine -from models import models - -models.Base.metadata.create_all(bind=engine) - -app = FastAPI( - title="DogeAPI", - description="API with high performance built with FastAPI & SQLAlchemy, help to improve connection with your Backend Side.", - version="1.0.0", -) -app.mount("/static", StaticFiles(directory="static"), name="static") -templates = Jinja2Templates(directory="templates") - -app.include_router(auth.router) -app.include_router(blog.router) -app.include_router(user.router) - - -@app.get("/", response_class=HTMLResponse) -async def index(request: Request): - """ - Home page - - Args: - request (Request): Request object - - Returns: - HTMLResponse: HTML response - """ - return templates.TemplateResponse("index.html", {"request": request}) diff --git a/models/models.py b/models/models.py deleted file mode 100644 index 5a19f0e..0000000 --- a/models/models.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/python3 - -from sqlalchemy import Column, ForeignKey, Integer, String -from sqlalchemy.orm import relationship - -from database.configuration import Base - - -class Blog(Base): - """ - Blog class - - Args: - Base (sqlalchemy.ext.declarative.api.Base): Base class - """ - - __tablename__ = "blogs" - id = Column(Integer, primary_key=True, index=True) - title = Column(String) - body = Column(String) - user_id = Column(Integer, ForeignKey("users.id")) - creator = relationship("User", back_populates="blogs") - - -class User(Base): - """ - User class - - Args: - Base (sqlalchemy.ext.declarative.api.Base): Base class - """ - - __tablename__ = "users" - id = Column(Integer, primary_key=True, index=True) - name = Column(String) - email = Column(String) - password = Column(String) - blogs = relationship("Blog", back_populates="creator") diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index d970a80..0000000 --- a/requirements.txt +++ /dev/null @@ -1,26 +0,0 @@ -aiofiles -asgiref -bcrypt -cffi -click -colorama -ecdsa -fastapi -greenlet -h11 -Jinja2 -MarkupSafe -passlib -pyasn1 -pycparser -pydantic -python-decouple -python-jose -python-multipart -rsa -six -SQLAlchemy -starlette -typing-extensions -uvicorn -pre-commit \ No newline at end of file diff --git a/schema/hash.py b/schema/hash.py deleted file mode 100644 index f7598af..0000000 --- a/schema/hash.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/python3 - -from passlib.context import CryptContext - -pwd_ctx = CryptContext(schemes=["bcrypt"], deprecated="auto") - - -class Hash: - @staticmethod - def bcrypt(password: str): - """ - Generate a bcrypt hashed password - - Args: - password (str): The password to hash - - Returns: - str: The hashed password - """ - return pwd_ctx.hash(password) - - def verify(hashed_password, plain_password): - """ - Verify a password against a hash - - Args: - hashed_password (bool): The hashed password - plain_password ([type]): The plain password - - Returns: - bool: True if the password matches, False otherwise - """ - return pwd_ctx.verify(plain_password, hashed_password) diff --git a/schema/oa2.py b/schema/oa2.py deleted file mode 100644 index c1fd66d..0000000 --- a/schema/oa2.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/python3 - -from fastapi import Depends, HTTPException, status -from fastapi.security import OAuth2PasswordBearer - -from schema.token import verify_token - -oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login") - - -def get_current_user(token: str = Depends(oauth2_scheme)): - """ - Get the current user from the token. - - Args: - token (str, optional): The token to verify. - - Returns: - dict: The user. - """ - credentials_exception = HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Could not validate credentials", - headers={"WWW-Authenticate": "bearer"}, - ) - return verify_token(token, credentials_exception=credentials_exception) diff --git a/schema/schemas.py b/schema/schemas.py deleted file mode 100644 index a263738..0000000 --- a/schema/schemas.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/python3 - -from typing import List, Optional - -from pydantic import BaseModel - - -class BlogBase(BaseModel): - title: str - body: str - - -class Blog(BlogBase): - class Config: - orm_mode = True - - -class User(BaseModel): - name: str - email: str - password: str - - -class ShowUser(BaseModel): - name: str - email: str - blogs: List[Blog] = [] - - class Config: - orm_mode = True - - -class ShowBlog(BaseModel): - title: str - body: str - creator: ShowUser - - class Config: - orm_mode = True - - -class Login(BaseModel): - username: str - password: str - - -class Token(BaseModel): - access_token: str - token_type: str - - -class TokenData(BaseModel): - username: Optional[str] = None diff --git a/schema/token.py b/schema/token.py deleted file mode 100644 index c244dcf..0000000 --- a/schema/token.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/python3 - -from datetime import datetime, timedelta -from typing import Optional - -from decouple import config -from jose import JWTError, jwt - -from schema.schemas import TokenData - -# openssl rand -hex 32 -SECRET_KEY = config("SECRET_KEY", default="secret") -ALGORITHM = "HS256" -ACCESS_TOKEN_EXPIRE_MINUTES = config( - "ACCESS_TOKEN_EXPIRE_MINUTES", default=60, cast=int -) - - -def create_access_token(data: dict, expires_delta: Optional[timedelta] = None): - """ - Generate JWT token - - Args: - data (dict): payload - expires_delta (Optional[timedelta]): token expiration time - - Returns: - str: JWT token - """ - to_encode = data.copy() - if expires_delta: - expire = datetime.utcnow() + expires_delta - else: - expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) - - to_encode["exp"] = expire - return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) - - -def verify_token(token: str, credentials_exception): - """ - Verify JWT token - - Args: - token (str): JWT token - credentials_exception (Exception): exception to raise if token is invalid - - Raises: - credentials_exception: if token is invalid - credentials_exception: if token is expired - """ - try: - payload = jwt.decode(token, SECRET_KEY, algorithms=ALGORITHM) - email: str = payload.get("sub") - if email is None: - raise credentials_exception - token_data = TokenData(email=email) - except JWTError: - raise credentials_exception diff --git a/static/main.js b/static/main.js deleted file mode 100644 index 9e9d9a2..0000000 --- a/static/main.js +++ /dev/null @@ -1,46 +0,0 @@ -const faceButton = document.querySelector(".face-button"); -const faceContainer = document.querySelector(".face-container"); -const containerCoords = document.querySelector("#container"); -const mouseCoords = containerCoords.getBoundingClientRect(); - -faceButton.addEventListener("mousemove", function (e) { - const mouseX = e.pageX - containerCoords.offsetLeft; - const mouseY = e.pageY - containerCoords.offsetTop; - - TweenMax.to(faceButton, 0.3, { - x: ((mouseX - mouseCoords.width / 2) / mouseCoords.width) * 50, - y: ((mouseY - mouseCoords.height / 2) / mouseCoords.width) * 50, - ease: Power4.easeOut, - }); -}); - -faceButton.addEventListener("mousemove", function (e) { - const mouseX = e.pageX - containerCoords.offsetLeft; - const mouseY = e.pageY - containerCoords.offsetTop; - - TweenMax.to(faceContainer, 0.3, { - x: ((mouseX - mouseCoords.width / 2) / mouseCoords.width) * 25, - y: ((mouseY - mouseCoords.height / 2) / mouseCoords.width) * 25, - ease: Power4.easeOut, - }); -}); - -faceButton.addEventListener("mouseenter", function (e) { - TweenMax.to(faceButton, 0.3, { - scale: 0.975, - }); -}); - -faceButton.addEventListener("mouseleave", function (e) { - TweenMax.to(faceButton, 0.3, { - x: 0, - y: 0, - scale: 1, - }); - - TweenMax.to(faceContainer, 0.3, { - x: 0, - y: 0, - scale: 1, - }); -}); diff --git a/static/style.css b/static/style.css deleted file mode 100644 index 62cfd61..0000000 --- a/static/style.css +++ /dev/null @@ -1,120 +0,0 @@ -* { - box-sizing: border-box; -} -*::before, -*::after { - box-sizing: border-box; -} - -body { - display: flex; - align-items: center; - justify-content: center; - margin: 0; - min-height: 100vh; - background: #000000; -} - -#container { - display: flex; - align-items: center; - justify-content: center; - width: 6.25rem; - height: 6.25rem; -} - -button { - position: relative; - display: inline-block; - cursor: pointer; - outline: none; - border: 0; - vertical-align: middle; -} -button.face-button { - width: 6.25rem; - height: 6.25rem; - border-radius: 50%; - background: #fdda5f; - box-shadow: inset 2px -4px 18px #fd9744; -} - -.face-container { - position: relative; - display: block; - width: 40px; - height: 20px; - margin: auto; -} - -.eye { - position: absolute; - height: 0.5rem; - width: 0.5rem; - background: #2a2927; - border-radius: 50%; - -webkit-animation: eyeBlink 3200ms linear infinite; - animation: eyeBlink 3200ms linear infinite; -} -.eye.left { - left: 0; -} -.eye.right { - left: 2rem; -} - -.mouth { - position: absolute; - top: 1.125rem; - left: 0.8rem; - width: 1rem; - height: 0.125rem; - background: #2a2927; - border-radius: 0; -} - -.eye, -.mouth { - box-shadow: inset 1px 2px 4px #121110; -} - -.face-button:hover .mouth, -.face-button:active .mouth { - left: 1rem; - width: 0.5rem; - height: 0.4rem; - border-radius: 1rem 1rem 0.125rem 0.125rem; -} - -.face-button:hover .eye, -.face-button:active .eye { - height: 0.375rem; - width: 0.375rem; - box-shadow: 0 0 0 0.25rem #fff; -} - -@-webkit-keyframes eyeBlink { - 0%, - 30%, - 36%, - 100% { - transform: scale(1); - } - 32%, - 34% { - transform: scale(1, 0); - } -} - -@keyframes eyeBlink { - 0%, - 30%, - 36%, - 100% { - transform: scale(1); - } - 32%, - 34% { - transform: scale(1, 0); - } -} diff --git a/templates/index.html b/templates/index.html deleted file mode 100644 index a261c36..0000000 --- a/templates/index.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - DogeAPI - - - - - - \ No newline at end of file From 1f55eeaa46c1605332c9bb7d037fbb891811ff55 Mon Sep 17 00:00:00 2001 From: Yasser Tahiri Date: Sat, 4 May 2024 23:48:10 +0100 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=94=A7Update=20funding=20platforms?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/FUNDING.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 7d9489b..5e24051 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1,3 @@ -custom: ["https://paypal.me/yassertahiri"] \ No newline at end of file +# These are supported funding model platforms +github: yezz123 +polar: yezz123 \ No newline at end of file From 2e8aaef73a3db16f8d15d09451db0e36637db52f Mon Sep 17 00:00:00 2001 From: Yasser Tahiri Date: Sat, 4 May 2024 23:48:22 +0100 Subject: [PATCH 3/5] =?UTF-8?q?=F0=9F=91=B7=20Update=20Dependabot=20config?= =?UTF-8?q?uration=20for=20GitHub=20Actions=20and=20Python=20packages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/dependabot.yml | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index cf7a39f..d54a610 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,11 +1,21 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - version: 2 + updates: - - package-ecosystem: "pip" # See documentation for possible values - directory: "/" # Location of package manifests + # GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: ⬆ + # Python + - package-ecosystem: "pip" + directory: "/" schedule: - interval: "daily" + interval: "weekly" + groups: + python-packages: + patterns: + - "*" + commit-message: + prefix: ⬆ \ No newline at end of file From a10abca8cd1ad661b8fe77963fbedb884b449c80 Mon Sep 17 00:00:00 2001 From: Yasser Tahiri Date: Sat, 4 May 2024 23:48:30 +0100 Subject: [PATCH 4/5] :memo: Update README.md --- README.md | 115 +----------------------------------------------------- 1 file changed, 2 insertions(+), 113 deletions(-) diff --git a/README.md b/README.md index 6659a7a..8a68d99 100644 --- a/README.md +++ b/README.md @@ -1,114 +1,3 @@ -![DOGEAPI](docs/Images/header.svg) +# DEV -

- - - - - - Star Badge - - -

- -# DogeAPI - -API with high performance built with FastAPI & SQLAlchemy, help to improve connection with your Backend Side to create a simple blog and Cruds with OAuth2PasswordBearer ⛏ - -## Getting Started - -### Prerequisites - -- Python 3.8.6 or higher -- FastAPI -- Docker - -### Project Setup - -```sh -# clone the repo -$ git clone https://github.com/yezz123/DogeAPI - -# move to the project folder -$ cd DogeAPI -``` - -### Creating Virtual Environment - -- Create a virtual environment using virtualenv. - -```shell -# creating virtual environment -$ virtualenv venv - -# activate virtual environment -$ source venv/bin/activate - -# install all dependencies -$ pip install -r requirements.txt -``` - -### Running the Application - -- 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. - -```sh -# Running the application using uvicorn -$ uvicorn main:app --reload -``` - -### Environment Variables - -- `SECRET_KEY`: A secret key for signing Json Web Token. -- `DATABASE_URL`: The database url to connect to the database. -- `ACCESS_TOKEN_EXPIRE_MINUTES`: The access token expire minutes. - -> change all the environment variables in the `.env.sample` and don't forget to rename it to `.env`. - -### Configured Environment - -#### Models - -- Here for the [Models.py](models/models.py), i create 2 tables based on the requirements for this project `blogs` and `users` - -## Running the Docker Container - -- 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. -- Using a preconfigured `Makefile` tor run the Docker Compose: - -```sh -# Pull the latest image -$ make pull - -# Build the image -$ make build - -# Run the container -$ make start - -``` - -## Preconfigured Packages - -Includes preconfigured packages to kick start DogeAPI by just setting appropriate configuration. - -| Package | Usage | -| ------------------------------------------------------------ | ---------------------------------------------------------------- | -| [uvicorn](https://www.uvicorn.org/) | a lightning-fast ASGI server implementation, using uvloop and httptools. | -| [Python-Jose](https://github.com/mpdavis/python-jose) | a JavaScript Object Signing and Encryption implementation in Python. | -| [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. | -| [starlette](https://www.starlette.io/) | a lightweight ASGI framework/toolkit, which is ideal for building high performance asyncio services. | -| [passlib](https://passlib.readthedocs.io/en/stable/) | a password hashing library for Python 2 & 3, which provides cross-platform implementations of over 30 password hashing algorithms | -| [bcrypt](https://github.com/pyca/bcrypt/) | Good password hashing for your software and your servers. | -| [python-multipart](https://github.com/andrew-d/python-multipart) | streaming multipart parser for Python. | - -`yapf` packages for `linting and formatting` - -## Contributing - -- Join the DOGEAPI 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. -- Check all information here at [docs's Folder](docs) to understand to how to contribute or to Read the Code of Conduct. - -## License - -This project is licensed under the terms of the MIT license. +WIP \ No newline at end of file From 6a25f68a2eef8b71808957ecff0babcacae1c6a2 Mon Sep 17 00:00:00 2001 From: Yasser Tahiri Date: Sun, 5 May 2024 16:35:39 +0100 Subject: [PATCH 5/5] Update dependencies and remove unnecessary files and code --- .pre-commit-config.yaml | 41 ------------------------------ core/auth.py | 56 ----------------------------------------- 2 files changed, 97 deletions(-) delete mode 100644 .pre-commit-config.yaml delete mode 100644 core/auth.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index d42266a..0000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,41 +0,0 @@ -default_language_version: - python: python3.8 -repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 - hooks: - - id: check-merge-conflict - - id: check-added-large-files - - id: check-ast - - id: check-symlinks - - id: check-yaml - - id: trailing-whitespace - - id: check-json - - id: debug-statements - - id: pretty-format-json - args: ["--autofix", "--allow-missing-credentials"] - - repo: https://github.com/PyCQA/isort - rev: 5.13.2 - hooks: - - id: isort - args: ["--profile", "black"] - - repo: https://github.com/PyCQA/flake8 - rev: "7.0.0" - hooks: - - id: flake8 - additional_dependencies: [flake8-print] - files: '\.py$' - args: - - --select=F401,F403,F406,F821,T001,T003 - - repo: https://github.com/humitos/mirrors-autoflake - rev: v1.1 - hooks: - - id: autoflake - files: '\.py$' - exclude: '^\..*' - args: ["--in-place", "--remove-all-unused-imports"] - - repo: https://github.com/psf/black - rev: 24.4.2 - hooks: - - id: black - args: ["--target-version", "py38"] diff --git a/core/auth.py b/core/auth.py deleted file mode 100644 index c5d5472..0000000 --- a/core/auth.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/python3 - - -from fastapi import APIRouter, Depends, HTTPException, status -from fastapi.security.oauth2 import OAuth2PasswordRequestForm -from sqlalchemy.orm import Session - -from database import configuration -from models import models -from schema import schemas -from schema.hash import Hash -from schema.token import create_access_token - -router = APIRouter( - prefix="/login", - tags=["Authentication"], -) - - -@router.post("/") -def login( - request: OAuth2PasswordRequestForm = Depends(), - db: Session = Depends(configuration.get_db), -): - """ - Login user - - Args: - request (OAuth2PasswordRequestForm, optional): OAuth2PasswordRequestForm. - db (Session, optional): Session. Defaults to Depends(configuration.get_db). - - Raises: - HTTPException: 401 Unauthorized - HTTPException: 404 Not Found - - Returns: - Hash: Hash - """ - user: schemas.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="Invalid Credentials" - ) - - if not Hash.verify(user.password, request.password): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Incorrect password" - ) - - # access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) - access_token = create_access_token(data={"sub": user.email}) - - # generate JWT token and return - return {"access_token": access_token, "token_type": "bearer"}