├── backend ├── db │ ├── __init__.py │ ├── base.py │ ├── repository │ │ ├── login.py │ │ ├── users.py │ │ └── jobs.py │ ├── base_class.py │ ├── models │ │ ├── users.py │ │ └── jobs.py │ └── session.py ├── .coveragerc ├── alembic │ ├── README │ ├── script.py.mako │ ├── versions │ │ └── 875b697ccea6_initial.py │ └── env.py ├── static │ ├── images │ │ ├── logo.png │ │ └── favicon.ico │ └── js │ │ └── autocomplete.js ├── templates │ ├── components │ │ ├── alerts.html │ │ ├── card.html │ │ └── navbar.html │ ├── jobs │ │ ├── homepage.html │ │ ├── detail.html │ │ ├── create_job.html │ │ ├── show_jobs_to_update_delete.html │ │ └── update_job.html │ ├── users │ │ └── users_register.html │ ├── auth │ │ └── login.html │ └── shared │ │ └── base.html ├── .env ├── schemas │ ├── users.py │ └── jobs.py ├── webapps │ ├── base.py │ ├── auth │ │ ├── forms.py │ │ └── route_login.py │ ├── users │ │ ├── forms.py │ │ └── route_users.py │ └── jobs │ │ ├── forms.py │ │ └── route_jobs.py ├── core │ ├── hashing.py │ ├── security.py │ └── config.py ├── tests │ ├── test_routes │ │ ├── test_users.py │ │ └── test_jobs.py │ ├── db │ │ └── test_jobs_repo.py │ ├── utils │ │ └── user.py │ └── conftest.py ├── apis │ ├── base.py │ ├── version1 │ │ ├── route_users.py │ │ ├── route_login.py │ │ └── route_jobs.py │ └── utils.py ├── requirements.txt ├── main.py └── alembic.ini ├── learn ├── routers │ ├── __init__.py │ ├── users.py │ ├── login.py │ └── items.py ├── .coveregerc ├── migrations │ ├── README │ ├── script.py.mako │ ├── versions │ │ └── 3da1df7e42b1_initial.py │ └── env.py ├── static │ ├── images │ │ ├── logo.png │ │ └── favicon.ico │ └── js │ │ └── autocomplete.js ├── 001_helloWorld.py ├── .env ├── requirements.txt ├── templates │ ├── card.html │ ├── item_homepage.html │ ├── item_detail.html │ ├── user_register.html │ ├── create_item.html │ ├── login.html │ ├── base.html │ ├── show_items_to_update_delete.html │ ├── update_item.html │ └── navbar.html ├── hashing.py ├── 004_docs.py ├── 005_disable_docs.py ├── schemas.py ├── database.py ├── 006_docs_url.py ├── 008_env_var.py ├── 002_helloWorld_metadata.py ├── models.py ├── 011_create_users1.py ├── config.py ├── tests │ ├── test1_userroute.py │ ├── test2_userroute.py │ ├── test_userroute.py │ ├── test_itemroute.py │ └── conftest.py ├── 007_project_config.py ├── 003_metadataTags_url.py ├── 009_connect_db.py ├── 012_test_jquery_datatbales.html.save ├── main.py ├── 010_create_users.py ├── 012_test_jquery_datatbales.html ├── webapps │ └── routers │ │ ├── users.py │ │ ├── auth.py │ │ └── items.py ├── utils.py └── alembic.ini ├── .dockerignore ├── Dockerfile ├── docker-compose.yml ├── .gitignore ├── .github └── dependabot.yml └── README.md /backend/db/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /learn/routers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = tests/* 3 | -------------------------------------------------------------------------------- /learn/.coveregerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = tests/* 3 | -------------------------------------------------------------------------------- /backend/alembic/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /learn/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /learn/static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sumanshu-Nankana/FastAPI/HEAD/learn/static/images/logo.png -------------------------------------------------------------------------------- /backend/static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sumanshu-Nankana/FastAPI/HEAD/backend/static/images/logo.png -------------------------------------------------------------------------------- /learn/static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sumanshu-Nankana/FastAPI/HEAD/learn/static/images/favicon.ico -------------------------------------------------------------------------------- /backend/db/base.py: -------------------------------------------------------------------------------- 1 | from db.base_class import Base 2 | from db.models.jobs import Job 3 | from db.models.users import User 4 | -------------------------------------------------------------------------------- /backend/static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sumanshu-Nankana/FastAPI/HEAD/backend/static/images/favicon.ico -------------------------------------------------------------------------------- /backend/templates/components/alerts.html: -------------------------------------------------------------------------------- 1 | {% if msg %} 2 | 5 | {% endif %} 6 | -------------------------------------------------------------------------------- /learn/static/js/autocomplete.js: -------------------------------------------------------------------------------- 1 | $(function() 2 | { 3 | $( "#autocomplete" ).autocomplete({ 4 | source: "/item/autocomplete" 5 | }); 6 | }); 7 | -------------------------------------------------------------------------------- /backend/static/js/autocomplete.js: -------------------------------------------------------------------------------- 1 | $( function() { 2 | $( "#autocomplete" ).autocomplete({ 3 | source: "/job/autocomplete" 4 | }); 5 | } ); 6 | -------------------------------------------------------------------------------- /learn/001_helloWorld.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | 3 | app = FastAPI() 4 | 5 | 6 | @app.get("/") 7 | def hello_api(): 8 | return {"detail": "hello World"} 9 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Ignore all those files which started with 00 and 01 2 | 3 | 00* 4 | 01* 5 | 6 | # Ignore directory htmlcov, migrations 7 | /htmlcov 8 | /migrations 9 | 10 | # Ignore test.db file 11 | test.db 12 | -------------------------------------------------------------------------------- /learn/.env: -------------------------------------------------------------------------------- 1 | POSTGRES_USER=postgres 2 | POSTGRES_PASSWORD=postgres 3 | POSTGRES_SERVER=localhost 4 | POSTGRES_PORT=5432 5 | POSTGRES_DB=testing 6 | SECRET_KEY=c5a2b76df93f80a337b0c8c1589f94b861dd6ecf2e4e45bf704b3b7014581895 7 | -------------------------------------------------------------------------------- /backend/.env: -------------------------------------------------------------------------------- 1 | POSTGRES_USER=postgres 2 | POSTGRES_PASSWORD=postgres 3 | POSTGRES_SERVER=localhost 4 | POSTGRES_PORT=5432 5 | POSTGRES_DB=jobcard 6 | SECRET_KEY=18d98d6c030abdcaaba632eb372280d31aec9663de82257eb04a193d4e94470d 7 | 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM tiangolo/uvicorn-gunicorn-fastapi:python3.8 2 | 3 | COPY ./learn /app 4 | 5 | RUN pip3 install -r requirements.txt 6 | 7 | EXPOSE 8000 8 | 9 | CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] 10 | -------------------------------------------------------------------------------- /backend/db/repository/login.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.orm import Session 2 | from db.models.users import User 3 | 4 | 5 | def get_user(username: str, db: Session): 6 | user = db.query(User).filter(User.email == username).first() 7 | return user 8 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | web: 4 | build: . 5 | ports: 6 | - 8000:8000 7 | environment: 8 | - DB_DBNAME:testing 9 | - DB_PORT:5432 10 | - DB_USER:postgres 11 | - DB_PASS:postgres 12 | - DB_HOST:127.0.0.1 13 | network_mode: host 14 | -------------------------------------------------------------------------------- /learn/requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi 2 | uvicorn 3 | sqlalchemy 4 | python-dotenv 5 | psycopg2-binary 6 | pydantic[email] 7 | bcrypt 8 | passlib 9 | pytest 10 | requests 11 | pytest-cov 12 | python-jose 13 | autopep8 14 | black # black . --exclude=env 15 | jinja2 16 | alembic 17 | aiofiles 18 | python-multipart 19 | -------------------------------------------------------------------------------- /backend/db/base_class.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | from sqlalchemy.ext.declarative import as_declarative, declared_attr 3 | 4 | 5 | @as_declarative() 6 | class Base: 7 | id: Any 8 | __name__: str 9 | 10 | @declared_attr 11 | def __tablename__(cls) -> str: 12 | return cls.__name__.lower() 13 | -------------------------------------------------------------------------------- /learn/templates/card.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
{{ obj.title[:10] }}
4 |

{{ obj.description[:10] }}

5 | Read More 6 |
7 |
8 | -------------------------------------------------------------------------------- /backend/schemas/users.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, EmailStr 2 | 3 | 4 | class UserCreate(BaseModel): 5 | username: str 6 | email: EmailStr 7 | password: str 8 | 9 | 10 | class ShowUser(BaseModel): 11 | username: str 12 | email: EmailStr 13 | is_active: bool 14 | 15 | class Config: 16 | orm_mode = True 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # exclude __pycache__ (bytecode .pyc or .pyo) folder and files 2 | __pycache__ 3 | # exclude env (virtual environment) folder 4 | env 5 | # exclude database file (if we use sqlite) 6 | *.db 7 | # exclude swap files, created by vim/nano editor 8 | *.swp 9 | # exclude .coverage file which get generated using pytest-cov 10 | .coverage 11 | # exclude htmlcov report folder 12 | htmlcov 13 | -------------------------------------------------------------------------------- /backend/webapps/base.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter 2 | from webapps.jobs import route_jobs 3 | from webapps.users import route_users 4 | from webapps.auth import route_login 5 | 6 | api_router = APIRouter(include_in_schema=False) 7 | 8 | api_router.include_router(route_jobs.router, tags=["homepage"]) 9 | api_router.include_router(route_users.router, tags=["users"]) 10 | api_router.include_router(route_login.router, tags=["Auth"]) 11 | -------------------------------------------------------------------------------- /learn/hashing.py: -------------------------------------------------------------------------------- 1 | from passlib.context import CryptContext 2 | 3 | pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") 4 | 5 | 6 | class Hasher: 7 | @staticmethod 8 | def get_hash_password(plain_password): 9 | return pwd_context.hash(plain_password) 10 | 11 | @staticmethod 12 | def verify_password(plain_password, hash_password): 13 | return pwd_context.verify(plain_password, hash_password) 14 | -------------------------------------------------------------------------------- /backend/core/hashing.py: -------------------------------------------------------------------------------- 1 | from passlib.context import CryptContext 2 | 3 | pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") 4 | 5 | 6 | class Hasher: 7 | @staticmethod 8 | def verify_password(plain_password, hashed_password): 9 | return pwd_context.verify(plain_password, hashed_password) 10 | 11 | @staticmethod 12 | def get_hash_password(plain_password): 13 | return pwd_context.hash(plain_password) 14 | -------------------------------------------------------------------------------- /backend/tests/test_routes/test_users.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | def test_create_user(client): 5 | data = { 6 | "username": "testuser1", 7 | "email": "user1@test.com", 8 | "password": "user1password", 9 | } 10 | response = client.post("/users/", json.dumps(data)) 11 | assert response.status_code == 200 12 | assert response.json()["email"] == "user1@test.com" 13 | assert response.json()["is_active"] == True 14 | -------------------------------------------------------------------------------- /backend/apis/base.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter 2 | from apis.version1 import route_users 3 | from apis.version1 import route_jobs 4 | from apis.version1 import route_login 5 | 6 | api_router = APIRouter() 7 | 8 | api_router.include_router(route_users.router, prefix="/users", tags=["users"]) 9 | api_router.include_router(route_jobs.router, prefix="/job", tags=["jobs"]) 10 | api_router.include_router(route_login.router, prefix="/login", tags=["login"]) 11 | -------------------------------------------------------------------------------- /backend/apis/version1/route_users.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter, Depends 2 | from sqlalchemy.orm import Session 3 | from schemas.users import UserCreate, ShowUser 4 | from db.session import get_db 5 | from db.repository.users import create_new_user 6 | 7 | router = APIRouter() 8 | 9 | 10 | @router.post("/", response_model=ShowUser) 11 | def create_user(user: UserCreate, db: Session = Depends(get_db)): 12 | user = create_new_user(user, db) 13 | return user 14 | -------------------------------------------------------------------------------- /backend/templates/components/card.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
{{ obj.title }}
4 |

Company : {{ obj.company }}

5 |

Company URL : {{ obj.company_url }}

6 |

Description : {{ obj.description[:40] }}

7 | Read More 8 |
9 |
10 | -------------------------------------------------------------------------------- /backend/db/repository/users.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.orm import Session 2 | from schemas.users import UserCreate 3 | from db.models.users import User 4 | from core.hashing import Hasher 5 | 6 | 7 | def create_new_user(user: UserCreate, db: Session): 8 | user = User( 9 | username=user.username, 10 | email=user.email, 11 | hashed_password=Hasher.get_hash_password(user.password), 12 | is_active=True, 13 | is_superuser=False, 14 | ) 15 | db.add(user) 16 | db.commit() 17 | db.refresh(user) 18 | return user 19 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "docker" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "monthly" 12 | -------------------------------------------------------------------------------- /learn/004_docs.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | 3 | 4 | # By default, the OpenAPI schema is served at /openapi.json 5 | # But you can configure it with the parameter openapi_url 6 | app = FastAPI(openapi_url="/api/v1/openapi.json") 7 | 8 | 9 | # we can use our own tags using 'tags' parameter 10 | @app.get("/users", tags=["users"]) 11 | def hello_api(): 12 | return {"detail": "hello user"} 13 | 14 | 15 | # we can use our own tags using 'tags' parameter 16 | @app.get("/items", tags=["items"]) 17 | def hello_api(): 18 | return {"detail": "hello item"} 19 | -------------------------------------------------------------------------------- /learn/routers/users.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter, Depends 2 | from schemas import UserCreate, ShowUser 3 | from sqlalchemy.orm import Session 4 | from models import User 5 | from hashing import Hasher 6 | from database import get_db 7 | 8 | router = APIRouter() 9 | 10 | 11 | @router.post("/user", tags=["user"], response_model=ShowUser) 12 | def create_user(user: UserCreate, db: Session = Depends(get_db)): 13 | user = User(email=user.email, password=Hasher.get_hash_password(user.password)) 14 | db.add(user) 15 | db.commit() 16 | db.refresh(user) 17 | return user 18 | -------------------------------------------------------------------------------- /learn/005_disable_docs.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | 3 | # If you want to disable the OpenAPI schema completely you can set openapi_url=None, 4 | # that will also disable the documentation user interfaces that use it. 5 | 6 | app = FastAPI(openapi_url=None) 7 | 8 | 9 | # we can use our own tags using 'tags' parameter 10 | @app.get("/users", tags=["users"]) 11 | def hello_api(): 12 | return {"detail": "hello user"} 13 | 14 | 15 | # we can use our own tags using 'tags' parameter 16 | @app.get("/items", tags=["items"]) 17 | def hello_api(): 18 | return {"detail": "hello item"} 19 | -------------------------------------------------------------------------------- /backend/db/models/users.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, Integer, String, Boolean, ForeignKey 2 | from sqlalchemy.orm import relationship 3 | from db.base_class import Base 4 | 5 | 6 | class User(Base): 7 | id = Column(Integer, primary_key=True, index=True) 8 | username = Column(String, unique=True, nullable=False) 9 | email = Column(String, unique=True, nullable=False, index=True) 10 | hashed_password = Column(String, nullable=False) 11 | is_active = Column(Boolean, default=True) 12 | is_superuser = Column(Boolean, default=False) 13 | jobs = relationship("Job", back_populates="owner") 14 | -------------------------------------------------------------------------------- /learn/schemas.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, EmailStr 2 | from datetime import date 3 | from typing import Optional 4 | 5 | 6 | class UserCreate(BaseModel): 7 | email: EmailStr 8 | password: str 9 | 10 | 11 | class ShowUser(BaseModel): 12 | email: EmailStr 13 | is_active: bool 14 | 15 | class Config: 16 | orm_mode = True 17 | 18 | 19 | class ItemCreate(BaseModel): 20 | title: str 21 | description: str 22 | 23 | 24 | class ShowItem(BaseModel): 25 | title: str 26 | description: str 27 | date_posted: date 28 | 29 | class Config: 30 | orm_mode = True 31 | -------------------------------------------------------------------------------- /backend/alembic/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /learn/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /learn/templates/item_homepage.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %} 3 | Item Home Page 4 | {% endblock %} 5 | {% block content %} 6 |
7 |

All Items...

8 | {% if msg %} 9 | 12 | {% endif %} 13 |
14 | {% for item in items %} 15 |
16 | {% with obj=item %} 17 | {% include 'card.html' %} 18 | {% endwith %} 19 | {% if loop.index % 3 %} 20 |
21 | {% endif %} 22 |
23 | {% endfor %} 24 |
25 |
26 | {% endblock %} 27 | -------------------------------------------------------------------------------- /backend/db/models/jobs.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, Integer, String, Date, Boolean, ForeignKey 2 | from sqlalchemy.orm import relationship 3 | from db.base_class import Base 4 | 5 | 6 | class Job(Base): 7 | id = Column(Integer, primary_key=True, index=True) 8 | title = Column(String, nullable=False) 9 | company = Column(String, nullable=False) 10 | company_url = Column(String) 11 | location = Column(String, nullable=False) 12 | description = Column(String) 13 | date_posted = Column(Date) 14 | is_active = Column(Boolean, default=True) 15 | owner_id = Column(Integer, ForeignKey("user.id")) 16 | owner = relationship("User", back_populates="jobs") 17 | -------------------------------------------------------------------------------- /backend/alembic/versions/875b697ccea6_initial.py: -------------------------------------------------------------------------------- 1 | """initial 2 | 3 | Revision ID: 875b697ccea6 4 | Revises: 5 | Create Date: 2021-09-03 11:13:13.998112 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = "875b697ccea6" 14 | down_revision = None 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | pass 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | pass 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /backend/core/security.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from datetime import datetime, timedelta 3 | from jose import jwt 4 | from core.config import settings 5 | 6 | 7 | def create_access_token(data: dict, expires_delta: Optional[timedelta] = None): 8 | to_encode = data.copy() 9 | if expires_delta: 10 | expire = datetime.utcnow() + expires_delta 11 | else: 12 | expire = datetime.utcnow() + timedelta( 13 | minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES 14 | ) 15 | to_encode.update({"exp": expire}) 16 | encoded_jwt = jwt.encode( 17 | to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM 18 | ) 19 | return encoded_jwt 20 | -------------------------------------------------------------------------------- /backend/templates/jobs/homepage.html: -------------------------------------------------------------------------------- 1 | {% extends "shared/base.html" %} 2 | 3 | {% block title %} 4 | New JobBoard 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 |

Find Jobs...

10 | 11 |
12 | {% for job in jobs %} 13 |
14 | {% with obj=job %} 15 | {% include 'components/card.html' %} 16 | {% endwith %} 17 | {% if loop.index % 3 %} 18 |
19 | {% else %} 20 |

21 | {% endif %} 22 | {% endfor %} 23 |
24 | 25 | {% endblock %} 26 | -------------------------------------------------------------------------------- /learn/templates/item_detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %} 4 | Job Detail 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 |

Job Detail

10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
Title{{item.title}}
Description{{item.description}}
Date Posted{{item.date_posted}}
Owner{{email}}
30 |
31 | {% endblock %} 32 | -------------------------------------------------------------------------------- /backend/schemas/jobs.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel 2 | from typing import Optional 3 | from datetime import datetime, date 4 | 5 | 6 | class JobBase(BaseModel): 7 | title: Optional[str] = None 8 | company: Optional[str] = None 9 | company_url: Optional[str] = None 10 | location: Optional[str] = "Remote" 11 | description: Optional[str] = None 12 | date_posted: Optional[date] = datetime.now().date() 13 | 14 | 15 | class JobCreate(JobBase): 16 | title: str 17 | company: str 18 | location: str 19 | description: str 20 | 21 | 22 | class ShowJob(JobBase): 23 | title: str 24 | company: str 25 | company_url: Optional[str] 26 | location: str 27 | date_posted: date 28 | description: Optional[str] 29 | 30 | class Config: 31 | orm_mode = True 32 | -------------------------------------------------------------------------------- /learn/database.py: -------------------------------------------------------------------------------- 1 | from config import settings 2 | from sqlalchemy import create_engine 3 | from sqlalchemy.orm import sessionmaker 4 | from sqlalchemy.ext.declarative import declarative_base 5 | from typing import Generator 6 | 7 | # For PostgreSQL Database 8 | SQLALCHEMY_DATABASE_URL = settings.DATABASE_URL 9 | engine = create_engine(SQLALCHEMY_DATABASE_URL) 10 | 11 | # For SQLite Database 12 | # SQLALCHEMY_DATABASE_URL = 'sqlite:///./sqlite.db' 13 | # engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args = {"check_same_thread" : False}) 14 | 15 | SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) 16 | Base = declarative_base() 17 | 18 | 19 | def get_db() -> Generator: 20 | try: 21 | db = SessionLocal() 22 | yield db 23 | finally: 24 | db.close() 25 | -------------------------------------------------------------------------------- /learn/006_docs_url.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | 3 | 4 | # We can configure documentation user interfaces. 5 | 6 | # By default, Swagger UI served at /docs 7 | # we can change Swagger UI URL by using parameter docs_url 8 | # You can disable it by setting docs_url=None 9 | 10 | # and ReDoc served at /redoc 11 | # we can change Swagger UI URL by using parameter docs_url 12 | # You can disable it by setting redoc_url=None 13 | 14 | app = FastAPI(docs_url="/documentation", redoc_url="/redocumentation") 15 | 16 | 17 | # we can use our own tags using 'tags' parameter 18 | @app.get("/users", tags=["users"]) 19 | def hello_api(): 20 | return {"detail": "hello user"} 21 | 22 | 23 | # we can use our own tags using 'tags' parameter 24 | @app.get("/items", tags=["items"]) 25 | def hello_api(): 26 | return {"detail": "hello item"} 27 | -------------------------------------------------------------------------------- /learn/008_env_var.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from config import setting 3 | 4 | tags_metadata = [ 5 | {"name": "user", "description": "This is user route"}, 6 | {"name": "products", "description": "This is product route"}, 7 | ] 8 | 9 | app = FastAPI( 10 | title=setting.TITLE, 11 | version=setting.VERSION, 12 | description=setting.DESCRIPTION, 13 | openapi_tags=tags_metadata, 14 | contact={"name": setting.NAME, "email": setting.EMAIL}, 15 | redoc_url=None, 16 | ) 17 | 18 | 19 | @app.get("/users", tags=["user"]) 20 | def get_users(): 21 | return {"message": "hello user"} 22 | 23 | 24 | @app.get("/items", tags=["products"]) 25 | def get_items(): 26 | return {"message": "hello items"} 27 | 28 | 29 | @app.get("/getenvvar", tags=["config"]) 30 | def get_envvars(): 31 | return {"database": setting.DATABASE_URL} 32 | -------------------------------------------------------------------------------- /backend/webapps/auth/forms.py: -------------------------------------------------------------------------------- 1 | from fastapi import Request 2 | from typing import List 3 | 4 | 5 | class LoginForm: 6 | def __init__(self, request: Request): 7 | self.request: Request = request 8 | self.errors: List = [] 9 | self.username: str = None 10 | self.password: str = None 11 | 12 | async def load_data(self): 13 | form = await self.request.form() 14 | self.username = form.get("email") 15 | self.password = form.get("password") 16 | 17 | async def is_valid(self): 18 | if not self.username or not (self.username.__contains__("@")): 19 | self.errors.append("Valid Email is mandatory") 20 | if not self.password or not len(self.password) >= 6: 21 | self.errors.append("Password needs to be > 6 chars") 22 | if not self.errors: 23 | return True 24 | return False 25 | -------------------------------------------------------------------------------- /learn/002_helloWorld_metadata.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | 3 | 4 | # we can pass the metadata information for API 5 | # some fields are of type string and some are of type dictionary (example - contact) 6 | # https://fastapi.tiangolo.com/tutorial/metadata/ 7 | # title = title of the API 8 | # version = version of the API. This is the version of your application, not of OpenAPI 9 | # we can see all this metadata information on /docs path of application 10 | # http://localhost:8000/docs 11 | 12 | description = """ 13 | Hello World API 14 | ## Heading 15 | **Return JSON format of Hello World ** 16 | """ 17 | 18 | app = FastAPI( 19 | title="JobBoard", 20 | version="0.0.1", 21 | description=description, 22 | contact={"name": "Sumanshu Nankana", "email": "sumanshunankana@gmail.com"}, 23 | ) 24 | 25 | 26 | @app.get("/") 27 | def hello_api(): 28 | return {"detail": "hello World"} 29 | -------------------------------------------------------------------------------- /learn/models.py: -------------------------------------------------------------------------------- 1 | from database import Base 2 | from sqlalchemy import Column, Integer, String, Boolean, ForeignKey, Date 3 | from sqlalchemy.orm import relationship 4 | 5 | 6 | class User(Base): 7 | __tablename__ = "users" 8 | 9 | id = Column(Integer, primary_key=True, index=True) 10 | email = Column(String, unique=True, nullable=False, index=True) 11 | password = Column(String, nullable=False) 12 | is_active = Column(Boolean, default=True) 13 | 14 | items = relationship("Items", back_populates="owner") 15 | 16 | 17 | class Items(Base): 18 | __tablename__ = "items" 19 | 20 | id = Column(Integer, primary_key=True, index=True) 21 | title = Column(String, nullable=False) 22 | description = Column(String) 23 | date_posted = Column(Date) 24 | owner_id = Column(Integer, ForeignKey("users.id")) 25 | 26 | owner = relationship("User", back_populates="items") 27 | -------------------------------------------------------------------------------- /learn/011_create_users1.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI, Depends 2 | from config import settings 3 | from database import engine, get_db 4 | from models import Base, User 5 | from schemas import UserCreate 6 | from hashing import Hasher 7 | from sqlalchemy.orm import Session 8 | from routers import users 9 | 10 | desc = """ 11 | This is project description 12 | """ 13 | 14 | tags_metadata = [ 15 | {"name": "user", "description": "This is user route"}, 16 | {"name": "products", "description": "This is product route"}, 17 | ] 18 | 19 | 20 | Base.metadata.create_all(bind=engine) 21 | 22 | app = FastAPI( 23 | title=settings.PROJECT_TITLE, 24 | version=settings.PROJECT_VERSION, 25 | description=desc, 26 | openapi_tags=tags_metadata, 27 | contact={"name": "Sumanshu Nankana", "email": "sumanshunankana@gmail.com"}, 28 | redoc_url=None, 29 | ) 30 | 31 | app.include_router(users.router) 32 | -------------------------------------------------------------------------------- /backend/core/config.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | from dotenv import load_dotenv 4 | 5 | env_path = Path(".") / ".env" 6 | load_dotenv(dotenv_path=env_path) 7 | 8 | 9 | class Settings: 10 | PROJECT_TITLE: str = "Jobboard" 11 | PROJECT_VERSION: str = "0.0.1" 12 | 13 | POSTGRES_USER: str = os.getenv("POSTGRES_USER") 14 | POSTGRES_PASSWORD: str = os.getenv("POSTGRES_PASSWORD") 15 | POSTGRES_SERVER: str = os.getenv("POSTGRES_SERVER", "localhost") 16 | POSTGRES_PORT: str = os.getenv("POSTGRES_PORT", 5432) 17 | POSTGRES_DB: str = os.getenv("POSTGRES_DB", "db_jobcard") 18 | DATABASE_URL = f"postgresql://{POSTGRES_USER}:{POSTGRES_PASSWORD}@{POSTGRES_SERVER}:{POSTGRES_PORT}/{POSTGRES_DB}" 19 | 20 | SECRET_KEY: str = os.getenv("SECRET_KEY") 21 | ALGORITHM = "HS256" 22 | ACCESS_TOKEN_EXPIRE_MINUTES = 30 23 | TEST_USER_EMAIL = "test@example.com" 24 | 25 | 26 | settings = Settings() 27 | -------------------------------------------------------------------------------- /learn/config.py: -------------------------------------------------------------------------------- 1 | import os 2 | from dotenv import load_dotenv 3 | 4 | load_dotenv(dotenv_path=".env") 5 | 6 | 7 | class Settings: 8 | PROJECT_TITLE: str = "Jobboard" 9 | PROJECT_VERSION: str = "0.0.1" 10 | POSTGRES_USER: str = os.getenv("POSTGRES_USER") 11 | POSTGRES_PASSWORD: str = os.getenv("POSTGRES_PASSWORD") 12 | POSTGRES_SERVER: str = os.getenv("POSTGRES_SERVER", "localhost") 13 | POSTGRES_PORT: str = os.getenv("POSTGRES_PORT", 5432) 14 | POSTGRES_DB: str = os.getenv("POSTGRES_DB", "db_jobcard") 15 | DATABASE_URL = f"postgresql://{POSTGRES_USER}:{POSTGRES_PASSWORD}@{POSTGRES_SERVER}:{POSTGRES_PORT}/{POSTGRES_DB}" 16 | ALGORITHM = "HS256" 17 | SECRET_KEY: str = os.getenv("SECRET_KEY") 18 | TEST_EMAIL: str = "test1@test.com" 19 | TEST_PASS: str = "test1pass" 20 | TEST_ITEM: str = "testitem" 21 | TEST_ITEM_DESC: str = "testitem description" 22 | 23 | 24 | settings = Settings() 25 | -------------------------------------------------------------------------------- /learn/tests/test1_userroute.py: -------------------------------------------------------------------------------- 1 | from fastapi.testclient import TestClient 2 | 3 | import sys 4 | import os 5 | import json 6 | 7 | # file = os.path.abspath(__file__) 8 | # print(f"The value os current file is __file__ : {file}") 9 | 10 | # parent_dir = os.path.dirname(file) 11 | # print(f"The value of patent_dir is : {parent_dir}") 12 | 13 | # parent_parent_dir = os.path.dirname(parent_dir) 14 | # print(f"The value of parent_parent_dir is : {parent_parent_dir}") 15 | 16 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 17 | 18 | from main import app 19 | 20 | client = TestClient(app) 21 | 22 | 23 | def test_create_user(): 24 | data = {"email": "test1@test.com", "password": "test1pass"} 25 | response = client.post("/user", json.dumps(data)) 26 | assert response.status_code == 200 27 | assert response.json()["email"] == "test1@test.com" 28 | assert response.json()["is_active"] == True 29 | -------------------------------------------------------------------------------- /backend/templates/jobs/detail.html: -------------------------------------------------------------------------------- 1 | {% extends "shared/base.html" %} 2 | 3 | {% block title %} 4 | Job Detail 5 | {% endblock %} 6 | 7 | {% block content %} 8 | 9 |
10 |

Job Detail

11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
Job Title{{job.title}}
Job Company{{job.company}}
Company URL{{job.company_url}}
Description{{job.description}}
Location{{job.location}}
36 |
37 | 38 | {% endblock %} 39 | -------------------------------------------------------------------------------- /backend/tests/db/test_jobs_repo.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.orm import Session 2 | from db.repository.jobs import create_new_job, retrieve_job 3 | from schemas.jobs import JobCreate 4 | from tests.utils.user import create_random_owner 5 | 6 | 7 | def test_retrieve_job_by_id(db_session: Session): 8 | title = "test title" 9 | company = "test company" 10 | company_url = "https://www/testcompany.com" 11 | location = "USA, NY" 12 | description = "This is test job" 13 | owner = create_random_owner(db=db_session) 14 | job_schema = JobCreate( 15 | title=title, 16 | company=company, 17 | company_url=company_url, 18 | location=location, 19 | description=description, 20 | ) 21 | job = create_new_job(job=job_schema, db=db_session, owner_id=owner.id) 22 | retrieved_job = retrieve_job(id=job.id, db=db_session) 23 | assert retrieved_job.id == job.id 24 | assert retrieved_job.title == "test title" 25 | -------------------------------------------------------------------------------- /learn/007_project_config.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from config import settings 3 | 4 | 5 | # We can configure documentation user interfaces. 6 | 7 | # By default, Swagger UI served at /docs 8 | # we can change Swagger UI URL by using parameter docs_url 9 | # You can disable it by setting docs_url=None 10 | 11 | # and ReDoc served at /redoc 12 | # we can change Swagger UI URL by using parameter docs_url 13 | # You can disable it by setting redoc_url=None 14 | 15 | app = FastAPI( 16 | docs_url="/documentation", 17 | redoc_url="/redocumentation", 18 | title=settings.PROJECT_TITLE, 19 | version=settings.PROJECT_VERSION, 20 | ) 21 | 22 | 23 | # we can use our own tags using 'tags' parameter 24 | @app.get("/users", tags=["users"]) 25 | def hello_api(): 26 | return {"detail": "hello user"} 27 | 28 | 29 | # we can use our own tags using 'tags' parameter 30 | @app.get("/items", tags=["items"]) 31 | def hello_api(): 32 | return {"detail": "hello item"} 33 | -------------------------------------------------------------------------------- /learn/templates/user_register.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %} 4 | User Registration 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 |

User Registration

10 | {% for error in errors %} 11 | 14 | {% endfor %} 15 |
16 |
17 | 18 | 19 |
20 |
21 | 22 | 23 |
24 | 25 |
26 |
27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /backend/db/session.py: -------------------------------------------------------------------------------- 1 | from core.config import settings 2 | from sqlalchemy import create_engine 3 | from sqlalchemy.orm import sessionmaker 4 | from typing import Generator 5 | 6 | # For postgrSQL database 7 | SQLALCHEMY_DATABASE_URL = settings.DATABASE_URL 8 | engine = create_engine(SQLALCHEMY_DATABASE_URL) 9 | 10 | # For SQlLite Database 11 | # By default SQLite will only allow one thread to communicate. 12 | # But in FastAPI, more than one thread can interact with the database 13 | # Thus we need to override default parameter and thus setting check_same_thread : False 14 | # This is required only for SQLite database 15 | 16 | # SQLALCHEMY_DATABASE_URL = 'sqlite:///./sql_app.db' 17 | # engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread" : False}) 18 | 19 | SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) 20 | 21 | 22 | # For dependency Injection 23 | def get_db() -> Generator: 24 | try: 25 | db = SessionLocal() 26 | yield db 27 | finally: 28 | db.close() 29 | -------------------------------------------------------------------------------- /learn/templates/create_item.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %} 4 | Create Item 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 |

Create an Item

10 | {% for error in errors %} 11 | 14 | {% endfor %} 15 |
16 |
17 | 18 | 19 |
20 |
21 | 22 | 23 |
Describe all details about items
24 |
25 | 26 |
27 |
28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /learn/templates/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %} 4 | Login Page 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 |

Login Here !!

10 | {% if msg %} 11 |
12 | {{ msg }} 13 |
14 | {% endif %} 15 | {% for error in errors %} 16 | 19 | {% endfor %} 20 | 21 |
22 |
23 | 24 | 25 |
26 |
27 | 28 | 29 |
30 | 31 |
32 |
33 | {% endblock %} 34 | -------------------------------------------------------------------------------- /learn/003_metadataTags_url.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | 3 | 4 | # we can create a tags for different routes 5 | # Create metadata for your tags and pass it to the openapi_tags parameter 6 | tags_metadata = [ 7 | {"name": "users", "description": "All the operations related with users."}, 8 | {"name": "items", "desciption": "All the operations related with items."}, 9 | ] 10 | 11 | description = """ 12 | Hello World API 13 | ## Heading 14 | **Return JSON format of Hello World ** 15 | """ 16 | 17 | app = FastAPI( 18 | title="JobBoard", 19 | version="0.0.1", 20 | description=description, 21 | contact={"name": "Sumanshu Nankana", "email": "sumanshunankana@gmail.com",}, 22 | openapi_tags=tags_metadata, 23 | ) 24 | 25 | 26 | # we can use our own tags using 'tags' parameter 27 | @app.get("/users", tags=["users"]) 28 | def hello_api(): 29 | return {"detail": "hello user"} 30 | 31 | 32 | # we can use our own tags using 'tags' parameter 33 | @app.get("/items", tags=["items"]) 34 | def hello_api(): 35 | return {"detail": "hello item"} 36 | -------------------------------------------------------------------------------- /backend/templates/users/users_register.html: -------------------------------------------------------------------------------- 1 | {% extends 'shared/base.html' %} 2 | 3 | {% block title %} 4 | User Registration 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 |

User Registration

10 |
11 | {% for error in errors %} 12 |
  • {{error}}
  • 13 | {% endfor %} 14 |
    15 |
    16 |
    17 | 18 | 19 |
    20 |
    21 | 22 | 23 |
    24 |
    25 | 26 | 27 |
    28 | 29 |
    30 |
    31 | {% endblock %} 32 | -------------------------------------------------------------------------------- /backend/tests/test_routes/test_jobs.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | def test_create_job(client, normal_user_token_headers): 5 | data = { 6 | "title": "FastAPI Developer", 7 | "company": "Wipro", 8 | "company_url": "https://www.wipro.com", 9 | "location": "India", 10 | "description": "We are hiring FastAPI Developers", 11 | "date_posted": "2022-07-20", 12 | } 13 | response = client.post( 14 | "/job/create-job", json.dumps(data), headers=normal_user_token_headers 15 | ) 16 | assert response.status_code == 200 17 | 18 | 19 | def test_retrieve_job_by_id(client): 20 | data = { 21 | "title": "FastAPI Developer", 22 | "company": "Wipro", 23 | "company_url": "https://www.wipro.com", 24 | "location": "India", 25 | "description": "We are hiring FastAPI Developers", 26 | "date_posted": "2022-07-20", 27 | } 28 | client.post("/job/create-job", json.dumps(data)) 29 | response = client.get("/job/get/1") 30 | assert response.status_code == 200 31 | assert response.json()["title"] == "FastAPI Developer" 32 | -------------------------------------------------------------------------------- /backend/webapps/users/forms.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional 2 | from fastapi import Request 3 | 4 | 5 | class UserCreateForm: 6 | def __init__(self, request: Request): 7 | self.request: Request = request 8 | self.errors: List = [] 9 | self.username: Optional[str] = None 10 | self.email: Optional[str] = None 11 | self.password: Optional[str] = None 12 | 13 | async def load_data(self): 14 | form = await self.request.form() 15 | self.username = form.get("username") 16 | self.email = form.get("email") 17 | self.password = form.get("password") 18 | 19 | async def is_valid(self): 20 | if not self.username or not len(self.username) > 4: 21 | self.errors.append("Username must by > 4 chars") 22 | if not self.email or not (self.email.__contains__("@")): 23 | self.errors.append("valid email is required") 24 | if not self.password or not len(self.password) > 5: 25 | self.errors.append("Password should be > 5 chars") 26 | if not self.errors: 27 | return True 28 | return False 29 | -------------------------------------------------------------------------------- /backend/templates/auth/login.html: -------------------------------------------------------------------------------- 1 | 2 | {% extends 'shared/base.html' %} 3 | 4 | {% block title %} 5 | Login Page 6 | {% endblock %} 7 | 8 | {% block content %} 9 |
    10 |

    Login Page !

    11 | 12 | {% if msg %} 13 | 16 | {% endif %} 17 | 18 | {% for error in errors %} 19 | 22 | {% endfor %} 23 | 24 |
    25 |
    26 | 27 | 28 |
    29 |
    30 | 31 | 32 |
    33 | 34 |
    35 |
    36 | {% endblock %} 37 | -------------------------------------------------------------------------------- /learn/009_connect_db.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from config import settings 3 | from database import engine 4 | from models import Base 5 | 6 | 7 | desc = """ 8 | This is project description 9 | """ 10 | 11 | tags_metadata = [ 12 | {"name": "user", "description": "This is user route"}, 13 | {"name": "products", "description": "This is product route"}, 14 | ] 15 | 16 | 17 | Base.metadata.create_all(bind=engine) 18 | 19 | app = FastAPI( 20 | title=settings.PROJECT_TITLE, 21 | version=settings.PROJECT_VERSION, 22 | description=desc, 23 | openapi_tags=tags_metadata, 24 | contact={"name": "Sumanshu Nankana", "email": "sumanshunankana@gmail.com"}, 25 | redoc_url=None, 26 | ) 27 | 28 | 29 | @app.get("/users", tags=["user"]) 30 | def get_users(): 31 | return {"message": "hello user"} 32 | 33 | 34 | @app.get("/items", tags=["products"]) 35 | def get_items(): 36 | return {"message": "hello items"} 37 | 38 | 39 | @app.get("/getenvvar", tags=["config"]) 40 | def get_envvars(): 41 | return {"database": setting.DATABASE_URL} 42 | 43 | 44 | @app.post("/users", target=["users"]) 45 | def create_users(): 46 | pass 47 | -------------------------------------------------------------------------------- /learn/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {% block title %} 10 | {% endblock %} 11 | 12 | 13 | {% include 'navbar.html' %} 14 | {% block content %} 15 | {% endblock %} 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | {% block script %} 25 | {% endblock %} 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /backend/templates/shared/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {% block title %} 11 | {% endblock %} 12 | 13 | 14 | 15 | {% include 'components/navbar.html' %} 16 | {% block content %} 17 | {% endblock %} 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | {% block script %} 26 | {% endblock %} 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /learn/tests/test2_userroute.py: -------------------------------------------------------------------------------- 1 | from fastapi.testclient import TestClient 2 | from sqlalchemy import create_engine 3 | from sqlalchemy.orm import sessionmaker 4 | 5 | import sys 6 | import os 7 | import json 8 | 9 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 10 | 11 | from models import Base 12 | from database import get_db 13 | from main import app 14 | 15 | SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db" 16 | engine = create_engine( 17 | SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} 18 | ) 19 | TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) 20 | 21 | Base.metadata.create_all(bind=engine) 22 | 23 | 24 | def override_get_db(): 25 | try: 26 | db = TestingSessionLocal() 27 | yield db 28 | finally: 29 | db.close() 30 | 31 | 32 | app.dependency_overrides[get_db] = override_get_db 33 | 34 | client = TestClient(app) 35 | 36 | 37 | def test_create_user(): 38 | data = {"email": "test2@test.com", "password": "test1pass"} 39 | response = client.post("/user", json.dumps(data)) 40 | assert response.status_code == 200 41 | assert response.json()["email"] == "test2@test.com" 42 | assert response.json()["is_active"] == True 43 | -------------------------------------------------------------------------------- /backend/requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi # for fastapi 2 | uvicorn # for server to run fastapi 3 | 4 | sqlalchemy # for orm 5 | psycopg2-binary # database adaptor which is require to connect to postgres via python 6 | 7 | python-dotenv # to store environment variables in .env file, we can store in system, but python-dotenv is recommended 8 | 9 | pydantic[email] # for email-validation 10 | 11 | # passlib[bcrypt] # for password hashing (This gives some error) 12 | bcrypt # required for passlib module 13 | passlib # for password hashing 14 | 15 | pytest # for testing 16 | requests # for testing 17 | 18 | pytest-cov # for code coverage report; pytest --cov="." ; pytest --cov="." --cov-report html 19 | 20 | python-jose # for JWT tokens (It's a superset of pyjwt library) 21 | 22 | python-multipart # for form data 23 | 24 | jinja2 # for html templates 25 | 26 | alembic # for db migrations 27 | 28 | aiofiles # for serving static files 29 | 30 | black # for formatting the code command : black . --exclude=env 31 | 32 | fastapi-pagination # for pagination in fast-api 33 | -------------------------------------------------------------------------------- /learn/tests/test_userroute.py: -------------------------------------------------------------------------------- 1 | # from fastapi.testclient import TestClient 2 | # from sqlalchemy import create_engine 3 | # from sqlalchemy.orm import sessionmaker 4 | 5 | # import sys 6 | # import os 7 | # import json 8 | 9 | # sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 10 | 11 | # from models import Base 12 | # from database import get_db 13 | # from main import app 14 | 15 | # SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db" 16 | # engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}) 17 | # TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) 18 | 19 | # Base.metadata.create_all(bind=engine) 20 | 21 | # def override_get_db(): 22 | # try: 23 | # db = TestingSessionLocal() 24 | # yield db 25 | # finally: 26 | # db.close() 27 | 28 | # app.dependency_overrides[get_db] = override_get_db 29 | 30 | # client = TestClient(app) 31 | 32 | import json 33 | from config import settings 34 | 35 | 36 | def test_create_user(client): 37 | data = {"email": "test2@test.com", "password": "test2pass"} 38 | response = client.post("/user", json.dumps(data)) 39 | assert response.status_code == 200 40 | assert response.json()["email"] == "test2@test.com" 41 | assert response.json()["is_active"] == True 42 | -------------------------------------------------------------------------------- /learn/012_test_jquery_datatbales.html.save: -------------------------------------------------------------------------------- 1 | 2 | 3 | DataTable 4 | 5 | 6 | 7 | 8 | 9 |
    10 |

    test datatable

    11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
    #FirstLastHandle
    1MarkOtto@mdo
    2JacobThornton@fat
    3Larrythe Bird@twitter
    41 |
    42 | 43 | 44 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /backend/webapps/auth/route_login.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter, Request, Depends, HTTPException 2 | from fastapi.templating import Jinja2Templates 3 | from sqlalchemy.orm import Session 4 | from db.session import get_db 5 | from webapps.auth.forms import LoginForm 6 | from apis.version1.route_login import login_for_access_token 7 | 8 | 9 | router = APIRouter(include_in_schema=False) 10 | templates = Jinja2Templates(directory="templates") 11 | 12 | 13 | @router.get("/login") 14 | def login(request: Request, msg: str = None): 15 | return templates.TemplateResponse( 16 | "auth/login.html", {"request": request, "msg": msg} 17 | ) 18 | 19 | 20 | @router.post("/login") 21 | async def login(request: Request, db: Session = Depends(get_db)): 22 | form = LoginForm(request) 23 | await form.load_data() 24 | if await form.is_valid(): 25 | try: 26 | form.__dict__.update(msg="Login Successful") 27 | response = templates.TemplateResponse("auth/login.html", form.__dict__) 28 | login_for_access_token(response=response, form_data=form, db=db) 29 | return response 30 | except HTTPException: 31 | form.__dict__.update(msg="") 32 | form.__dict__.get("errors").append("Incorrect Username or password") 33 | return templates.TemplateResponse("auth/login.html", form.__dict__) 34 | return templates.TemplateResponse("auth/login.html", form.__dict__) 35 | -------------------------------------------------------------------------------- /learn/tests/test_itemroute.py: -------------------------------------------------------------------------------- 1 | # from fastapi.testclient import TestClient 2 | # from sqlalchemy import create_engine 3 | # from sqlalchemy.orm import sessionmaker 4 | 5 | # import sys 6 | # import os 7 | # import json 8 | 9 | # sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 10 | 11 | # from models import Base 12 | # from database import get_db 13 | # from main import app 14 | 15 | # SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db" 16 | # engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}) 17 | # TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) 18 | 19 | # Base.metadata.create_all(bind=engine) 20 | 21 | # def override_get_db(): 22 | # try: 23 | # db = TestingSessionLocal() 24 | # yield db 25 | # finally: 26 | # db.close() 27 | 28 | # app.dependency_overrides[get_db] = override_get_db 29 | 30 | # client = TestClient(app) 31 | 32 | import json 33 | from config import settings 34 | 35 | 36 | def test_create_item(client, token_headers): 37 | data = {"title": settings.TEST_ITEM, "description": settings.TEST_ITEM_DESC} 38 | response = client.post("/item", json.dumps(data), headers=token_headers) 39 | assert response.status_code == 200 40 | 41 | 42 | def test_retrieve_item_by_id(client): 43 | response = client.get("/item/1") 44 | assert response.status_code == 200 45 | assert response.json()["title"] == settings.TEST_ITEM 46 | -------------------------------------------------------------------------------- /learn/routers/login.py: -------------------------------------------------------------------------------- 1 | from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm 2 | from utils import OAuth2PasswordBearerWithCookie 3 | from fastapi import APIRouter, Depends, HTTPException, status, Response 4 | from sqlalchemy.orm import Session 5 | from database import get_db 6 | from models import User 7 | from hashing import Hasher 8 | from jose import jwt 9 | from config import settings 10 | 11 | oauth2_scheme = OAuth2PasswordBearerWithCookie(tokenUrl="/login/token") 12 | 13 | 14 | router = APIRouter() 15 | 16 | 17 | @router.post("/login/token") 18 | def retrieve_token_for_authenticated_user( 19 | response: Response, 20 | form_data: OAuth2PasswordRequestForm = Depends(), 21 | db: Session = Depends(get_db), 22 | ): 23 | user = db.query(User).filter(User.email == form_data.username).first() 24 | if not user: 25 | raise HTTPException( 26 | status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid username" 27 | ) 28 | if not Hasher.verify_password(form_data.password, user.password): 29 | raise HTTPException( 30 | status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid Password" 31 | ) 32 | data = {"sub": form_data.username} 33 | jwt_token = jwt.encode(data, settings.SECRET_KEY, algorithm=settings.ALGORITHM) 34 | response.set_cookie(key="access_token", value=f"Bearer {jwt_token}", httponly=True) 35 | return {"access_token": jwt_token, "token_type": "bearer"} 36 | -------------------------------------------------------------------------------- /backend/db/repository/jobs.py: -------------------------------------------------------------------------------- 1 | from fastapi.encoders import jsonable_encoder 2 | from sqlalchemy.orm import Session 3 | from schemas.jobs import JobCreate 4 | from db.models.jobs import Job 5 | 6 | 7 | def create_new_job(job: JobCreate, db: Session, owner_id: int): 8 | job = Job(**job.dict(), owner_id=owner_id) 9 | db.add(job) 10 | db.commit() 11 | db.refresh(job) 12 | return job 13 | 14 | 15 | def retrieve_job(id: int, db: Session): 16 | job = db.query(Job).filter(Job.id == id).first() 17 | return job 18 | 19 | 20 | def list_jobs(db: Session): 21 | jobs = db.query(Job).filter(Job.is_active == True).all() 22 | return jobs 23 | 24 | 25 | def update_job_by_id(id: int, job: JobCreate, db: Session, owner_id: int): 26 | existing_job = db.query(Job).filter(Job.id == id) 27 | if not existing_job.first(): 28 | return 0 29 | 30 | # job.__dict__.update(owner_id=owner_id) 31 | try: 32 | existing_job.update(job.__dict__) 33 | db.commit() 34 | except Exception as e: 35 | print(e) 36 | return 1 37 | 38 | 39 | def delete_job_by_id(id: int, db: Session, owner_id: int): 40 | existing_job = db.query(Job).filter(Job.id == id) 41 | if not existing_job.first(): 42 | return 0 43 | existing_job.delete(synchronize_session=False) 44 | db.commit() 45 | return 1 46 | 47 | 48 | def search_job(query: str, db: Session): 49 | jobs = db.query(Job).filter(Job.title.contains(query)) 50 | return jobs 51 | -------------------------------------------------------------------------------- /learn/main.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI, Depends 2 | from config import settings 3 | from database import engine, get_db 4 | from models import Base, User 5 | from schemas import UserCreate 6 | from hashing import Hasher 7 | from sqlalchemy.orm import Session 8 | from routers import users, items, login 9 | from webapps.routers import items as web_items 10 | from webapps.routers import users as web_users 11 | from webapps.routers import auth as web_auth 12 | from fastapi.staticfiles import StaticFiles 13 | 14 | 15 | desc = """ 16 | This is project description 17 | """ 18 | 19 | tags_metadata = [ 20 | {"name": "user", "description": "This is user route"}, 21 | {"name": "products", "description": "This is product route"}, 22 | ] 23 | 24 | # commented this line, because we have shifted to alembic migrations 25 | # Base.metadata.create_all(bind=engine) 26 | 27 | app = FastAPI( 28 | title=settings.PROJECT_TITLE, 29 | version=settings.PROJECT_VERSION, 30 | description=desc, 31 | openapi_tags=tags_metadata, 32 | contact={"name": "Sumanshu Nankana", "email": "sumanshunankana@gmail.com"}, 33 | redoc_url=None, 34 | ) 35 | 36 | app.mount("/static", StaticFiles(directory="static"), name="static") 37 | 38 | # we can pass the prefix argument as well 39 | app.include_router(users.router) 40 | app.include_router(items.router) 41 | app.include_router(login.router) 42 | app.include_router(web_items.router) 43 | app.include_router(web_users.router) 44 | app.include_router(web_auth.router) 45 | -------------------------------------------------------------------------------- /backend/webapps/jobs/forms.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from typing import Optional 3 | from fastapi import Request 4 | 5 | 6 | class JobCreateForm: 7 | def __init__(self, request: Request): 8 | self.request: Request = request 9 | self.errors: List = [] 10 | self.title: Optional[str] = None 11 | self.company: Optional[str] = None 12 | self.company_url: Optional[str] = None 13 | self.location: Optional[str] = None 14 | self.description: Optional[str] = None 15 | 16 | async def load_data(self): 17 | form = await self.request.form() 18 | self.title = form.get("title") 19 | self.company = form.get("company") 20 | self.company_url = form.get("company_url") 21 | self.location = form.get("location") 22 | self.description = form.get("description") 23 | 24 | def is_valid(self): 25 | if not self.title or not len(self.title) >= 4: 26 | self.errors.append("A valid title is required") 27 | if not self.company_url or not (self.company_url.__contains__("http")): 28 | self.errors.append("Valid Url is required e.g. https://example.com") 29 | if not self.company or not len(self.company) >= 1: 30 | self.errors.append("A valid company is required") 31 | if not self.description or not len(self.description) >= 20: 32 | self.errors.append("Description too short") 33 | if not self.errors: 34 | return True 35 | return False 36 | -------------------------------------------------------------------------------- /learn/010_create_users.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI, Depends 2 | from config import settings 3 | from database import engine, get_db 4 | from models import Base, User 5 | from schemas import UserCreate 6 | from hashing import Hasher 7 | from sqlalchemy.orm import Session 8 | 9 | 10 | desc = """ 11 | This is project description 12 | """ 13 | 14 | tags_metadata = [ 15 | {"name": "user", "description": "This is user route"}, 16 | {"name": "products", "description": "This is product route"}, 17 | ] 18 | 19 | 20 | Base.metadata.create_all(bind=engine) 21 | 22 | app = FastAPI( 23 | title=settings.PROJECT_TITLE, 24 | version=settings.PROJECT_VERSION, 25 | description=desc, 26 | openapi_tags=tags_metadata, 27 | contact={"name": "Sumanshu Nankana", "email": "sumanshunankana@gmail.com"}, 28 | redoc_url=None, 29 | ) 30 | 31 | 32 | @app.get("/users", tags=["user"]) 33 | def get_users(): 34 | return {"message": "hello user"} 35 | 36 | 37 | @app.get("/items", tags=["products"]) 38 | def get_items(): 39 | return {"message": "hello items"} 40 | 41 | 42 | @app.get("/getenvvar", tags=["config"]) 43 | def get_envvars(): 44 | return {"database": setting.DATABASE_URL} 45 | 46 | 47 | @app.post("/users", tags=["user"]) 48 | def create_users(user: UserCreate, db: Session = Depends(get_db)): 49 | hashed_password = Hasher.get_hash_password(user.password) 50 | user = User(email=user.email, password=hashed_password) 51 | db.add(user) 52 | db.commit() 53 | db.refresh(user) 54 | return user 55 | -------------------------------------------------------------------------------- /learn/012_test_jquery_datatbales.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | DataTable 4 | 5 | 6 | 7 | 8 | 9 |
    10 |

    test datatable

    11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
    #FirstLastHandle
    1MarkOtto@mdo
    2JacobThornton@fat
    3Larrythe Bird@twitter
    41 |
    42 | 43 | 44 | 45 | 46 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /backend/apis/utils.py: -------------------------------------------------------------------------------- 1 | from fastapi.security import OAuth2 2 | from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel 3 | from fastapi import Request, HTTPException, status 4 | from fastapi.security.utils import get_authorization_scheme_param 5 | from typing import Optional 6 | from typing import Dict 7 | 8 | 9 | class OAuth2PasswordBearerWithCookie(OAuth2): 10 | def __init__( 11 | self, 12 | tokenUrl: str, 13 | scheme_name: Optional[str] = None, 14 | scopes: Optional[Dict[str, str]] = None, 15 | description: Optional[str] = None, 16 | auto_error: bool = True, 17 | ): 18 | if not scopes: 19 | scopes = {} 20 | flows = OAuthFlowsModel(password={"tokenUrl": tokenUrl, "scopes": scopes}) 21 | super().__init__( 22 | flows=flows, 23 | scheme_name=scheme_name, 24 | description=description, 25 | auto_error=auto_error, 26 | ) 27 | 28 | async def __call__(self, request: Request) -> Optional[str]: 29 | authorization: str = request.cookies.get("access_token") 30 | scheme, param = get_authorization_scheme_param(authorization) 31 | if not authorization or scheme.lower() != "bearer": 32 | if self.auto_error: 33 | raise HTTPException( 34 | status_code=status.HTTP_401_UNAUTHORIZED, 35 | detail="Not authenticated", 36 | headers={"WWW-Authenticate": "Bearer"}, 37 | ) 38 | else: 39 | return None 40 | return param 41 | -------------------------------------------------------------------------------- /backend/webapps/users/route_users.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter, Request, responses, status, Depends 2 | from fastapi.templating import Jinja2Templates 3 | from db.repository.users import create_new_user 4 | from db.session import get_db 5 | from schemas.users import UserCreate 6 | from sqlalchemy.orm import Session 7 | from sqlalchemy.exc import IntegrityError 8 | from webapps.users.forms import UserCreateForm 9 | 10 | 11 | router = APIRouter(include_in_schema=False) 12 | templates = Jinja2Templates(directory="templates") 13 | 14 | 15 | @router.get("/register") 16 | def register(request: Request): 17 | return templates.TemplateResponse("users/users_register.html", {"request": request}) 18 | 19 | 20 | @router.post("/register") 21 | async def register(request: Request, db: Session = Depends(get_db)): 22 | form = UserCreateForm(request) 23 | await form.load_data() 24 | if await form.is_valid(): 25 | user = UserCreate( 26 | username=form.username, email=form.email, password=form.password 27 | ) 28 | try: 29 | user = create_new_user(user=user, db=db) 30 | return responses.RedirectResponse( 31 | "/login?msg=Successfully-Registered", status_code=status.HTTP_302_FOUND 32 | ) 33 | except IntegrityError: 34 | form.__dict__.get("errors").append("Duplicate username or email") 35 | return templates.TemplateResponse( 36 | "users/users_register.html", form.__dict__ 37 | ) 38 | return templates.TemplateResponse("users/users_register.html", form.__dict__) 39 | -------------------------------------------------------------------------------- /backend/tests/utils/user.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.orm import Session 2 | import random 3 | import string 4 | from schemas.users import UserCreate 5 | from db.repository.users import create_new_user 6 | 7 | from db.repository.users import create_new_user 8 | from db.repository.login import get_user 9 | from fastapi.testclient import TestClient 10 | from schemas.users import UserCreate 11 | 12 | 13 | def random_lower_string() -> str: 14 | return "".join(random.choices(string.ascii_lowercase, k=32)) 15 | 16 | 17 | def create_random_owner(db: Session): 18 | email = f"{random_lower_string()}@{random_lower_string()}.com" 19 | password = random_lower_string() 20 | user_schema = UserCreate(username=email, email=email, password=password) 21 | user = create_new_user(user=user_schema, db=db) 22 | return user 23 | 24 | 25 | def user_authentication_headers(client: TestClient, email: str, password: str): 26 | data = {"username": email, "password": password} 27 | r = client.post("/login/token", data=data) 28 | response = r.json() 29 | auth_token = response["access_token"] 30 | headers = {"Authorization": f"Bearer {auth_token}"} 31 | return headers 32 | 33 | 34 | def authentication_token_from_email(client: TestClient, email: str, db: Session): 35 | password = "randomPassword" 36 | user = get_user(username=email, db=db) 37 | if not user: 38 | user_in_create = UserCreate(username=email, email=email, password=password) 39 | user = create_new_user(user=user_in_create, db=db) 40 | return user_authentication_headers(client=client, email=email, password=password) 41 | -------------------------------------------------------------------------------- /backend/main.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from core.config import settings 3 | from db.session import engine 4 | from db.base import Base 5 | from apis.base import api_router 6 | from webapps.base import api_router as webapp_router 7 | from fastapi.staticfiles import StaticFiles 8 | from fastapi_pagination import add_pagination 9 | 10 | 11 | # we can pass the metadata information for API 12 | # some fields are of type string and some are of type dictionary (example - contact) 13 | # https://fastapi.tiangolo.com/tutorial/metadata/ 14 | # title = title of the API 15 | # version = version of the API. This is the version of your application, not of OpenAPI 16 | # we can see all this metadata information on /docs path of application 17 | # http://localhost:8000/docs 18 | 19 | 20 | def create_tables(): 21 | Base.metadata.create_all(bind=engine) 22 | 23 | 24 | description = """ 25 | Hello World API 26 | ## Heading 27 | **Return JSON format of Hello World ** 28 | """ 29 | 30 | 31 | def include_router(app): 32 | app.include_router(api_router) 33 | app.include_router(webapp_router) 34 | 35 | 36 | def configure_static(app): 37 | app.mount("/static", StaticFiles(directory="static"), name="static") 38 | 39 | 40 | def start_application(): 41 | app = FastAPI( 42 | title=settings.PROJECT_TITLE, 43 | version=settings.PROJECT_VERSION, 44 | description=description, 45 | contact={"name": "Sumanshu Nankana", "email": "sumanshunankana@gmail.com"}, 46 | ) 47 | create_tables() 48 | include_router(app) 49 | configure_static(app) 50 | return app 51 | 52 | 53 | app = start_application() 54 | -------------------------------------------------------------------------------- /learn/webapps/routers/users.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter, Request, Depends, responses, status 2 | from fastapi.templating import Jinja2Templates 3 | from sqlalchemy.orm import Session 4 | from database import get_db 5 | from models import User 6 | from hashing import Hasher 7 | from sqlalchemy.exc import IntegrityError 8 | 9 | router = APIRouter() 10 | templates = Jinja2Templates(directory="templates") 11 | 12 | 13 | @router.get("/register") 14 | def registration(request: Request): 15 | return templates.TemplateResponse("user_register.html", {"request": request}) 16 | 17 | 18 | @router.post("/register") 19 | async def registration(request: Request, db: Session = Depends(get_db)): 20 | form = await request.form() 21 | email = form.get("email") 22 | password = form.get("password") 23 | errors = [] 24 | if not password or len(password) < 6: 25 | errors.append("Password should be greater than 6 chars") 26 | if not email: 27 | errors.append("Email can't be blank") 28 | user = User(email=email, password=Hasher.get_hash_password(password)) 29 | if len(errors) > 0: 30 | return templates.TemplateResponse( 31 | "user_register.html", {"request": request, "errors": errors} 32 | ) 33 | try: 34 | db.add(user) 35 | db.commit() 36 | db.refresh(user) 37 | return responses.RedirectResponse( 38 | "/?msg=successfully registered", status_code=status.HTTP_302_FOUND 39 | ) 40 | except IntegrityError: 41 | errors.append("Duplicate email") 42 | return templates.TemplateResponse( 43 | "user_register.html", {"request": request, "errors": errors} 44 | ) 45 | -------------------------------------------------------------------------------- /learn/templates/show_items_to_update_delete.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %} 4 | Delete Item 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
    9 |

    UPDATE and DELETE ITEMS

    10 | {% for error in errors %} 11 | 14 | {% endfor %} 15 | 16 |
    17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | {% for item in items %} 28 | 29 | 30 | 31 | 32 | 36 | 37 | {% endfor %} 38 | 39 |
    S.No.TitleDescriptionAction
    {{ loop.index }}{{ item.title }}{{ item.description }} 33 | 34 | Edit 35 |
    40 |
    41 | {% endblock %} 42 | 43 | {% block script %} 44 | 50 | 58 | {% endblock %} 59 | -------------------------------------------------------------------------------- /learn/utils.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Dict, List, Optional, Union 2 | 3 | from fastapi.security import OAuth2 4 | from fastapi.exceptions import HTTPException 5 | from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel 6 | from fastapi.security.utils import get_authorization_scheme_param 7 | from starlette.requests import Request 8 | from starlette.status import HTTP_401_UNAUTHORIZED 9 | 10 | 11 | class OAuth2PasswordBearerWithCookie(OAuth2): 12 | def __init__( 13 | self, 14 | tokenUrl: str, 15 | scheme_name: Optional[str] = None, 16 | scopes: Optional[Dict[str, str]] = None, 17 | description: Optional[str] = None, 18 | auto_error: bool = True, 19 | ): 20 | if not scopes: 21 | scopes = {} 22 | flows = OAuthFlowsModel(password={"tokenUrl": tokenUrl, "scopes": scopes}) 23 | super().__init__( 24 | flows=flows, 25 | scheme_name=scheme_name, 26 | description=description, 27 | auto_error=auto_error, 28 | ) 29 | 30 | async def __call__(self, request: Request) -> Optional[str]: 31 | # authorization: str = request.headers.get("Authorization") 32 | authorization: str = request.cookies.get("access_token") 33 | scheme, param = get_authorization_scheme_param(authorization) 34 | if not authorization or scheme.lower() != "bearer": 35 | if self.auto_error: 36 | raise HTTPException( 37 | status_code=HTTP_401_UNAUTHORIZED, 38 | detail="Not authenticated", 39 | headers={"WWW-Authenticate": "Bearer"}, 40 | ) 41 | else: 42 | return None 43 | return param 44 | -------------------------------------------------------------------------------- /learn/templates/update_item.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %} 4 | Create Item 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
    9 |

    Update an Item

    10 | {% for error in errors %} 11 | 14 | {% endfor %} 15 |
    16 |
    17 |
    18 | 19 | 20 |
    21 |
    22 | 23 | 24 |
    Describe all details about items
    25 |
    26 | 27 |
    28 | 29 |
    30 | {% endblock %} 31 | 32 | {% block script %} 33 | 46 | {% endblock %} 47 | -------------------------------------------------------------------------------- /backend/templates/jobs/create_job.html: -------------------------------------------------------------------------------- 1 | {% extends 'shared/base.html' %} 2 | 3 | {% block title %} 4 | Create a Job Post 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
    9 |

    Create a Job Post

    10 | {% for error in errors %} 11 | 14 | {% endfor %} 15 |
    16 |
    17 | 18 | 19 |
    20 |
    21 | 22 | 23 |
    24 |
    25 | 26 | 27 |
    28 |
    29 | 30 | 31 |
    32 |
    33 | 34 | 35 |
    Please provide complete Job description,requirements,perks and benefits.
    36 |
    37 | 38 |
    39 |
    40 | {% endblock %} 41 | -------------------------------------------------------------------------------- /backend/templates/jobs/show_jobs_to_update_delete.html: -------------------------------------------------------------------------------- 1 | {% extends 'shared/base.html' %} 2 | 3 | {% block title %} 4 | Delete Jobs 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
    9 |
    10 |
    11 |

    Update and Delete Jobs

    12 |
    13 |
    14 |
    15 | 16 |
    17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {% for job in jobs %} 30 | 31 | 32 | 33 | 34 | 35 | 36 | 39 | 40 | {% endfor %} 41 | 42 |
    Sr.No.TitleCompanyCompany URLLocationAction
    {{loop.index}}{{job.title}}{{job.company}}{{job.company_url}}{{job.location}} 37 | Edit 38 |
    43 |
    44 |
    45 | {% endblock %} 46 | 47 | 48 | {% block script %} 49 | 55 | 62 | {% endblock %} 63 | -------------------------------------------------------------------------------- /learn/migrations/versions/3da1df7e42b1_initial.py: -------------------------------------------------------------------------------- 1 | """Initial 2 | 3 | Revision ID: 3da1df7e42b1 4 | Revises: 5 | Create Date: 2021-09-03 10:42:56.245147 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = "3da1df7e42b1" 14 | down_revision = None 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.create_table( 22 | "users", 23 | sa.Column("id", sa.Integer(), nullable=False), 24 | sa.Column("email", sa.String(), nullable=False), 25 | sa.Column("password", sa.String(), nullable=False), 26 | sa.Column("is_active", sa.Boolean(), nullable=True), 27 | sa.PrimaryKeyConstraint("id"), 28 | ) 29 | op.create_index(op.f("ix_users_email"), "users", ["email"], unique=True) 30 | op.create_index(op.f("ix_users_id"), "users", ["id"], unique=False) 31 | op.create_table( 32 | "items", 33 | sa.Column("id", sa.Integer(), nullable=False), 34 | sa.Column("title", sa.String(), nullable=False), 35 | sa.Column("description", sa.String(), nullable=True), 36 | sa.Column("date_posted", sa.Date(), nullable=True), 37 | sa.Column("owner_id", sa.Integer(), nullable=True), 38 | sa.ForeignKeyConstraint(["owner_id"], ["users.id"],), 39 | sa.PrimaryKeyConstraint("id"), 40 | ) 41 | op.create_index(op.f("ix_items_id"), "items", ["id"], unique=False) 42 | # ### end Alembic commands ### 43 | 44 | 45 | def downgrade(): 46 | # ### commands auto generated by Alembic - please adjust! ### 47 | op.drop_index(op.f("ix_items_id"), table_name="items") 48 | op.drop_table("items") 49 | op.drop_index(op.f("ix_users_id"), table_name="users") 50 | op.drop_index(op.f("ix_users_email"), table_name="users") 51 | op.drop_table("users") 52 | # ### end Alembic commands ### 53 | -------------------------------------------------------------------------------- /learn/tests/conftest.py: -------------------------------------------------------------------------------- 1 | from fastapi.testclient import TestClient 2 | from sqlalchemy import create_engine 3 | from sqlalchemy.orm import sessionmaker 4 | from typing import Generator, Any 5 | 6 | import pytest 7 | import sys 8 | import os 9 | 10 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 11 | 12 | from models import Base, User 13 | from database import get_db 14 | from main import app 15 | from config import settings 16 | from schemas import UserCreate 17 | from hashing import Hasher 18 | 19 | SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db" 20 | engine = create_engine( 21 | SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} 22 | ) 23 | TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) 24 | 25 | Base.metadata.create_all(bind=engine) 26 | 27 | 28 | @pytest.fixture 29 | def client() -> Generator[TestClient, Any, None]: 30 | def override_get_db(): 31 | try: 32 | db = TestingSessionLocal() 33 | yield db 34 | finally: 35 | db.close() 36 | 37 | app.dependency_overrides[get_db] = override_get_db 38 | client = TestClient(app) 39 | yield client 40 | 41 | 42 | @pytest.fixture 43 | def token_headers(client: TestClient): 44 | test_email = settings.TEST_EMAIL 45 | test_password = settings.TEST_PASS 46 | db = TestingSessionLocal() 47 | user = db.query(User).filter(User.email == test_email).first() 48 | if not user: 49 | user_schema = UserCreate(email=test_email, password=test_password) 50 | user = User( 51 | email=user_schema.email, 52 | password=Hasher.get_hash_password(user_schema.password), 53 | ) 54 | db.add(user) 55 | db.commit() 56 | db.refresh(user) 57 | data = {"username": test_email, "password": test_password} 58 | response = client.post("/login/token", data=data) 59 | token = response.json()["access_token"] 60 | return {"Authorization": f"Bearer {token}"} 61 | -------------------------------------------------------------------------------- /backend/tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from typing import Generator, Any 3 | from fastapi import FastAPI 4 | from sqlalchemy import create_engine 5 | from sqlalchemy.orm import sessionmaker, Session 6 | from fastapi.testclient import TestClient 7 | 8 | 9 | import sys 10 | import os 11 | 12 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 13 | 14 | from db.base import Base 15 | from db.session import get_db 16 | from apis.base import api_router 17 | from core.config import settings 18 | from tests.utils.user import authentication_token_from_email 19 | 20 | 21 | def start_application(): 22 | app = FastAPI() 23 | app.include_router(api_router) 24 | return app 25 | 26 | 27 | SQLALCHEMY_DATABASE_URL = "sqlite:///./test_db.db" 28 | 29 | engine = create_engine( 30 | SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} 31 | ) 32 | SessionTesting = sessionmaker(autocommit=False, autoflush=False, bind=engine) 33 | 34 | 35 | @pytest.fixture(scope="module") 36 | def app() -> Generator[FastAPI, Any, None]: 37 | Base.metadata.create_all(bind=engine) 38 | _app = start_application() 39 | yield _app 40 | Base.metadata.drop_all(bind=engine) 41 | 42 | 43 | @pytest.fixture(scope="module") 44 | def db_session(app: FastAPI) -> Generator[SessionTesting, Any, None]: 45 | connection = engine.connect() 46 | transaction = connection.begin() 47 | session = SessionTesting(bind=connection) 48 | yield session 49 | session.close() 50 | transaction.rollback() 51 | connection.close() 52 | 53 | 54 | @pytest.fixture(scope="module") 55 | def client( 56 | app: FastAPI, db_session: SessionTesting 57 | ) -> Generator[TestClient, Any, None]: 58 | def _get_test_db(): 59 | try: 60 | yield db_session 61 | finally: 62 | pass 63 | 64 | app.dependency_overrides[get_db] = _get_test_db 65 | with TestClient(app) as client: 66 | yield client 67 | 68 | 69 | @pytest.fixture(scope="module") 70 | def normal_user_token_headers(client: TestClient, db_session: Session): 71 | return authentication_token_from_email( 72 | client=client, email=settings.TEST_USER_EMAIL, db=db_session 73 | ) 74 | -------------------------------------------------------------------------------- /learn/templates/navbar.html: -------------------------------------------------------------------------------- 1 | 46 | -------------------------------------------------------------------------------- /backend/templates/components/navbar.html: -------------------------------------------------------------------------------- 1 | 46 | -------------------------------------------------------------------------------- /learn/migrations/env.py: -------------------------------------------------------------------------------- 1 | from logging.config import fileConfig 2 | 3 | from sqlalchemy import engine_from_config 4 | from sqlalchemy import pool 5 | 6 | from alembic import context 7 | 8 | # this is the Alembic Config object, which provides 9 | # access to the values within the .ini file in use. 10 | config = context.config 11 | 12 | # Interpret the config file for Python logging. 13 | # This line sets up loggers basically. 14 | fileConfig(config.config_file_name) 15 | 16 | # add your model's MetaData object here 17 | # for 'autogenerate' support 18 | # from myapp import mymodel 19 | # target_metadata = mymodel.Base.metadata 20 | from models import Base 21 | 22 | target_metadata = Base.metadata 23 | 24 | # other values from the config, defined by the needs of env.py, 25 | # can be acquired: 26 | # my_important_option = config.get_main_option("my_important_option") 27 | # ... etc. 28 | 29 | 30 | def run_migrations_offline(): 31 | """Run migrations in 'offline' mode. 32 | 33 | This configures the context with just a URL 34 | and not an Engine, though an Engine is acceptable 35 | here as well. By skipping the Engine creation 36 | we don't even need a DBAPI to be available. 37 | 38 | Calls to context.execute() here emit the given string to the 39 | script output. 40 | 41 | """ 42 | url = config.get_main_option("sqlalchemy.url") 43 | context.configure( 44 | url=url, 45 | target_metadata=target_metadata, 46 | literal_binds=True, 47 | dialect_opts={"paramstyle": "named"}, 48 | ) 49 | 50 | with context.begin_transaction(): 51 | context.run_migrations() 52 | 53 | 54 | def run_migrations_online(): 55 | """Run migrations in 'online' mode. 56 | 57 | In this scenario we need to create an Engine 58 | and associate a connection with the context. 59 | 60 | """ 61 | connectable = engine_from_config( 62 | config.get_section(config.config_ini_section), 63 | prefix="sqlalchemy.", 64 | poolclass=pool.NullPool, 65 | ) 66 | 67 | with connectable.connect() as connection: 68 | context.configure(connection=connection, target_metadata=target_metadata) 69 | 70 | with context.begin_transaction(): 71 | context.run_migrations() 72 | 73 | 74 | if context.is_offline_mode(): 75 | run_migrations_offline() 76 | else: 77 | run_migrations_online() 78 | -------------------------------------------------------------------------------- /backend/alembic/env.py: -------------------------------------------------------------------------------- 1 | from logging.config import fileConfig 2 | 3 | from sqlalchemy import engine_from_config 4 | from sqlalchemy import pool 5 | 6 | from alembic import context 7 | 8 | # this is the Alembic Config object, which provides 9 | # access to the values within the .ini file in use. 10 | config = context.config 11 | 12 | # Interpret the config file for Python logging. 13 | # This line sets up loggers basically. 14 | fileConfig(config.config_file_name) 15 | 16 | # add your model's MetaData object here 17 | # for 'autogenerate' support 18 | # from myapp import mymodel 19 | # target_metadata = mymodel.Base.metadata 20 | from db.base_class import Base 21 | 22 | target_metadata = Base.metadata 23 | 24 | # other values from the config, defined by the needs of env.py, 25 | # can be acquired: 26 | # my_important_option = config.get_main_option("my_important_option") 27 | # ... etc. 28 | 29 | 30 | def run_migrations_offline(): 31 | """Run migrations in 'offline' mode. 32 | 33 | This configures the context with just a URL 34 | and not an Engine, though an Engine is acceptable 35 | here as well. By skipping the Engine creation 36 | we don't even need a DBAPI to be available. 37 | 38 | Calls to context.execute() here emit the given string to the 39 | script output. 40 | 41 | """ 42 | url = config.get_main_option("sqlalchemy.url") 43 | context.configure( 44 | url=url, 45 | target_metadata=target_metadata, 46 | literal_binds=True, 47 | dialect_opts={"paramstyle": "named"}, 48 | ) 49 | 50 | with context.begin_transaction(): 51 | context.run_migrations() 52 | 53 | 54 | def run_migrations_online(): 55 | """Run migrations in 'online' mode. 56 | 57 | In this scenario we need to create an Engine 58 | and associate a connection with the context. 59 | 60 | """ 61 | connectable = engine_from_config( 62 | config.get_section(config.config_ini_section), 63 | prefix="sqlalchemy.", 64 | poolclass=pool.NullPool, 65 | ) 66 | 67 | with connectable.connect() as connection: 68 | context.configure(connection=connection, target_metadata=target_metadata) 69 | 70 | with context.begin_transaction(): 71 | context.run_migrations() 72 | 73 | 74 | if context.is_offline_mode(): 75 | run_migrations_offline() 76 | else: 77 | run_migrations_online() 78 | -------------------------------------------------------------------------------- /backend/apis/version1/route_login.py: -------------------------------------------------------------------------------- 1 | from fastapi.security import OAuth2PasswordRequestForm, OAuth2PasswordBearer 2 | from apis.utils import OAuth2PasswordBearerWithCookie 3 | from fastapi import Depends 4 | from fastapi import APIRouter, HTTPException, status 5 | from db.session import get_db 6 | from sqlalchemy.orm import Session 7 | from core.config import settings 8 | from datetime import timedelta 9 | from core.security import create_access_token 10 | from db.repository.login import get_user 11 | from core.hashing import Hasher 12 | from jose import jwt, JWTError 13 | from fastapi import Response 14 | 15 | router = APIRouter() 16 | 17 | 18 | def authenticate_user(username: str, password: str, db: Session): 19 | user = get_user(username=username, db=db) 20 | if not user: 21 | return False 22 | if not Hasher.verify_password(password, user.hashed_password): 23 | return False 24 | return user 25 | 26 | 27 | @router.post("/token") 28 | def login_for_access_token( 29 | response: Response, 30 | form_data: OAuth2PasswordRequestForm = Depends(), 31 | db: Session = Depends(get_db), 32 | ): 33 | user = authenticate_user(form_data.username, form_data.password, db) 34 | if not user: 35 | raise HTTPException( 36 | status_code=status.HTTP_401_UNAUTHORIZED, 37 | detail="Invalid username or password", 38 | ) 39 | access_token_expire = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) 40 | access_token = create_access_token( 41 | data={"sub": user.email}, expires_delta=access_token_expire 42 | ) 43 | response.set_cookie( 44 | key="access_token", value=f"Bearer {access_token}", httponly=True 45 | ) 46 | return {"access_token": access_token, "token_type": "bearer"} 47 | 48 | 49 | oauth2_scheme = OAuth2PasswordBearerWithCookie(tokenUrl="/login/token") 50 | 51 | 52 | def get_current_user_from_token( 53 | token: str = Depends(oauth2_scheme), db: Session = Depends(get_db) 54 | ): 55 | credentials_exception = HTTPException( 56 | status_code=status.HTTP_401_UNAUTHORIZED, 57 | detail="Could not validate credentials", 58 | ) 59 | try: 60 | payload = jwt.decode( 61 | token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM] 62 | ) 63 | username: str = payload.get("sub") 64 | if username is None: 65 | raise credentials_exception 66 | except JWTError: 67 | raise credentials_exception 68 | user = get_user(username=username, db=db) 69 | if user is None: 70 | raise credentials_exception 71 | return user 72 | -------------------------------------------------------------------------------- /backend/templates/jobs/update_job.html: -------------------------------------------------------------------------------- 1 | {% extends 'shared/base.html' %} 2 | 3 | {% block title %} 4 | Update a Job Post 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
    9 |

    Update a Job Post

    10 | {% for error in errors %} 11 | 14 | {% endfor %} 15 |
    16 |
    17 | 18 | 19 |
    20 |
    21 | 22 | 23 |
    24 |
    25 | 26 | 27 |
    28 |
    29 | 30 | 31 |
    32 |
    33 | 34 | 35 |
    Please provide complete Job description,requirements,perks and benefits.
    36 |
    37 | 38 |
    39 | 40 |
    41 | {% endblock %} 42 | 43 | {% block script %} 44 | 60 | {% endblock %} 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /learn/webapps/routers/auth.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter, Request, Depends, status, Response 2 | from fastapi.templating import Jinja2Templates 3 | from models import User 4 | from sqlalchemy.orm import Session 5 | from database import get_db 6 | from hashing import Hasher 7 | from jose import jwt 8 | from config import settings 9 | 10 | router = APIRouter(include_in_schema=False) 11 | templates = Jinja2Templates(directory="templates") 12 | 13 | 14 | @router.get("/login") 15 | def login(request: Request): 16 | return templates.TemplateResponse("login.html", {"request": request}) 17 | 18 | 19 | @router.post("/login") 20 | async def login(response: Response, request: Request, db: Session = Depends(get_db)): 21 | form = await request.form() 22 | email = form.get("email") 23 | password = form.get("password") 24 | errors = [] 25 | if not email: 26 | errors.append("Please Enter valid Email") 27 | if not password: 28 | errros.append("Password enter password") 29 | if len(errors) > 0: 30 | return templates.TemplateResponse( 31 | "login.html", {"request": request, "errors": errors} 32 | ) 33 | try: 34 | user = db.query(User).filter(User.email == email).first() 35 | if user is None: 36 | errors.append("Email does not exists") 37 | return templates.TemplateResponse( 38 | "login.html", {"request": request, "errors": errors} 39 | ) 40 | else: 41 | if Hasher.verify_password(password, user.password): 42 | data = {"sub": email} 43 | jwt_token = jwt.encode( 44 | data, settings.SECRET_KEY, algorithm=settings.ALGORITHM 45 | ) 46 | # if we redirect response in below way, it will not set the cookie 47 | # return responses.RedirectResponse("/?msg=Login Successfull", status_code=status.HTTP_302_FOUND) 48 | msg = "Login Successful" 49 | response = templates.TemplateResponse( 50 | "login.html", {"request": request, "msg": msg} 51 | ) 52 | response.set_cookie( 53 | key="access_token", value=f"Bearer {jwt_token}", httponly=True 54 | ) 55 | return response 56 | else: 57 | errors.append("Invalid Password") 58 | return templates.TemplateResponse( 59 | "login.html", {"request": request, "errors": errors} 60 | ) 61 | except: 62 | errors.append("Something Wrong while authentication or storing tokens!") 63 | return templates.TemplateResponse( 64 | "login.html", {"request": request, "errors": errors} 65 | ) 66 | -------------------------------------------------------------------------------- /backend/apis/version1/route_jobs.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter, Depends, HTTPException, status 2 | from sqlalchemy.orm import Session 3 | from db.session import get_db 4 | from db.models.jobs import Job 5 | from schemas.jobs import JobCreate, ShowJob 6 | from db.repository.jobs import ( 7 | create_new_job, 8 | retrieve_job, 9 | list_jobs, 10 | update_job_by_id, 11 | delete_job_by_id, 12 | search_job, 13 | ) 14 | from typing import List, Optional 15 | from apis.version1.route_login import get_current_user_from_token 16 | from db.models.users import User 17 | 18 | router = APIRouter() 19 | 20 | 21 | @router.post("/create-job", response_model=ShowJob) 22 | def create_job( 23 | job: JobCreate, 24 | db: Session = Depends(get_db), 25 | current_user: User = Depends(get_current_user_from_token), 26 | ): 27 | owner_id = current_user.id 28 | job = create_new_job(job=job, db=db, owner_id=owner_id) 29 | return job 30 | 31 | 32 | @router.get("/get/{id}", response_model=ShowJob) 33 | def retrieve_job_by_id(id: int, db: Session = Depends(get_db)): 34 | job = retrieve_job(id=id, db=db) 35 | if not job: 36 | raise HTTPException( 37 | status_code=status.HTTP_404_NOT_FOUND, 38 | detail=f"Job with id {id} does not exists", 39 | ) 40 | return job 41 | 42 | 43 | @router.get("/all", response_model=List[ShowJob]) 44 | def retrieve_all_jobs(db: Session = Depends(get_db)): 45 | jobs = list_jobs(db=db) 46 | return jobs 47 | 48 | 49 | @router.put("/update/{id}") 50 | def update_job( 51 | id: int, 52 | job: JobCreate, 53 | db: Session = Depends(get_db), 54 | current_user: User = Depends(get_current_user_from_token), 55 | ): 56 | job = retrieve_job(id=id, db=db) 57 | if not job: 58 | raise HTTPException( 59 | status_code=status.HTTP_404_NOT_FOUND, 60 | detail=f"Job with id {id} does not exists", 61 | ) 62 | if job.owner_id == current_user.id or current_user.is_superuser: 63 | message = update_job_by_id(id=id, job=job, db=db, owner_id=job.owner_id) 64 | raise HTTPException( 65 | status_code=status.HTTP_401_UNAUTHORIZED, detail="You are not permitted" 66 | ) 67 | 68 | 69 | @router.delete("/delete/{id}") 70 | def delete_job( 71 | id: int, 72 | db: Session = Depends(get_db), 73 | current_user: User = Depends(get_current_user_from_token), 74 | ): 75 | job = retrieve_job(id=id, db=db) 76 | if not job: 77 | raise HTTPException( 78 | status_code=status.HTTP_404_NOT_FOUND, 79 | detail=f"Job with id {id} does not exists", 80 | ) 81 | if job.owner_id == current_user.id or current_user.is_superuser: 82 | delete_job_by_id(id=id, db=db, owner_id=current_user.id) 83 | return {"message": "Job Successfully deleted"} 84 | raise HTTPException( 85 | status_code=status.HTTP_401_UNAUTHORIZED, detail="You are not permitted" 86 | ) 87 | 88 | 89 | @router.get("/autocomplete") 90 | def autocomplete(term: Optional[str] = None, db: Session = Depends(get_db)): 91 | jobs = search_job(term, db=db) 92 | job_titles = [] 93 | for job in jobs: 94 | job_titles.append(job.title) 95 | return job_titles 96 | -------------------------------------------------------------------------------- /backend/alembic.ini: -------------------------------------------------------------------------------- 1 | # A generic, single database configuration. 2 | 3 | [alembic] 4 | # path to migration scripts 5 | script_location = alembic 6 | 7 | # template used to generate migration files 8 | # file_template = %%(rev)s_%%(slug)s 9 | 10 | # sys.path path, will be prepended to sys.path if present. 11 | # defaults to the current working directory. 12 | prepend_sys_path = . 13 | 14 | # timezone to use when rendering the date within the migration file 15 | # as well as the filename. 16 | # If specified, requires the python-dateutil library that can be 17 | # installed by adding `alembic[tz]` to the pip requirements 18 | # string value is passed to dateutil.tz.gettz() 19 | # leave blank for localtime 20 | # timezone = 21 | 22 | # max length of characters to apply to the 23 | # "slug" field 24 | # truncate_slug_length = 40 25 | 26 | # set to 'true' to run the environment during 27 | # the 'revision' command, regardless of autogenerate 28 | # revision_environment = false 29 | 30 | # set to 'true' to allow .pyc and .pyo files without 31 | # a source .py file to be detected as revisions in the 32 | # versions/ directory 33 | # sourceless = false 34 | 35 | # version location specification; This defaults 36 | # to alembic/versions. When using multiple version 37 | # directories, initial revisions must be specified with --version-path. 38 | # The path separator used here should be the separator specified by "version_path_separator" 39 | # version_locations = %(here)s/bar:%(here)s/bat:alembic/versions 40 | 41 | # version path separator; As mentioned above, this is the character used to split 42 | # version_locations. Valid values are: 43 | # 44 | # version_path_separator = : 45 | # version_path_separator = ; 46 | # version_path_separator = space 47 | version_path_separator = os # default: use os.pathsep 48 | 49 | # the output encoding used when revision files 50 | # are written from script.py.mako 51 | # output_encoding = utf-8 52 | 53 | #sqlalchemy.url = driver://user:pass@localhost/dbname 54 | sqlalchemy.url = postgresql://postgres:postgres@localhost:5432/jobcard 55 | 56 | [post_write_hooks] 57 | # post_write_hooks defines scripts or Python functions that are run 58 | # on newly generated revision scripts. See the documentation for further 59 | # detail and examples 60 | 61 | # format using "black" - use the console_scripts runner, against the "black" entrypoint 62 | # hooks = black 63 | # black.type = console_scripts 64 | # black.entrypoint = black 65 | # black.options = -l 79 REVISION_SCRIPT_FILENAME 66 | 67 | # Logging configuration 68 | [loggers] 69 | keys = root,sqlalchemy,alembic 70 | 71 | [handlers] 72 | keys = console 73 | 74 | [formatters] 75 | keys = generic 76 | 77 | [logger_root] 78 | level = WARN 79 | handlers = console 80 | qualname = 81 | 82 | [logger_sqlalchemy] 83 | level = WARN 84 | handlers = 85 | qualname = sqlalchemy.engine 86 | 87 | [logger_alembic] 88 | level = INFO 89 | handlers = 90 | qualname = alembic 91 | 92 | [handler_console] 93 | class = StreamHandler 94 | args = (sys.stderr,) 95 | level = NOTSET 96 | formatter = generic 97 | 98 | [formatter_generic] 99 | format = %(levelname)-5.5s [%(name)s] %(message)s 100 | datefmt = %H:%M:%S 101 | -------------------------------------------------------------------------------- /learn/alembic.ini: -------------------------------------------------------------------------------- 1 | # A generic, single database configuration. 2 | 3 | [alembic] 4 | # path to migration scripts 5 | script_location = migrations 6 | 7 | # template used to generate migration files 8 | # file_template = %%(rev)s_%%(slug)s 9 | 10 | # sys.path path, will be prepended to sys.path if present. 11 | # defaults to the current working directory. 12 | prepend_sys_path = . 13 | 14 | # timezone to use when rendering the date within the migration file 15 | # as well as the filename. 16 | # If specified, requires the python-dateutil library that can be 17 | # installed by adding `alembic[tz]` to the pip requirements 18 | # string value is passed to dateutil.tz.gettz() 19 | # leave blank for localtime 20 | # timezone = 21 | 22 | # max length of characters to apply to the 23 | # "slug" field 24 | # truncate_slug_length = 40 25 | 26 | # set to 'true' to run the environment during 27 | # the 'revision' command, regardless of autogenerate 28 | # revision_environment = false 29 | 30 | # set to 'true' to allow .pyc and .pyo files without 31 | # a source .py file to be detected as revisions in the 32 | # versions/ directory 33 | # sourceless = false 34 | 35 | # version location specification; This defaults 36 | # to migrations/versions. When using multiple version 37 | # directories, initial revisions must be specified with --version-path. 38 | # The path separator used here should be the separator specified by "version_path_separator" 39 | # version_locations = %(here)s/bar:%(here)s/bat:migrations/versions 40 | 41 | # version path separator; As mentioned above, this is the character used to split 42 | # version_locations. Valid values are: 43 | # 44 | # version_path_separator = : 45 | # version_path_separator = ; 46 | # version_path_separator = space 47 | version_path_separator = os # default: use os.pathsep 48 | 49 | # the output encoding used when revision files 50 | # are written from script.py.mako 51 | # output_encoding = utf-8 52 | 53 | #sqlalchemy.url = driver://user:pass@localhost/dbname 54 | sqlalchemy.url = postgresql://postgres:postgres@localhost:5432/testing 55 | 56 | [post_write_hooks] 57 | # post_write_hooks defines scripts or Python functions that are run 58 | # on newly generated revision scripts. See the documentation for further 59 | # detail and examples 60 | 61 | # format using "black" - use the console_scripts runner, against the "black" entrypoint 62 | # hooks = black 63 | # black.type = console_scripts 64 | # black.entrypoint = black 65 | # black.options = -l 79 REVISION_SCRIPT_FILENAME 66 | 67 | # Logging configuration 68 | [loggers] 69 | keys = root,sqlalchemy,alembic 70 | 71 | [handlers] 72 | keys = console 73 | 74 | [formatters] 75 | keys = generic 76 | 77 | [logger_root] 78 | level = WARN 79 | handlers = console 80 | qualname = 81 | 82 | [logger_sqlalchemy] 83 | level = WARN 84 | handlers = 85 | qualname = sqlalchemy.engine 86 | 87 | [logger_alembic] 88 | level = INFO 89 | handlers = 90 | qualname = alembic 91 | 92 | [handler_console] 93 | class = StreamHandler 94 | args = (sys.stderr,) 95 | level = NOTSET 96 | formatter = generic 97 | 98 | [formatter_generic] 99 | format = %(levelname)-5.5s [%(name)s] %(message)s 100 | datefmt = %H:%M:%S 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## This repository contains the FAST-API Learning Documentation, Code 2 | ## 3 | 4 | ## YouTube Videos for explanation of all available code 5 | [You Tube - FastAPI Videos](https://www.youtube.com/playlist?list=PLaNsxqNgctlM0CEzKBidDbYVmNsoBK8Ss "You Tube - Fast API") 6 | 7 | ## Credits to Sourabh Sinha for Udemy course 8 | Course Name: FastAPI Full Stack Web Development (API + Webapp) 9 | 10 | ## How to change the Default password for 'postgres' 11 | - su root 12 | - su postgres 13 | - psql 14 | - ALTER USER postgres PASSWORD 'postgres'; 15 | - \q 16 | 17 | ## Some postgres commands 18 | - \l (To list all databases) 19 | 20 | ## How to check and restart postgresql service in Ubuntu 21 | - ```sudo systemctl status postgresql``` (to check status of postgresql service) 22 | - ```sudo service postgresql restart``` (to restart the postgresql service) 23 | 24 | 25 | ## Commands/Steps for the SQLAlchemy alembic 26 | - ```alembic init migrations``` ('migrations' is the folder name which I want, we can gave any name) 27 | 28 | - Update the alembic.ini file with the database url in the key : sqlalchemy.url 29 | 30 | > a) if we want to write migrations scripts manually - then directly issue : ```alembic revision -m "Initial"``` 31 | 32 | > b) if we want to generate migrations scripts automatically - then perform below steps 33 | 34 | >> b.1) Update the env.py file (in migrations folder) with target_metdata 35 | 36 | >> b.2) Issue : ```alembic revision --autogenerate -m "Initial"``` 37 | 38 | - Last step is to apply the migrations scripts by command : ```alembic upgrade head``` 39 | (head is basically pointing to the current version) 40 | 41 | 42 | ## If we are using alembic 43 | - Then comment the Base.metdata.create_all(bind=engine) line in main.py 44 | Because, now tables will get created using alembic 45 | 46 | 47 | ## Steps to set connectivity between Docker Container (Web-app) with Postgres Database (On Localhost) 48 | 49 | >Step1: Update postgresql configuration file, to allow all remote connection access 50 | 51 | ```sudo nano /etc/postgresql/12/main/postgresql.conf``` 52 | 53 | Find the line "listen_address" and uncomment it , also change from localhost to '*', So after change it should look like this 54 | listen_address = '*' 55 | 56 | >Step2: Update postgres hba configuration file, to allow docker container connection to host database 57 | 58 | ```sudo nano /etc/postgresql/12/main/pg_hba.conf``` 59 | 60 | #### host db_name user_name docker_ip/16 trust 61 | ```host testing postgres 172.17.0.0/16 trust``` 62 | 63 | >Step3: Restart the postgres service 64 | 65 | ```sudo /etc/init.d/postgresql restart``` 66 | 67 | >Step4: Build the docker image 68 | 69 | ```docker build --network=host -t myappimage . ``` 70 | 71 | >Step5: Run the container 72 | 73 | ```docker run -d --network=host -e "DB_DBNAME=testing" -e "DB_PORT=5432" -e "DB_USER=postgres" -e "DB_PASS=postgres" -e "DB_HOST=127.0.0.1" --name myappcontainer myappimage``` 74 | 75 | ## Deploy using docker-compose 76 | 77 | > Check the version of docker-compose : ```docker-compose -v``` 78 | 79 | > Step1 : Create the docker-compose.yml file 80 | 81 | > Step2 : To Build and Start the Container, issue 82 | 83 | ```docker-compose up -d``` 84 | 85 | > Step3 : To Stop all the container/services, issue 86 | 87 | ```docker-compose down``` 88 | -------------------------------------------------------------------------------- /backend/webapps/jobs/route_jobs.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter, Depends 2 | from fastapi import Request, status, responses 3 | from fastapi.security.utils import get_authorization_scheme_param 4 | from fastapi.responses import HTMLResponse 5 | from fastapi.templating import Jinja2Templates 6 | from db.repository.jobs import list_jobs, search_job 7 | from db.repository.jobs import retrieve_job, create_new_job 8 | from sqlalchemy.orm import Session 9 | from db.session import get_db 10 | from db.models.users import User 11 | from apis.version1.route_login import get_current_user_from_token 12 | from webapps.jobs.forms import JobCreateForm 13 | from schemas.jobs import JobCreate 14 | from typing import Optional 15 | 16 | templates = Jinja2Templates(directory="templates") 17 | router = APIRouter() 18 | 19 | 20 | @router.get("/") 21 | def home(request: Request, db: Session = Depends(get_db), msg: str = None): 22 | jobs = list_jobs(db=db) 23 | return templates.TemplateResponse( 24 | "jobs/homepage.html", {"request": request, "jobs": jobs, "msg": msg} 25 | ) 26 | 27 | 28 | @router.get("/detail/{id}") 29 | def job_detail(id: int, request: Request, db: Session = Depends(get_db)): 30 | job = retrieve_job(id=id, db=db) 31 | return templates.TemplateResponse( 32 | "jobs/detail.html", {"request": request, "job": job} 33 | ) 34 | 35 | 36 | @router.get("/post-a-job") 37 | def create_job(request: Request): 38 | return templates.TemplateResponse("jobs/create_job.html", {"request": request}) 39 | 40 | 41 | @router.post("/post-a-job") 42 | async def create_job(request: Request, db: Session = Depends(get_db)): 43 | form = JobCreateForm(request) 44 | await form.load_data() 45 | if form.is_valid(): 46 | try: 47 | token = request.cookies.get("access_token") 48 | scheme, param = get_authorization_scheme_param(token) 49 | current_user: User = get_current_user_from_token(token=param, db=db) 50 | job = JobCreate(**form.__dict__) 51 | job = create_new_job(job=job, db=db, owner_id=current_user.id) 52 | return responses.RedirectResponse( 53 | f"/detail/{job.id}", status_code=status.HTTP_302_FOUND 54 | ) 55 | except Exception as e: 56 | print(e) 57 | form.__dict__.get("errors").append( 58 | "You might not be logged in,In case problem persists, please contact us." 59 | ) 60 | return templates.TemplateResponse("jobs/create_job.html", form.__dict__) 61 | return templates.TemplateResponse("jobs/create_job.html", form.__dict__) 62 | 63 | 64 | @router.get("/update-delete-job") 65 | def show_jobs_to_delete(request: Request, db: Session = Depends(get_db)): 66 | jobs = list_jobs(db=db) 67 | return templates.TemplateResponse( 68 | "jobs/show_jobs_to_update_delete.html", {"request": request, "jobs": jobs} 69 | ) 70 | 71 | 72 | @router.get("/updatejob/{id}") 73 | def updatejob(id: int, request: Request, db: Session = Depends(get_db)): 74 | job = retrieve_job(id=id, db=db) 75 | return templates.TemplateResponse( 76 | "jobs/update_job.html", {"request": request, "job": job} 77 | ) 78 | 79 | 80 | @router.get("/search/") 81 | def search(query: Optional[str], request: Request, db: Session = Depends(get_db)): 82 | jobs = search_job(query, db=db) 83 | return templates.TemplateResponse( 84 | "jobs/homepage.html", {"request": request, "jobs": jobs} 85 | ) 86 | -------------------------------------------------------------------------------- /learn/routers/items.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter, Depends, HTTPException, status 2 | from sqlalchemy.orm import Session 3 | from schemas import ItemCreate, ShowItem 4 | from models import Items, User 5 | from database import get_db 6 | from datetime import datetime 7 | from typing import List, Optional 8 | from fastapi.encoders import jsonable_encoder 9 | from routers.login import oauth2_scheme 10 | from jose import jwt 11 | from config import settings 12 | 13 | router = APIRouter() 14 | 15 | 16 | def get_user_from_token(db, token): 17 | try: 18 | payload = jwt.decode(token, settings.SECRET_KEY, settings.ALGORITHM) 19 | username = payload.get("sub") 20 | if not username: 21 | raise HTTPException( 22 | status_code=status.HTTP_401_UNAUTHORIZED, 23 | detail="Could not validate Credentials", 24 | ) 25 | except: 26 | raise HTTPException( 27 | status_code=status.HTTP_401_UNAUTHORIZED, 28 | detail="Could not validate Credetials", 29 | ) 30 | user = db.query(User).filter(User.email == username).first() 31 | if user is None: 32 | raise HTTPException( 33 | status_code=status.HTP_401_UNAUTHORIZED, 34 | detail="Could not validate credentials", 35 | ) 36 | return user 37 | 38 | 39 | @router.post("/item", tags=["item"], response_model=ShowItem) 40 | def create_item( 41 | item: ItemCreate, db: Session = Depends(get_db), token: str = Depends(oauth2_scheme) 42 | ): 43 | user = get_user_from_token(db, token) 44 | owner_id = user.id 45 | item = Items(**item.dict(), date_posted=datetime.now().date(), owner_id=owner_id) 46 | db.add(item) 47 | db.commit() 48 | db.refresh(item) 49 | return item 50 | 51 | 52 | @router.get("/item/all", tags=["item"], response_model=List[ShowItem]) 53 | def get_all_items(db: Session = Depends(get_db)): 54 | items = db.query(Items).all() 55 | return items 56 | 57 | 58 | @router.get("/item/autocomplete") 59 | def autocomplete(term: Optional[str] = None, db: Session = Depends(get_db)): 60 | items = db.query(Items).filter(Items.title.contains(term)).all() 61 | suggestions = [] 62 | for item in items: 63 | suggestions.append(item.title) 64 | return suggestions 65 | 66 | 67 | @router.get("/item/{id}", tags=["item"], response_model=ShowItem) 68 | def get_item_by_id(id: int, db: Session = Depends(get_db)): 69 | item = db.query(Items).filter(Items.id == id).first() 70 | if not item: 71 | raise HTTPException( 72 | status_code=status.HTTP_404_NOT_FOUND, 73 | detail=f"Item with {id} does not exists", 74 | ) 75 | return item 76 | 77 | 78 | # Method-1 79 | @router.put("/item/update/{id}", tags=["item"]) 80 | def update_item_by_id( 81 | id: int, 82 | item: ItemCreate, 83 | db: Session = Depends(get_db), 84 | token: str = Depends(oauth2_scheme), 85 | ): 86 | user = get_user_from_token(db, token) 87 | existing_item = db.query(Items).filter(Items.id == id) 88 | if not existing_item.first(): 89 | return {"message": f"No Details found for Item ID {id}"} 90 | if existing_item.first().owner_id == user.id: 91 | existing_item.update(jsonable_encoder(item)) 92 | db.commit() 93 | return {"message": f"Details successfully updated for Item ID {id}"} 94 | else: 95 | return {"message": "You are not authorized"} 96 | 97 | 98 | # Method-2 99 | @router.put("/item/update1/{id}", tags=["item"]) 100 | def update1_item_by_id( 101 | id: int, 102 | item: ItemCreate, 103 | db: Session = Depends(get_db), 104 | token: str = Depends(oauth2_scheme), 105 | ): 106 | user = get_user_from_token(db, token) 107 | existing_item = db.query(Items).filter(Items.id == id) 108 | if not existing_item.first(): 109 | return {"message": f"No Details found for Item ID {id}"} 110 | if existing_item.first().owner_id == user.id: 111 | existing_item.update(item.__dict__) 112 | db.commit() 113 | return {"message": f"Details successfully updated for Item ID {id}"} 114 | else: 115 | return {"message": "You are not authorized"} 116 | 117 | 118 | @router.delete("/item/delete/{id}", tags=["item"]) 119 | def delete_item_by_id( 120 | id: int, db: Session = Depends(get_db), token: str = Depends(oauth2_scheme) 121 | ): 122 | user = get_user_from_token(db, token) 123 | existing_item = db.query(Items).filter(Items.id == id) 124 | if not existing_item.first(): 125 | return {"message": f"No Details found for Item ID {id}"} 126 | if existing_item.first().owner_id == user.id: 127 | existing_item.delete() 128 | db.commit() 129 | return {"message": f"Item ID {id} has been successfully deleted"} 130 | else: 131 | return {"message": "You are not authorized"} 132 | -------------------------------------------------------------------------------- /learn/webapps/routers/items.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter, Request, Depends, responses, status 2 | from fastapi.templating import Jinja2Templates 3 | from models import Items, User 4 | from sqlalchemy.orm import Session 5 | from database import get_db 6 | from jose import jwt 7 | from config import settings 8 | from datetime import datetime 9 | from typing import Optional 10 | 11 | router = APIRouter(include_in_schema=False) 12 | templates = Jinja2Templates(directory="templates") 13 | 14 | 15 | @router.get("/", tags=["HomePage"]) 16 | def home_page(request: Request, db: Session = Depends(get_db), msg: str = None): 17 | items = db.query(Items).all() 18 | return templates.TemplateResponse( 19 | "item_homepage.html", {"request": request, "items": items, "msg": msg} 20 | ) 21 | 22 | 23 | @router.get("/detail/{id}") 24 | def item_detail(request: Request, id: int, db: Session = Depends(get_db)): 25 | item = db.query(Items).filter(Items.id == id).first() 26 | email = db.query(User).filter(User.id == item.owner_id).first().email 27 | return templates.TemplateResponse( 28 | "item_detail.html", {"request": request, "item": item, "email": email} 29 | ) 30 | 31 | 32 | @router.get("/update/{id}") 33 | def update_item(id: int, request: Request, db: Session = Depends(get_db)): 34 | item = db.query(Items).filter(Items.id == id).first() 35 | return templates.TemplateResponse( 36 | "update_item.html", {"request": request, "item": item} 37 | ) 38 | 39 | 40 | @router.get("/create-an-item") 41 | def create_an_item(request: Request): 42 | return templates.TemplateResponse("create_item.html", {"request": request}) 43 | 44 | 45 | @router.post("/create-an-item") 46 | async def create_an_item(request: Request, db: Session = Depends(get_db)): 47 | form = await request.form() 48 | title = form.get("title") 49 | description = form.get("description") 50 | errors = [] 51 | if not title or len(title) < 4: 52 | errors.append("Title should be > 4 chars") 53 | if not description or len(description) < 10: 54 | errors.append("Description should be > 10 chars") 55 | if len(errors) > 0: 56 | return templates.TemplateResponse( 57 | "create_item.html", {"request": request, "errors": errors} 58 | ) 59 | try: 60 | token = request.cookies.get("access_token") 61 | if not token: 62 | errors.append("Kindly Authenticate first by login") 63 | return templates.TemplateResponse( 64 | "create_item.html", {"request": request, "errors": errors} 65 | ) 66 | scheme, _, param = token.partition(" ") 67 | payload = jwt.decode(param, settings.SECRET_KEY, algorithms=settings.ALGORITHM) 68 | email = payload.get("sub") 69 | if email is None: 70 | errors.append("Kindly login first, you are not authenticated") 71 | return templates.TemplateResponse( 72 | "create_item.html", {"request": request, "errors": errors} 73 | ) 74 | else: 75 | user = db.query(User).filter(User.email == email).first() 76 | if user is None: 77 | errors.append("You are not authenticated, Kindly Login") 78 | return templates.TemplateResponse( 79 | "create_item.html", {"request": request, "errors": errros} 80 | ) 81 | else: 82 | item = Items( 83 | title=title, 84 | description=description, 85 | date_posted=datetime.now().date(), 86 | owner_id=user.id, 87 | ) 88 | db.add(item) 89 | db.commit() 90 | db.refresh(item) 91 | print(item.id) 92 | return responses.RedirectResponse( 93 | f"/detail/{item.id}", status_code=status.HTTP_302_FOUND 94 | ) 95 | except Exception as e: 96 | errors.append("Something is wrong !") 97 | print(e) 98 | return templates.TemplateResponse( 99 | "create_item.html", {"request": request, "errors": errors} 100 | ) 101 | 102 | 103 | @router.get("/update-delete-item") 104 | def show_items_to_delete(request: Request, db: Session = Depends(get_db)): 105 | errors = [] 106 | token = request.cookies.get("access_token") 107 | if token is None: 108 | errors.append("Kindly Login/Authenticate") 109 | return templates.TemplateResponse( 110 | "show_items_to_update_delete.html", {"request": request, "errors": errors} 111 | ) 112 | else: 113 | try: 114 | scheme, _, param = token.partition(" ") 115 | payload = jwt.decode( 116 | param, settings.SECRET_KEY, algorithms=settings.ALGORITHM 117 | ) 118 | email = payload.get("sub") 119 | user = db.query(User).filter(User.email == email).first() 120 | items = db.query(Items).filter(Items.owner_id == user.id).all() 121 | return templates.TemplateResponse( 122 | "show_items_to_update_delete.html", {"request": request, "items": items} 123 | ) 124 | except Exception as e: 125 | print(e) 126 | errors.append("Something is wrong!!, May be you are not Authenticated") 127 | return templates.TemplateResponse( 128 | "show_items_to_update_delete.html", 129 | {"request": request, "errors": errors}, 130 | ) 131 | 132 | 133 | @router.get("/search") 134 | def search_jobs(request: Request, query: Optional[str], db: Session = Depends(get_db)): 135 | items = db.query(Items).filter(Items.title.contains(query)).all() 136 | return templates.TemplateResponse( 137 | "item_homepage.html", {"request": request, "items": items} 138 | ) 139 | --------------------------------------------------------------------------------