├── .gitignore ├── README.md ├── back ├── .gitignore ├── alembic.ini ├── app │ ├── auth │ │ ├── crud.py │ │ ├── dependencies.py │ │ ├── helpers.py │ │ ├── models.py │ │ ├── routes.py │ │ └── schemas.py │ ├── database.py │ ├── main.py │ └── settings.py ├── migrations │ ├── env.py │ ├── script.py.mako │ └── versions │ │ └── 2020-12-20_c6151d23daaa_add_users.py ├── poetry.lock └── pyproject.toml ├── docker-compose.yml └── front ├── .gitignore ├── README.md ├── babel.config.js ├── package-lock.json ├── package.json ├── public ├── favicon.ico └── index.html └── src ├── App.vue ├── main.js ├── router └── index.js └── views ├── GithubCallback.vue └── Home.vue /.gitignore: -------------------------------------------------------------------------------- 1 | volumes/ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FastAPI and frontend auth example 2 | 3 | This repository is an example for a FastAPI projet with a frontend, demonstrating how auth with an exernal OAuth provider should work. It is associated to the article [Demystifying authentication with FastAPI and a frontend](https://kernelpanic.io/demystifying-authentication-with-fastapi-and-a-frontend) on Kernel Panic. 4 | 5 | On this example, it is possible to login with Github OAuth app. 6 | 7 | ## Getting started 8 | 9 | To launch this projet, you may need a PostgreSQL database, that is included in the `docker-compose.yml` file. You can launch it with: 10 | 11 | ``` 12 | $ docker-compose up -d 13 | ``` 14 | 15 | ### Backend 16 | 17 | To run the backend, you need to create a virtualenv and use [Poetry](https://github.com/python-poetry/poetry) for dependency management: 18 | 19 | ``` 20 | $ virtualenv venv --python=python3.8 21 | $ source venv/bin/activate 22 | $ poetry install 23 | ``` 24 | 25 | Then, you need to set up your Github client id and secret in a `.env` file: 26 | 27 | ``` 28 | GITHUB_CLIENT_ID= 29 | GITHUB_CLIENT_SECRET= 30 | ``` 31 | 32 | And you can launch the backend with: 33 | 34 | ``` 35 | $ uvicorn app.main:app --reload 36 | ``` 37 | 38 | ### Frontend 39 | 40 | To run the frontend, you need to install the dependencies: 41 | 42 | ``` 43 | $ npm install 44 | ``` 45 | 46 | Then, you can launch it with: 47 | 48 | ``` 49 | $ npm run serve 50 | ``` 51 | 52 | And access it on the [http://localhost:8080](http://localhost:8080) and login ! 53 | 54 | -------------------------------------------------------------------------------- /back/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Python template 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | share/python-wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | MANIFEST 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .nox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | *.py,cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | cover/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | db.sqlite3-journal 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | .pybuilder/ 78 | target/ 79 | 80 | # Jupyter Notebook 81 | .ipynb_checkpoints 82 | 83 | # IPython 84 | profile_default/ 85 | ipython_config.py 86 | 87 | # pyenv 88 | # For a library or package, you might want to ignore these files since the code is 89 | # intended to run in multiple environments; otherwise, check them in: 90 | # .python-version 91 | 92 | # pipenv 93 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 94 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 95 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 96 | # install all needed dependencies. 97 | #Pipfile.lock 98 | 99 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 100 | __pypackages__/ 101 | 102 | # Celery stuff 103 | celerybeat-schedule 104 | celerybeat.pid 105 | 106 | # SageMath parsed files 107 | *.sage.py 108 | 109 | # Environments 110 | .env 111 | .venv 112 | env/ 113 | venv/ 114 | ENV/ 115 | env.bak/ 116 | venv.bak/ 117 | 118 | # Spyder project settings 119 | .spyderproject 120 | .spyproject 121 | 122 | # Rope project settings 123 | .ropeproject 124 | 125 | # mkdocs documentation 126 | /site 127 | 128 | # mypy 129 | .mypy_cache/ 130 | .dmypy.json 131 | dmypy.json 132 | 133 | # Pyre type checker 134 | .pyre/ 135 | 136 | # pytype static type analyzer 137 | .pytype/ 138 | 139 | # Cython debug symbols 140 | cython_debug/ 141 | -------------------------------------------------------------------------------- /back/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 = %%(year)d-%%(month).2d-%%(day).2d_%%(rev)s_%%(slug)s 9 | 10 | # timezone to use when rendering the date 11 | # within the migration file as well as the filename. 12 | # string value is passed to dateutil.tz.gettz() 13 | # leave blank for localtime 14 | # timezone = 15 | 16 | # max length of characters to apply to the 17 | # "slug" field 18 | # truncate_slug_length = 40 19 | 20 | # set to 'true' to run the environment during 21 | # the 'revision' command, regardless of autogenerate 22 | revision_environment = true 23 | 24 | # set to 'true' to allow .pyc and .pyo files without 25 | # a source .py file to be detected as revisions in the 26 | # versions/ directory 27 | # sourceless = false 28 | 29 | # version location specification; this defaults 30 | # to migrations/versions. When using multiple version 31 | # directories, initial revisions must be specified with --version-path 32 | # version_locations = %(here)s/bar %(here)s/bat migrations/versions 33 | 34 | # the output encoding used when revision files 35 | # are written from script.py.mako 36 | # output_encoding = utf-8 37 | 38 | [post_write_hooks] 39 | # post_write_hooks defines scripts or Python functions that are run 40 | # on newly generated revision scripts. See the documentation for further 41 | # detail and examples 42 | 43 | # format using "black" - use the console_scripts runner, against the "black" entrypoint 44 | # hooks=black 45 | # black.type=console_scripts 46 | # black.entrypoint=black 47 | # black.options=-l 79 48 | 49 | # Logging configuration 50 | [loggers] 51 | keys = root,sqlalchemy,alembic 52 | 53 | [handlers] 54 | keys = console 55 | 56 | [formatters] 57 | keys = generic 58 | 59 | [logger_root] 60 | level = WARN 61 | handlers = console 62 | qualname = 63 | 64 | [logger_sqlalchemy] 65 | level = WARN 66 | handlers = 67 | qualname = sqlalchemy.engine 68 | 69 | [logger_alembic] 70 | level = INFO 71 | handlers = 72 | qualname = alembic 73 | 74 | [handler_console] 75 | class = StreamHandler 76 | args = (sys.stderr,) 77 | level = NOTSET 78 | formatter = generic 79 | 80 | [formatter_generic] 81 | format = %(levelname)-5.5s [%(name)s] %(message)s 82 | datefmt = %H:%M:%S 83 | -------------------------------------------------------------------------------- /back/app/auth/crud.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional 2 | 3 | from sqlalchemy.orm import Session 4 | 5 | from .schemas import GithubUser 6 | from .models import User 7 | 8 | 9 | def get_user(db: Session, user_id: int) -> Optional[User]: 10 | return db.query(User).filter_by(id=user_id).first() 11 | 12 | 13 | def get_user_by_login(db: Session, login: str) -> Optional[User]: 14 | return db.query(User).filter_by(login=login).first() 15 | 16 | 17 | def get_users(db: Session, skip: int = 0, limit: int = 100) -> List[User]: 18 | return db.query(User).offset(skip).limit(limit).all() 19 | 20 | 21 | def create_user(db: Session, github_user: GithubUser) -> User: 22 | user = User( 23 | login=github_user.login, 24 | name=github_user.name, 25 | email=github_user.email, 26 | picture=github_user.avatar_url, 27 | ) 28 | db.add(user) 29 | db.commit() 30 | db.refresh(user) 31 | 32 | return user 33 | 34 | -------------------------------------------------------------------------------- /back/app/auth/dependencies.py: -------------------------------------------------------------------------------- 1 | import jwt 2 | from fastapi import Header, HTTPException, status 3 | from fastapi.security.utils import get_authorization_scheme_param 4 | from pydantic import ValidationError 5 | 6 | from app.settings import settings 7 | 8 | from .schemas import User 9 | 10 | 11 | def get_user_from_header(*, authorization: str = Header(None)) -> User: 12 | credentials_exception = HTTPException( 13 | status_code=status.HTTP_401_UNAUTHORIZED, 14 | detail="Could not validate credentials", 15 | headers={"WWW-Authenticate": "Bearer"}, 16 | ) 17 | 18 | scheme, token = get_authorization_scheme_param(authorization) 19 | if scheme.lower() != "bearer": 20 | raise credentials_exception 21 | 22 | try: 23 | payload = jwt.decode( 24 | token, settings.jwt_secret_key, algorithms=[settings.jwt_algorithm] 25 | ) 26 | try: 27 | token_data = User(**payload) 28 | return token_data 29 | except ValidationError: 30 | raise credentials_exception 31 | except jwt.PyJWTError: 32 | raise credentials_exception 33 | -------------------------------------------------------------------------------- /back/app/auth/helpers.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timedelta 2 | import string 3 | import random 4 | 5 | import jwt 6 | 7 | from app.settings import settings 8 | 9 | from .schemas import User 10 | 11 | 12 | def generate_token(length: int = 24) -> str: 13 | return "".join( 14 | random.choice(string.ascii_uppercase + string.digits) for _ in range(length) 15 | ) 16 | 17 | 18 | def create_access_token(*, data: User, exp: int = None) -> bytes: 19 | to_encode = data.dict() 20 | if exp is not None: 21 | to_encode.update({"exp": exp}) 22 | else: 23 | expire = datetime.utcnow() + timedelta(minutes=60) 24 | to_encode.update({"exp": expire}) 25 | encoded_jwt = jwt.encode( 26 | to_encode, settings.jwt_secret_key, algorithm=settings.jwt_algorithm 27 | ) 28 | return encoded_jwt 29 | -------------------------------------------------------------------------------- /back/app/auth/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, Integer, String 2 | 3 | from app.database import Base 4 | 5 | 6 | class User(Base): 7 | __tablename__ = "users" 8 | 9 | id = Column(Integer, primary_key=True, index=True) 10 | login = Column(String, unique=True, index=True, nullable=False) 11 | name = Column(String) 12 | email = Column(String) 13 | picture = Column(String) 14 | 15 | def get_display_name(self) -> str: 16 | return self.name if self.name is not None else self.login 17 | -------------------------------------------------------------------------------- /back/app/auth/routes.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | from urllib.parse import urlencode, parse_qsl 3 | 4 | import httpx 5 | from fastapi import APIRouter, Depends 6 | from sqlalchemy.orm import Session 7 | 8 | from app.settings import settings 9 | from app.database import get_db 10 | 11 | from .schemas import Url, AuthorizationResponse, GithubUser, User, Token 12 | from .helpers import generate_token, create_access_token 13 | from .crud import get_user_by_login, create_user, get_user 14 | from .dependencies import get_user_from_header 15 | from .models import User as DbUser 16 | 17 | LOGIN_URL = "https://github.com/login/oauth/authorize" 18 | REDIRECT_URL = f"{settings.app_url}/auth/github" 19 | TOKEN_URL = "https://github.com/login/oauth/access_token" 20 | USER_URL = "https://api.github.com/user" 21 | 22 | router = APIRouter() 23 | 24 | 25 | @router.get("/login") 26 | def get_login_url() -> Url: 27 | params = { 28 | "client_id": settings.github_client_id, 29 | "redirect_uri": REDIRECT_URL, 30 | "state": generate_token(), 31 | } 32 | return Url(url=f"{LOGIN_URL}?{urlencode(params)}") 33 | 34 | 35 | @router.post("/authorize") 36 | async def verify_authorization( 37 | body: AuthorizationResponse, db: Session = Depends(get_db) 38 | ) -> Token: 39 | params = { 40 | "client_id": settings.github_client_id, 41 | "client_secret": settings.github_client_secret, 42 | "code": body.code, 43 | "state": body.state, 44 | } 45 | 46 | async with httpx.AsyncClient() as client: 47 | token_request = await client.post(TOKEN_URL, params=params) 48 | response: Dict[bytes, bytes] = dict(parse_qsl(token_request.content)) 49 | github_token = response[b"access_token"].decode("utf-8") 50 | github_header = {"Authorization": f"token {github_token}"} 51 | user_request = await client.get(USER_URL, headers=github_header) 52 | github_user = GithubUser(**user_request.json()) 53 | 54 | db_user = get_user_by_login(db, github_user.login) 55 | if db_user is None: 56 | db_user = create_user(db, github_user) 57 | 58 | verified_user = User.from_orm(db_user) 59 | access_token = create_access_token(data=verified_user) 60 | 61 | return Token(access_token=access_token, token_type="bearer", user=db_user) 62 | 63 | 64 | @router.get("/me", response_model=User) 65 | def read_profile( 66 | user: User = Depends(get_user_from_header), 67 | db: Session = Depends(get_db), 68 | ) -> DbUser: 69 | db_user = get_user(db, user.id) 70 | if db_user is None: 71 | raise HTTPException(status_code=404, detail="User not found") 72 | return db_user 73 | -------------------------------------------------------------------------------- /back/app/auth/schemas.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel 2 | 3 | 4 | class Url(BaseModel): 5 | url: str 6 | 7 | 8 | class AuthorizationResponse(BaseModel): 9 | state: str 10 | code: str 11 | 12 | 13 | class GithubUser(BaseModel): 14 | login: str 15 | name: str 16 | company: str 17 | location: str 18 | email: str 19 | avatar_url: str 20 | 21 | 22 | class User(BaseModel): 23 | id: int 24 | login: str 25 | name: str 26 | email: str 27 | picture: str 28 | 29 | class Config: 30 | orm_mode = True 31 | 32 | 33 | class Token(BaseModel): 34 | access_token: str 35 | token_type: str 36 | user: User 37 | -------------------------------------------------------------------------------- /back/app/database.py: -------------------------------------------------------------------------------- 1 | from typing import Generator 2 | 3 | from sqlalchemy import MetaData, create_engine, inspect 4 | from sqlalchemy.ext.declarative import declarative_base 5 | from sqlalchemy.ext.declarative.api import DeclarativeMeta 6 | from sqlalchemy.orm import sessionmaker 7 | from sqlalchemy.orm.session import Session 8 | 9 | from app.settings import settings 10 | 11 | SQLALCHEMY_DATABASE_URL = settings.db_uri 12 | 13 | engine = create_engine(SQLALCHEMY_DATABASE_URL) 14 | SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) 15 | 16 | meta = MetaData( 17 | naming_convention={ 18 | "ix": "ix_%(column_0_N_label)s", 19 | "uq": "uq_%(table_name)s_%(column_0_N_name)s", 20 | "ck": "ck_%(table_name)s_%(constraint_name)s", 21 | "fk": "fk_%(table_name)s_%(column_0_N_name)s_%(referred_table_name)s", 22 | "pk": "pk_%(table_name)s", 23 | } 24 | ) 25 | 26 | BaseMeta = DeclarativeMeta 27 | SQLBase = declarative_base(metadata=meta, metaclass=BaseMeta) 28 | 29 | 30 | class Base(SQLBase): 31 | __abstract__ = True 32 | 33 | def to_dict(self): 34 | return { 35 | col.key: getattr(self, col.key) for col in inspect(self).mapper.column_attrs 36 | } 37 | 38 | 39 | # Dependency 40 | def get_db() -> Generator[Session, None, None]: 41 | db = SessionLocal() 42 | try: 43 | yield db 44 | finally: 45 | db.close() 46 | -------------------------------------------------------------------------------- /back/app/main.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Dict 2 | 3 | from fastapi import FastAPI 4 | from fastapi.middleware.cors import CORSMiddleware 5 | 6 | from .auth.routes import router as auth_router 7 | 8 | app = FastAPI() 9 | 10 | app.add_middleware( 11 | CORSMiddleware, 12 | allow_origins=["*"], 13 | allow_credentials=True, 14 | allow_methods=["*"], 15 | allow_headers=["*"], 16 | ) 17 | 18 | app.include_router(auth_router, prefix="/auth") 19 | -------------------------------------------------------------------------------- /back/app/settings.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseSettings, Field, validator 2 | 3 | 4 | class Settings(BaseSettings): 5 | env: str = Field("prod", env="ENV") 6 | app_url: str = Field("http://localhost:8080", env="APP_URL") 7 | db_uri: str = Field( 8 | "postgresql://example:example@localhost:5432/postgres", env="DB_URI" 9 | ) 10 | github_client_id: str = Field("", env="GITHUB_CLIENT_ID") 11 | github_client_secret: str = Field("", env="GITHUB_CLIENT_SECRET") 12 | jwt_secret_key: str = Field("example_key_super_secret", env="JWT_SECRET_KEY") 13 | jwt_algorithm: str = Field("HS256", env="JWT_ALGORITHM") 14 | 15 | class Config: 16 | env_file = '.env' 17 | 18 | 19 | settings = Settings() 20 | 21 | -------------------------------------------------------------------------------- /back/migrations/env.py: -------------------------------------------------------------------------------- 1 | # add your model's MetaData object here 2 | # for 'autogenerate' support 3 | import sys 4 | from logging.config import fileConfig 5 | 6 | from alembic import context 7 | from sqlalchemy import engine_from_config, pool 8 | 9 | sys.path = ["", ".."] + sys.path[1:] 10 | 11 | from app.settings import settings 12 | from app.database import Base 13 | from app.auth.models import * 14 | 15 | 16 | # this is the Alembic Config object, which provides 17 | # access to the values within the .ini file in use. 18 | config = context.config 19 | 20 | # Set db uri using environment variables 21 | config.set_main_option("sqlalchemy.url", settings.db_uri) 22 | 23 | # Interpret the config file for Python logging. 24 | # This line sets up loggers basically. 25 | fileConfig(config.config_file_name) 26 | 27 | target_metadata = Base.metadata 28 | 29 | # other values from the config, defined by the needs of env.py, 30 | # can be acquired: 31 | # my_important_option = config.get_main_option("my_important_option") 32 | # ... etc. 33 | 34 | 35 | def run_migrations_offline(): 36 | """Run migrations in 'offline' mode. 37 | 38 | This configures the context with just a URL 39 | and not an Engine, though an Engine is acceptable 40 | here as well. By skipping the Engine creation 41 | we don't even need a DBAPI to be available. 42 | 43 | Calls to context.execute() here emit the given string to the 44 | script output. 45 | 46 | """ 47 | url = config.get_main_option("sqlalchemy.url") 48 | context.configure( 49 | url=url, 50 | target_metadata=target_metadata, 51 | literal_binds=True, 52 | dialect_opts={"paramstyle": "named"}, 53 | compare_type=True, 54 | ) 55 | 56 | with context.begin_transaction(): 57 | context.run_migrations() 58 | 59 | 60 | def run_migrations_online(): 61 | """Run migrations in 'online' mode. 62 | 63 | In this scenario we need to create an Engine 64 | and associate a connection with the context. 65 | 66 | """ 67 | connectable = engine_from_config( 68 | config.get_section(config.config_ini_section), 69 | prefix="sqlalchemy.", 70 | poolclass=pool.NullPool, 71 | ) 72 | 73 | with connectable.connect() as connection: 74 | context.configure( 75 | connection=connection, target_metadata=target_metadata, compare_type=True 76 | ) 77 | 78 | with context.begin_transaction(): 79 | context.run_migrations() 80 | 81 | 82 | if context.is_offline_mode(): 83 | run_migrations_offline() 84 | else: 85 | run_migrations_online() 86 | -------------------------------------------------------------------------------- /back/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 | -------------------------------------------------------------------------------- /back/migrations/versions/2020-12-20_c6151d23daaa_add_users.py: -------------------------------------------------------------------------------- 1 | """add users 2 | 3 | Revision ID: c6151d23daaa 4 | Revises: 5 | Create Date: 2020-12-20 19:02:24.775989 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = 'c6151d23daaa' 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('users', 22 | sa.Column('id', sa.Integer(), nullable=False), 23 | sa.Column('login', sa.String(), nullable=False), 24 | sa.Column('name', sa.String(), nullable=True), 25 | sa.Column('email', sa.String(), nullable=True), 26 | sa.Column('picture', sa.String(), nullable=True), 27 | sa.PrimaryKeyConstraint('id', name=op.f('pk_users')) 28 | ) 29 | op.create_index(op.f('ix_users_id'), 'users', ['id'], unique=False) 30 | op.create_index(op.f('ix_users_login'), 'users', ['login'], unique=True) 31 | # ### end Alembic commands ### 32 | 33 | 34 | def downgrade(): 35 | # ### commands auto generated by Alembic - please adjust! ### 36 | op.drop_index(op.f('ix_users_login'), table_name='users') 37 | op.drop_index(op.f('ix_users_id'), table_name='users') 38 | op.drop_table('users') 39 | # ### end Alembic commands ### 40 | -------------------------------------------------------------------------------- /back/poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "alembic" 3 | version = "1.4.3" 4 | description = "A database migration tool for SQLAlchemy." 5 | category = "main" 6 | optional = false 7 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 8 | 9 | [package.dependencies] 10 | Mako = "*" 11 | python-dateutil = "*" 12 | python-editor = ">=0.3" 13 | SQLAlchemy = ">=1.1.0" 14 | 15 | [[package]] 16 | name = "appdirs" 17 | version = "1.4.4" 18 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 19 | category = "dev" 20 | optional = false 21 | python-versions = "*" 22 | 23 | [[package]] 24 | name = "black" 25 | version = "20.8b1" 26 | description = "The uncompromising code formatter." 27 | category = "dev" 28 | optional = false 29 | python-versions = ">=3.6" 30 | 31 | [package.dependencies] 32 | appdirs = "*" 33 | click = ">=7.1.2" 34 | mypy-extensions = ">=0.4.3" 35 | pathspec = ">=0.6,<1" 36 | regex = ">=2020.1.8" 37 | toml = ">=0.10.1" 38 | typed-ast = ">=1.4.0" 39 | typing-extensions = ">=3.7.4" 40 | 41 | [package.extras] 42 | colorama = ["colorama (>=0.4.3)"] 43 | d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] 44 | 45 | [[package]] 46 | name = "certifi" 47 | version = "2020.12.5" 48 | description = "Python package for providing Mozilla's CA Bundle." 49 | category = "main" 50 | optional = false 51 | python-versions = "*" 52 | 53 | [[package]] 54 | name = "click" 55 | version = "7.1.2" 56 | description = "Composable command line interface toolkit" 57 | category = "main" 58 | optional = false 59 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 60 | 61 | [[package]] 62 | name = "fastapi" 63 | version = "0.62.0" 64 | description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" 65 | category = "main" 66 | optional = false 67 | python-versions = ">=3.6" 68 | 69 | [package.dependencies] 70 | pydantic = ">=1.0.0,<2.0.0" 71 | starlette = "0.13.6" 72 | 73 | [package.extras] 74 | all = ["requests (>=2.24.0,<3.0.0)", "aiofiles (>=0.5.0,<0.6.0)", "jinja2 (>=2.11.2,<3.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<2.0.0)", "pyyaml (>=5.3.1,<6.0.0)", "graphene (>=2.1.8,<3.0.0)", "ujson (>=3.0.0,<4.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn (>=0.11.5,<0.12.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)"] 75 | dev = ["python-jose[cryptography] (>=3.1.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn (>=0.11.5,<0.12.0)", "graphene (>=2.1.8,<3.0.0)"] 76 | doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=6.1.4,<7.0.0)", "markdown-include (>=0.5.1,<0.6.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.2.0)", "typer (>=0.3.0,<0.4.0)", "typer-cli (>=0.0.9,<0.0.10)", "pyyaml (>=5.3.1,<6.0.0)"] 77 | test = ["pytest (==5.4.3)", "pytest-cov (==2.10.0)", "pytest-asyncio (>=0.14.0,<0.15.0)", "mypy (==0.782)", "flake8 (>=3.8.3,<4.0.0)", "black (==19.10b0)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.15.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.4.0)", "orjson (>=3.2.1,<4.0.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "aiofiles (>=0.5.0,<0.6.0)", "flask (>=1.1.2,<2.0.0)"] 78 | 79 | [[package]] 80 | name = "h11" 81 | version = "0.11.0" 82 | description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" 83 | category = "main" 84 | optional = false 85 | python-versions = "*" 86 | 87 | [[package]] 88 | name = "httpcore" 89 | version = "0.12.2" 90 | description = "A minimal low-level HTTP client." 91 | category = "main" 92 | optional = false 93 | python-versions = ">=3.6" 94 | 95 | [package.dependencies] 96 | h11 = "<1.0.0" 97 | sniffio = ">=1.0.0,<2.0.0" 98 | 99 | [package.extras] 100 | http2 = ["h2 (>=3,<5)"] 101 | 102 | [[package]] 103 | name = "httpx" 104 | version = "0.16.1" 105 | description = "The next generation HTTP client." 106 | category = "main" 107 | optional = false 108 | python-versions = ">=3.6" 109 | 110 | [package.dependencies] 111 | certifi = "*" 112 | httpcore = ">=0.12.0,<0.13.0" 113 | rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} 114 | sniffio = "*" 115 | 116 | [package.extras] 117 | brotli = ["brotlipy (>=0.7.0,<0.8.0)"] 118 | http2 = ["h2 (>=3.0.0,<4.0.0)"] 119 | 120 | [[package]] 121 | name = "idna" 122 | version = "2.10" 123 | description = "Internationalized Domain Names in Applications (IDNA)" 124 | category = "main" 125 | optional = false 126 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 127 | 128 | [[package]] 129 | name = "mako" 130 | version = "1.1.3" 131 | description = "A super-fast templating language that borrows the best ideas from the existing templating languages." 132 | category = "main" 133 | optional = false 134 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 135 | 136 | [package.dependencies] 137 | MarkupSafe = ">=0.9.2" 138 | 139 | [package.extras] 140 | babel = ["babel"] 141 | lingua = ["lingua"] 142 | 143 | [[package]] 144 | name = "markupsafe" 145 | version = "1.1.1" 146 | description = "Safely add untrusted strings to HTML/XML markup." 147 | category = "main" 148 | optional = false 149 | python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" 150 | 151 | [[package]] 152 | name = "mypy-extensions" 153 | version = "0.4.3" 154 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 155 | category = "dev" 156 | optional = false 157 | python-versions = "*" 158 | 159 | [[package]] 160 | name = "pathspec" 161 | version = "0.8.1" 162 | description = "Utility library for gitignore style pattern matching of file paths." 163 | category = "dev" 164 | optional = false 165 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 166 | 167 | [[package]] 168 | name = "psycopg2-binary" 169 | version = "2.8.6" 170 | description = "psycopg2 - Python-PostgreSQL Database Adapter" 171 | category = "main" 172 | optional = false 173 | python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" 174 | 175 | [[package]] 176 | name = "pydantic" 177 | version = "1.7.3" 178 | description = "Data validation and settings management using python 3.6 type hinting" 179 | category = "main" 180 | optional = false 181 | python-versions = ">=3.6" 182 | 183 | [package.dependencies] 184 | python-dotenv = {version = ">=0.10.4", optional = true, markers = "extra == \"dotenv\""} 185 | 186 | [package.extras] 187 | dotenv = ["python-dotenv (>=0.10.4)"] 188 | email = ["email-validator (>=1.0.3)"] 189 | typing_extensions = ["typing-extensions (>=3.7.2)"] 190 | 191 | [[package]] 192 | name = "pyjwt" 193 | version = "1.7.1" 194 | description = "JSON Web Token implementation in Python" 195 | category = "main" 196 | optional = false 197 | python-versions = "*" 198 | 199 | [package.extras] 200 | crypto = ["cryptography (>=1.4)"] 201 | flake8 = ["flake8", "flake8-import-order", "pep8-naming"] 202 | test = ["pytest (>=4.0.1,<5.0.0)", "pytest-cov (>=2.6.0,<3.0.0)", "pytest-runner (>=4.2,<5.0.0)"] 203 | 204 | [[package]] 205 | name = "python-dateutil" 206 | version = "2.8.1" 207 | description = "Extensions to the standard Python datetime module" 208 | category = "main" 209 | optional = false 210 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 211 | 212 | [package.dependencies] 213 | six = ">=1.5" 214 | 215 | [[package]] 216 | name = "python-dotenv" 217 | version = "0.15.0" 218 | description = "Add .env support to your django/flask apps in development and deployments" 219 | category = "main" 220 | optional = false 221 | python-versions = "*" 222 | 223 | [package.extras] 224 | cli = ["click (>=5.0)"] 225 | 226 | [[package]] 227 | name = "python-editor" 228 | version = "1.0.4" 229 | description = "Programmatically open an editor, capture the result." 230 | category = "main" 231 | optional = false 232 | python-versions = "*" 233 | 234 | [[package]] 235 | name = "regex" 236 | version = "2020.11.13" 237 | description = "Alternative regular expression module, to replace re." 238 | category = "dev" 239 | optional = false 240 | python-versions = "*" 241 | 242 | [[package]] 243 | name = "rfc3986" 244 | version = "1.4.0" 245 | description = "Validating URI References per RFC 3986" 246 | category = "main" 247 | optional = false 248 | python-versions = "*" 249 | 250 | [package.dependencies] 251 | idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} 252 | 253 | [package.extras] 254 | idna2008 = ["idna"] 255 | 256 | [[package]] 257 | name = "six" 258 | version = "1.15.0" 259 | description = "Python 2 and 3 compatibility utilities" 260 | category = "main" 261 | optional = false 262 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 263 | 264 | [[package]] 265 | name = "sniffio" 266 | version = "1.2.0" 267 | description = "Sniff out which async library your code is running under" 268 | category = "main" 269 | optional = false 270 | python-versions = ">=3.5" 271 | 272 | [[package]] 273 | name = "sqlalchemy" 274 | version = "1.3.22" 275 | description = "Database Abstraction Library" 276 | category = "main" 277 | optional = false 278 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 279 | 280 | [package.extras] 281 | mssql = ["pyodbc"] 282 | mssql_pymssql = ["pymssql"] 283 | mssql_pyodbc = ["pyodbc"] 284 | mysql = ["mysqlclient"] 285 | oracle = ["cx-oracle"] 286 | postgresql = ["psycopg2"] 287 | postgresql_pg8000 = ["pg8000"] 288 | postgresql_psycopg2binary = ["psycopg2-binary"] 289 | postgresql_psycopg2cffi = ["psycopg2cffi"] 290 | pymysql = ["pymysql"] 291 | 292 | [[package]] 293 | name = "starlette" 294 | version = "0.13.6" 295 | description = "The little ASGI library that shines." 296 | category = "main" 297 | optional = false 298 | python-versions = ">=3.6" 299 | 300 | [package.extras] 301 | full = ["aiofiles", "graphene", "itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests", "ujson"] 302 | 303 | [[package]] 304 | name = "toml" 305 | version = "0.10.2" 306 | description = "Python Library for Tom's Obvious, Minimal Language" 307 | category = "dev" 308 | optional = false 309 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 310 | 311 | [[package]] 312 | name = "typed-ast" 313 | version = "1.4.1" 314 | description = "a fork of Python 2 and 3 ast modules with type comment support" 315 | category = "dev" 316 | optional = false 317 | python-versions = "*" 318 | 319 | [[package]] 320 | name = "typing-extensions" 321 | version = "3.7.4.3" 322 | description = "Backported and Experimental Type Hints for Python 3.5+" 323 | category = "dev" 324 | optional = false 325 | python-versions = "*" 326 | 327 | [[package]] 328 | name = "uvicorn" 329 | version = "0.13.2" 330 | description = "The lightning-fast ASGI server." 331 | category = "main" 332 | optional = false 333 | python-versions = "*" 334 | 335 | [package.dependencies] 336 | click = ">=7.0.0,<8.0.0" 337 | h11 = ">=0.8" 338 | 339 | [package.extras] 340 | standard = ["websockets (>=8.0.0,<9.0.0)", "watchgod (>=0.6,<0.7)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "httptools (>=0.1.0,<0.2.0)", "uvloop (>=0.14.0)", "colorama (>=0.4)"] 341 | 342 | [metadata] 343 | lock-version = "1.1" 344 | python-versions = "^3.8" 345 | content-hash = "2d9f22387a7e2fa7828b2d869735deaf0ee5d7acd03de95f59eb2187f499a436" 346 | 347 | [metadata.files] 348 | alembic = [ 349 | {file = "alembic-1.4.3-py2.py3-none-any.whl", hash = "sha256:4e02ed2aa796bd179965041afa092c55b51fb077de19d61835673cc80672c01c"}, 350 | {file = "alembic-1.4.3.tar.gz", hash = "sha256:5334f32314fb2a56d86b4c4dd1ae34b08c03cae4cb888bc699942104d66bc245"}, 351 | ] 352 | appdirs = [ 353 | {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, 354 | {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, 355 | ] 356 | black = [ 357 | {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, 358 | ] 359 | certifi = [ 360 | {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, 361 | {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, 362 | ] 363 | click = [ 364 | {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, 365 | {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, 366 | ] 367 | fastapi = [ 368 | {file = "fastapi-0.62.0-py3-none-any.whl", hash = "sha256:62074dd38541d9d7245f3aacbbd0d44340c53d56186c9b249d261a18dad4874b"}, 369 | {file = "fastapi-0.62.0.tar.gz", hash = "sha256:8f4c64cd9cea67fb7dd175ca5015961efa572b9f43a8731014dac8929d86225f"}, 370 | ] 371 | h11 = [ 372 | {file = "h11-0.11.0-py2.py3-none-any.whl", hash = "sha256:ab6c335e1b6ef34b205d5ca3e228c9299cc7218b049819ec84a388c2525e5d87"}, 373 | {file = "h11-0.11.0.tar.gz", hash = "sha256:3c6c61d69c6f13d41f1b80ab0322f1872702a3ba26e12aa864c928f6a43fbaab"}, 374 | ] 375 | httpcore = [ 376 | {file = "httpcore-0.12.2-py3-none-any.whl", hash = "sha256:420700af11db658c782f7e8fda34f9dcd95e3ee93944dd97d78cb70247e0cd06"}, 377 | {file = "httpcore-0.12.2.tar.gz", hash = "sha256:dd1d762d4f7c2702149d06be2597c35fb154c5eff9789a8c5823fbcf4d2978d6"}, 378 | ] 379 | httpx = [ 380 | {file = "httpx-0.16.1-py3-none-any.whl", hash = "sha256:9cffb8ba31fac6536f2c8cde30df859013f59e4bcc5b8d43901cb3654a8e0a5b"}, 381 | {file = "httpx-0.16.1.tar.gz", hash = "sha256:126424c279c842738805974687e0518a94c7ae8d140cd65b9c4f77ac46ffa537"}, 382 | ] 383 | idna = [ 384 | {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, 385 | {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, 386 | ] 387 | mako = [ 388 | {file = "Mako-1.1.3-py2.py3-none-any.whl", hash = "sha256:93729a258e4ff0747c876bd9e20df1b9758028946e976324ccd2d68245c7b6a9"}, 389 | {file = "Mako-1.1.3.tar.gz", hash = "sha256:8195c8c1400ceb53496064314c6736719c6f25e7479cd24c77be3d9361cddc27"}, 390 | ] 391 | markupsafe = [ 392 | {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, 393 | {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"}, 394 | {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"}, 395 | {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"}, 396 | {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"}, 397 | {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"}, 398 | {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"}, 399 | {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"}, 400 | {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"}, 401 | {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"}, 402 | {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"}, 403 | {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"}, 404 | {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"}, 405 | {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"}, 406 | {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"}, 407 | {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, 408 | {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, 409 | {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, 410 | {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, 411 | {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, 412 | {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, 413 | {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, 414 | {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, 415 | {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, 416 | {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, 417 | {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, 418 | {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, 419 | {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, 420 | {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, 421 | {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, 422 | {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, 423 | {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, 424 | {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, 425 | ] 426 | mypy-extensions = [ 427 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 428 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 429 | ] 430 | pathspec = [ 431 | {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, 432 | {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, 433 | ] 434 | psycopg2-binary = [ 435 | {file = "psycopg2-binary-2.8.6.tar.gz", hash = "sha256:11b9c0ebce097180129e422379b824ae21c8f2a6596b159c7659e2e5a00e1aa0"}, 436 | {file = "psycopg2_binary-2.8.6-cp27-cp27m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:d14b140a4439d816e3b1229a4a525df917d6ea22a0771a2a78332273fd9528a4"}, 437 | {file = "psycopg2_binary-2.8.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:1fabed9ea2acc4efe4671b92c669a213db744d2af8a9fc5d69a8e9bc14b7a9db"}, 438 | {file = "psycopg2_binary-2.8.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f5ab93a2cb2d8338b1674be43b442a7f544a0971da062a5da774ed40587f18f5"}, 439 | {file = "psycopg2_binary-2.8.6-cp27-cp27m-win32.whl", hash = "sha256:b4afc542c0ac0db720cf516dd20c0846f71c248d2b3d21013aa0d4ef9c71ca25"}, 440 | {file = "psycopg2_binary-2.8.6-cp27-cp27m-win_amd64.whl", hash = "sha256:e74a55f6bad0e7d3968399deb50f61f4db1926acf4a6d83beaaa7df986f48b1c"}, 441 | {file = "psycopg2_binary-2.8.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:0deac2af1a587ae12836aa07970f5cb91964f05a7c6cdb69d8425ff4c15d4e2c"}, 442 | {file = "psycopg2_binary-2.8.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ad20d2eb875aaa1ea6d0f2916949f5c08a19c74d05b16ce6ebf6d24f2c9f75d1"}, 443 | {file = "psycopg2_binary-2.8.6-cp34-cp34m-win32.whl", hash = "sha256:950bc22bb56ee6ff142a2cb9ee980b571dd0912b0334aa3fe0fe3788d860bea2"}, 444 | {file = "psycopg2_binary-2.8.6-cp34-cp34m-win_amd64.whl", hash = "sha256:b8a3715b3c4e604bcc94c90a825cd7f5635417453b253499664f784fc4da0152"}, 445 | {file = "psycopg2_binary-2.8.6-cp35-cp35m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:d1b4ab59e02d9008efe10ceabd0b31e79519da6fb67f7d8e8977118832d0f449"}, 446 | {file = "psycopg2_binary-2.8.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:ac0c682111fbf404525dfc0f18a8b5f11be52657d4f96e9fcb75daf4f3984859"}, 447 | {file = "psycopg2_binary-2.8.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7d92a09b788cbb1aec325af5fcba9fed7203897bbd9269d5691bb1e3bce29550"}, 448 | {file = "psycopg2_binary-2.8.6-cp35-cp35m-win32.whl", hash = "sha256:aaa4213c862f0ef00022751161df35804127b78adf4a2755b9f991a507e425fd"}, 449 | {file = "psycopg2_binary-2.8.6-cp35-cp35m-win_amd64.whl", hash = "sha256:c2507d796fca339c8fb03216364cca68d87e037c1f774977c8fc377627d01c71"}, 450 | {file = "psycopg2_binary-2.8.6-cp36-cp36m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:ee69dad2c7155756ad114c02db06002f4cded41132cc51378e57aad79cc8e4f4"}, 451 | {file = "psycopg2_binary-2.8.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:e82aba2188b9ba309fd8e271702bd0d0fc9148ae3150532bbb474f4590039ffb"}, 452 | {file = "psycopg2_binary-2.8.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d5227b229005a696cc67676e24c214740efd90b148de5733419ac9aaba3773da"}, 453 | {file = "psycopg2_binary-2.8.6-cp36-cp36m-win32.whl", hash = "sha256:a0eb43a07386c3f1f1ebb4dc7aafb13f67188eab896e7397aa1ee95a9c884eb2"}, 454 | {file = "psycopg2_binary-2.8.6-cp36-cp36m-win_amd64.whl", hash = "sha256:e1f57aa70d3f7cc6947fd88636a481638263ba04a742b4a37dd25c373e41491a"}, 455 | {file = "psycopg2_binary-2.8.6-cp37-cp37m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:833709a5c66ca52f1d21d41865a637223b368c0ee76ea54ca5bad6f2526c7679"}, 456 | {file = "psycopg2_binary-2.8.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ba28584e6bca48c59eecbf7efb1576ca214b47f05194646b081717fa628dfddf"}, 457 | {file = "psycopg2_binary-2.8.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6a32f3a4cb2f6e1a0b15215f448e8ce2da192fd4ff35084d80d5e39da683e79b"}, 458 | {file = "psycopg2_binary-2.8.6-cp37-cp37m-win32.whl", hash = "sha256:0e4dc3d5996760104746e6cfcdb519d9d2cd27c738296525d5867ea695774e67"}, 459 | {file = "psycopg2_binary-2.8.6-cp37-cp37m-win_amd64.whl", hash = "sha256:cec7e622ebc545dbb4564e483dd20e4e404da17ae07e06f3e780b2dacd5cee66"}, 460 | {file = "psycopg2_binary-2.8.6-cp38-cp38-macosx_10_9_x86_64.macosx_10_9_intel.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:ba381aec3a5dc29634f20692349d73f2d21f17653bda1decf0b52b11d694541f"}, 461 | {file = "psycopg2_binary-2.8.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a0c50db33c32594305b0ef9abc0cb7db13de7621d2cadf8392a1d9b3c437ef77"}, 462 | {file = "psycopg2_binary-2.8.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2dac98e85565d5688e8ab7bdea5446674a83a3945a8f416ad0110018d1501b94"}, 463 | {file = "psycopg2_binary-2.8.6-cp38-cp38-win32.whl", hash = "sha256:bd1be66dde2b82f80afb9459fc618216753f67109b859a361cf7def5c7968729"}, 464 | {file = "psycopg2_binary-2.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:8cd0fb36c7412996859cb4606a35969dd01f4ea34d9812a141cd920c3b18be77"}, 465 | {file = "psycopg2_binary-2.8.6-cp39-cp39-macosx_10_9_x86_64.macosx_10_9_intel.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:89705f45ce07b2dfa806ee84439ec67c5d9a0ef20154e0e475e2b2ed392a5b83"}, 466 | {file = "psycopg2_binary-2.8.6-cp39-cp39-manylinux1_i686.whl", hash = "sha256:42ec1035841b389e8cc3692277a0bd81cdfe0b65d575a2c8862cec7a80e62e52"}, 467 | {file = "psycopg2_binary-2.8.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7312e931b90fe14f925729cde58022f5d034241918a5c4f9797cac62f6b3a9dd"}, 468 | {file = "psycopg2_binary-2.8.6-cp39-cp39-win32.whl", hash = "sha256:6422f2ff0919fd720195f64ffd8f924c1395d30f9a495f31e2392c2efafb5056"}, 469 | {file = "psycopg2_binary-2.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:15978a1fbd225583dd8cdaf37e67ccc278b5abecb4caf6b2d6b8e2b948e953f6"}, 470 | ] 471 | pydantic = [ 472 | {file = "pydantic-1.7.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c59ea046aea25be14dc22d69c97bee629e6d48d2b2ecb724d7fe8806bf5f61cd"}, 473 | {file = "pydantic-1.7.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a4143c8d0c456a093387b96e0f5ee941a950992904d88bc816b4f0e72c9a0009"}, 474 | {file = "pydantic-1.7.3-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:d8df4b9090b595511906fa48deda47af04e7d092318bfb291f4d45dfb6bb2127"}, 475 | {file = "pydantic-1.7.3-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:514b473d264671a5c672dfb28bdfe1bf1afd390f6b206aa2ec9fed7fc592c48e"}, 476 | {file = "pydantic-1.7.3-cp36-cp36m-win_amd64.whl", hash = "sha256:dba5c1f0a3aeea5083e75db9660935da90216f8a81b6d68e67f54e135ed5eb23"}, 477 | {file = "pydantic-1.7.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:59e45f3b694b05a69032a0d603c32d453a23f0de80844fb14d55ab0c6c78ff2f"}, 478 | {file = "pydantic-1.7.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:5b24e8a572e4b4c18f614004dda8c9f2c07328cb5b6e314d6e1bbd536cb1a6c1"}, 479 | {file = "pydantic-1.7.3-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:b2b054d095b6431cdda2f852a6d2f0fdec77686b305c57961b4c5dd6d863bf3c"}, 480 | {file = "pydantic-1.7.3-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:025bf13ce27990acc059d0c5be46f416fc9b293f45363b3d19855165fee1874f"}, 481 | {file = "pydantic-1.7.3-cp37-cp37m-win_amd64.whl", hash = "sha256:6e3874aa7e8babd37b40c4504e3a94cc2023696ced5a0500949f3347664ff8e2"}, 482 | {file = "pydantic-1.7.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e682f6442ebe4e50cb5e1cfde7dda6766fb586631c3e5569f6aa1951fd1a76ef"}, 483 | {file = "pydantic-1.7.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:185e18134bec5ef43351149fe34fda4758e53d05bb8ea4d5928f0720997b79ef"}, 484 | {file = "pydantic-1.7.3-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:f5b06f5099e163295b8ff5b1b71132ecf5866cc6e7f586d78d7d3fd6e8084608"}, 485 | {file = "pydantic-1.7.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:24ca47365be2a5a3cc3f4a26dcc755bcdc9f0036f55dcedbd55663662ba145ec"}, 486 | {file = "pydantic-1.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:d1fe3f0df8ac0f3a9792666c69a7cd70530f329036426d06b4f899c025aca74e"}, 487 | {file = "pydantic-1.7.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f6864844b039805add62ebe8a8c676286340ba0c6d043ae5dea24114b82a319e"}, 488 | {file = "pydantic-1.7.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:ecb54491f98544c12c66ff3d15e701612fc388161fd455242447083350904730"}, 489 | {file = "pydantic-1.7.3-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:ffd180ebd5dd2a9ac0da4e8b995c9c99e7c74c31f985ba090ee01d681b1c4b95"}, 490 | {file = "pydantic-1.7.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8d72e814c7821125b16f1553124d12faba88e85405b0864328899aceaad7282b"}, 491 | {file = "pydantic-1.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:475f2fa134cf272d6631072554f845d0630907fce053926ff634cc6bc45bf1af"}, 492 | {file = "pydantic-1.7.3-py3-none-any.whl", hash = "sha256:38be427ea01a78206bcaf9a56f835784afcba9e5b88fbdce33bbbfbcd7841229"}, 493 | {file = "pydantic-1.7.3.tar.gz", hash = "sha256:213125b7e9e64713d16d988d10997dabc6a1f73f3991e1ff8e35ebb1409c7dc9"}, 494 | ] 495 | pyjwt = [ 496 | {file = "PyJWT-1.7.1-py2.py3-none-any.whl", hash = "sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e"}, 497 | {file = "PyJWT-1.7.1.tar.gz", hash = "sha256:8d59a976fb773f3e6a39c85636357c4f0e242707394cadadd9814f5cbaa20e96"}, 498 | ] 499 | python-dateutil = [ 500 | {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, 501 | {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, 502 | ] 503 | python-dotenv = [ 504 | {file = "python-dotenv-0.15.0.tar.gz", hash = "sha256:587825ed60b1711daea4832cf37524dfd404325b7db5e25ebe88c495c9f807a0"}, 505 | {file = "python_dotenv-0.15.0-py2.py3-none-any.whl", hash = "sha256:0c8d1b80d1a1e91717ea7d526178e3882732420b03f08afea0406db6402e220e"}, 506 | ] 507 | python-editor = [ 508 | {file = "python-editor-1.0.4.tar.gz", hash = "sha256:51fda6bcc5ddbbb7063b2af7509e43bd84bfc32a4ff71349ec7847713882327b"}, 509 | {file = "python_editor-1.0.4-py2-none-any.whl", hash = "sha256:5f98b069316ea1c2ed3f67e7f5df6c0d8f10b689964a4a811ff64f0106819ec8"}, 510 | {file = "python_editor-1.0.4-py2.7.egg", hash = "sha256:ea87e17f6ec459e780e4221f295411462e0d0810858e055fc514684350a2f522"}, 511 | {file = "python_editor-1.0.4-py3-none-any.whl", hash = "sha256:1bf6e860a8ad52a14c3ee1252d5dc25b2030618ed80c022598f00176adc8367d"}, 512 | {file = "python_editor-1.0.4-py3.5.egg", hash = "sha256:c3da2053dbab6b29c94e43c486ff67206eafbe7eb52dbec7390b5e2fb05aac77"}, 513 | ] 514 | regex = [ 515 | {file = "regex-2020.11.13-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85"}, 516 | {file = "regex-2020.11.13-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70"}, 517 | {file = "regex-2020.11.13-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee"}, 518 | {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5"}, 519 | {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7"}, 520 | {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31"}, 521 | {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa"}, 522 | {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6"}, 523 | {file = "regex-2020.11.13-cp36-cp36m-win32.whl", hash = "sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e"}, 524 | {file = "regex-2020.11.13-cp36-cp36m-win_amd64.whl", hash = "sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884"}, 525 | {file = "regex-2020.11.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b"}, 526 | {file = "regex-2020.11.13-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88"}, 527 | {file = "regex-2020.11.13-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0"}, 528 | {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1"}, 529 | {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0"}, 530 | {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512"}, 531 | {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba"}, 532 | {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538"}, 533 | {file = "regex-2020.11.13-cp37-cp37m-win32.whl", hash = "sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4"}, 534 | {file = "regex-2020.11.13-cp37-cp37m-win_amd64.whl", hash = "sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444"}, 535 | {file = "regex-2020.11.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f"}, 536 | {file = "regex-2020.11.13-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d"}, 537 | {file = "regex-2020.11.13-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af"}, 538 | {file = "regex-2020.11.13-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f"}, 539 | {file = "regex-2020.11.13-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b"}, 540 | {file = "regex-2020.11.13-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8"}, 541 | {file = "regex-2020.11.13-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5"}, 542 | {file = "regex-2020.11.13-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b"}, 543 | {file = "regex-2020.11.13-cp38-cp38-win32.whl", hash = "sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c"}, 544 | {file = "regex-2020.11.13-cp38-cp38-win_amd64.whl", hash = "sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683"}, 545 | {file = "regex-2020.11.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc"}, 546 | {file = "regex-2020.11.13-cp39-cp39-manylinux1_i686.whl", hash = "sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364"}, 547 | {file = "regex-2020.11.13-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e"}, 548 | {file = "regex-2020.11.13-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e"}, 549 | {file = "regex-2020.11.13-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917"}, 550 | {file = "regex-2020.11.13-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b"}, 551 | {file = "regex-2020.11.13-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9"}, 552 | {file = "regex-2020.11.13-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c"}, 553 | {file = "regex-2020.11.13-cp39-cp39-win32.whl", hash = "sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f"}, 554 | {file = "regex-2020.11.13-cp39-cp39-win_amd64.whl", hash = "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d"}, 555 | {file = "regex-2020.11.13.tar.gz", hash = "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562"}, 556 | ] 557 | rfc3986 = [ 558 | {file = "rfc3986-1.4.0-py2.py3-none-any.whl", hash = "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"}, 559 | {file = "rfc3986-1.4.0.tar.gz", hash = "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d"}, 560 | ] 561 | six = [ 562 | {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, 563 | {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, 564 | ] 565 | sniffio = [ 566 | {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"}, 567 | {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"}, 568 | ] 569 | sqlalchemy = [ 570 | {file = "SQLAlchemy-1.3.22-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:61628715931f4962e0cdb2a7c87ff39eea320d2aa96bd471a3c293d146f90394"}, 571 | {file = "SQLAlchemy-1.3.22-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:81d8d099a49f83111cce55ec03cc87eef45eec0d90f9842b4fc674f860b857b0"}, 572 | {file = "SQLAlchemy-1.3.22-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:d055ff750fcab69ca4e57b656d9c6ad33682e9b8d564f2fbe667ab95c63591b0"}, 573 | {file = "SQLAlchemy-1.3.22-cp27-cp27m-win32.whl", hash = "sha256:9bf572e4f5aa23f88dd902f10bb103cb5979022a38eec684bfa6d61851173fec"}, 574 | {file = "SQLAlchemy-1.3.22-cp27-cp27m-win_amd64.whl", hash = "sha256:7d4b8de6bb0bc736161cb0bbd95366b11b3eb24dd6b814a143d8375e75af9990"}, 575 | {file = "SQLAlchemy-1.3.22-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:4a84c7c7658dd22a33dab2e2aa2d17c18cb004a42388246f2e87cb4085ef2811"}, 576 | {file = "SQLAlchemy-1.3.22-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:f1e88b30da8163215eab643962ae9d9252e47b4ea53404f2c4f10f24e70ddc62"}, 577 | {file = "SQLAlchemy-1.3.22-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:f115150cc4361dd46153302a640c7fa1804ac207f9cc356228248e351a8b4676"}, 578 | {file = "SQLAlchemy-1.3.22-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6aaa13ee40c4552d5f3a59f543f0db6e31712cc4009ec7385407be4627259d41"}, 579 | {file = "SQLAlchemy-1.3.22-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3ab5b44a07b8c562c6dcb7433c6a6c6e03266d19d64f87b3333eda34e3b9936b"}, 580 | {file = "SQLAlchemy-1.3.22-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:426ece890153ccc52cc5151a1a0ed540a5a7825414139bb4c95a868d8da54a52"}, 581 | {file = "SQLAlchemy-1.3.22-cp35-cp35m-win32.whl", hash = "sha256:bd4b1af45fd322dcd1fb2a9195b4f93f570d1a5902a842e3e6051385fac88f9c"}, 582 | {file = "SQLAlchemy-1.3.22-cp35-cp35m-win_amd64.whl", hash = "sha256:62285607a5264d1f91590abd874d6a498e229d5840669bd7d9f654cfaa599bd0"}, 583 | {file = "SQLAlchemy-1.3.22-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:314f5042c0b047438e19401d5f29757a511cfc2f0c40d28047ca0e4c95eabb5b"}, 584 | {file = "SQLAlchemy-1.3.22-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:62fb881ba51dbacba9af9b779211cf9acff3442d4f2993142015b22b3cd1f92a"}, 585 | {file = "SQLAlchemy-1.3.22-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:bde677047305fe76c7ee3e4492b545e0018918e44141cc154fe39e124e433991"}, 586 | {file = "SQLAlchemy-1.3.22-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:0c6406a78a714a540d980a680b86654feadb81c8d0eecb59f3d6c554a4c69f19"}, 587 | {file = "SQLAlchemy-1.3.22-cp36-cp36m-win32.whl", hash = "sha256:95bde07d19c146d608bccb9b16e144ec8f139bcfe7fd72331858698a71c9b4f5"}, 588 | {file = "SQLAlchemy-1.3.22-cp36-cp36m-win_amd64.whl", hash = "sha256:888d5b4b5aeed0d3449de93ea80173653e939e916cc95fe8527079e50235c1d2"}, 589 | {file = "SQLAlchemy-1.3.22-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:d53f59744b01f1440a1b0973ed2c3a7de204135c593299ee997828aad5191693"}, 590 | {file = "SQLAlchemy-1.3.22-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:70121f0ae48b25ef3e56e477b88cd0b0af0e1f3a53b5554071aa6a93ef378a03"}, 591 | {file = "SQLAlchemy-1.3.22-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:54da615e5b92c339e339fe8536cce99fe823b6ed505d4ea344852aefa1c205fb"}, 592 | {file = "SQLAlchemy-1.3.22-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:68428818cf80c60dc04aa0f38da20ad39b28aba4d4d199f949e7d6e04444ea86"}, 593 | {file = "SQLAlchemy-1.3.22-cp37-cp37m-win32.whl", hash = "sha256:17610d573e698bf395afbbff946544fbce7c5f4ee77b5bcb1f821b36345fae7a"}, 594 | {file = "SQLAlchemy-1.3.22-cp37-cp37m-win_amd64.whl", hash = "sha256:216ba5b4299c95ed179b58f298bda885a476b16288ab7243e89f29f6aeced7e0"}, 595 | {file = "SQLAlchemy-1.3.22-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:0c72b90988be749e04eff0342dcc98c18a14461eb4b2ad59d611b57b31120f90"}, 596 | {file = "SQLAlchemy-1.3.22-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:491fe48adc07d13e020a8b07ef82eefc227003a046809c121bea81d3dbf1832d"}, 597 | {file = "SQLAlchemy-1.3.22-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:f8191fef303025879e6c3548ecd8a95aafc0728c764ab72ec51a0bdf0c91a341"}, 598 | {file = "SQLAlchemy-1.3.22-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:108580808803c7732f34798eb4a329d45b04c562ed83ee90f09f6a184a42b766"}, 599 | {file = "SQLAlchemy-1.3.22-cp38-cp38-win32.whl", hash = "sha256:bab5a1e15b9466a25c96cda19139f3beb3e669794373b9ce28c4cf158c6e841d"}, 600 | {file = "SQLAlchemy-1.3.22-cp38-cp38-win_amd64.whl", hash = "sha256:318b5b727e00662e5fc4b4cd2bf58a5116d7c1b4dd56ffaa7d68f43458a8d1ed"}, 601 | {file = "SQLAlchemy-1.3.22-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:1418f5e71d6081aa1095a1d6b567a562d2761996710bdce9b6e6ba20a03d0864"}, 602 | {file = "SQLAlchemy-1.3.22-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:5a7f224cdb7233182cec2a45d4c633951268d6a9bcedac37abbf79dd07012aea"}, 603 | {file = "SQLAlchemy-1.3.22-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:715b34578cc740b743361f7c3e5f584b04b0f1344f45afc4e87fbac4802eb0a0"}, 604 | {file = "SQLAlchemy-1.3.22-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:2ff132a379838b1abf83c065be54cef32b47c987aedd06b82fc76476c85225eb"}, 605 | {file = "SQLAlchemy-1.3.22-cp39-cp39-win32.whl", hash = "sha256:c389d7cc2b821853fb018c85457da3e7941db64f4387720a329bc7ff06a27963"}, 606 | {file = "SQLAlchemy-1.3.22-cp39-cp39-win_amd64.whl", hash = "sha256:04f995fcbf54e46cddeb4f75ce9dfc17075d6ae04ac23b2bacb44b3bc6f6bf11"}, 607 | {file = "SQLAlchemy-1.3.22.tar.gz", hash = "sha256:758fc8c4d6c0336e617f9f6919f9daea3ab6bb9b07005eda9a1a682e24a6cacc"}, 608 | ] 609 | starlette = [ 610 | {file = "starlette-0.13.6-py3-none-any.whl", hash = "sha256:bd2ffe5e37fb75d014728511f8e68ebf2c80b0fa3d04ca1479f4dc752ae31ac9"}, 611 | {file = "starlette-0.13.6.tar.gz", hash = "sha256:ebe8ee08d9be96a3c9f31b2cb2a24dbdf845247b745664bd8a3f9bd0c977fdbc"}, 612 | ] 613 | toml = [ 614 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 615 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 616 | ] 617 | typed-ast = [ 618 | {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, 619 | {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"}, 620 | {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"}, 621 | {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"}, 622 | {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"}, 623 | {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"}, 624 | {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"}, 625 | {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"}, 626 | {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"}, 627 | {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"}, 628 | {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"}, 629 | {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"}, 630 | {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"}, 631 | {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"}, 632 | {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"}, 633 | {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"}, 634 | {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"}, 635 | {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"}, 636 | {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"}, 637 | {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, 638 | {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, 639 | ] 640 | typing-extensions = [ 641 | {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, 642 | {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, 643 | {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, 644 | ] 645 | uvicorn = [ 646 | {file = "uvicorn-0.13.2-py3-none-any.whl", hash = "sha256:6707fa7f4dbd86fd6982a2d4ecdaad2704e4514d23a1e4278104311288b04691"}, 647 | {file = "uvicorn-0.13.2.tar.gz", hash = "sha256:d19ca083bebd212843e01f689900e5c637a292c63bb336c7f0735a99300a5f38"}, 648 | ] 649 | -------------------------------------------------------------------------------- /back/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "fastapi-frontend-auth-example" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Alexis Tacnet "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.8" 9 | fastapi = "^0.62.0" 10 | httpx = "^0.16.1" 11 | uvicorn = "^0.13.2" 12 | PyJWT = "^1.7.1" 13 | SQLAlchemy = "^1.3.22" 14 | psycopg2-binary = "^2.8.6" 15 | pydantic = {extras = ["dotenv"], version = "^1.7.3"} 16 | alembic = "^1.4.3" 17 | 18 | [tool.poetry.dev-dependencies] 19 | black = "^20.8b1" 20 | 21 | [build-system] 22 | requires = ["poetry-core>=1.0.0"] 23 | build-backend = "poetry.core.masonry.api" 24 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | database: 5 | image: postgres:10 6 | environment: 7 | - POSTGRES_USER=example 8 | - POSTGRES_PASSWORD=example 9 | volumes: 10 | - ./volumes/database:/var/lib/postgresql/data 11 | ports: 12 | - 5432:5432 13 | -------------------------------------------------------------------------------- /front/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /front/README.md: -------------------------------------------------------------------------------- 1 | # fastapi-frontend-auth-example 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | npm run lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /front/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@vue/cli-plugin-babel/preset"] 3 | }; 4 | -------------------------------------------------------------------------------- /front/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fastapi-frontend-auth-example", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "axios": "^0.21.0", 12 | "core-js": "^3.6.5", 13 | "vue": "^3.0.0", 14 | "vue-router": "^4.0.0-0" 15 | }, 16 | "devDependencies": { 17 | "@vue/cli-plugin-babel": "~4.5.0", 18 | "@vue/cli-plugin-eslint": "~4.5.0", 19 | "@vue/cli-plugin-router": "~4.5.0", 20 | "@vue/cli-service": "~4.5.0", 21 | "@vue/compiler-sfc": "^3.0.0", 22 | "@vue/eslint-config-prettier": "^6.0.0", 23 | "babel-eslint": "^10.1.0", 24 | "eslint": "^6.7.2", 25 | "eslint-plugin-prettier": "^3.1.3", 26 | "eslint-plugin-vue": "^7.0.0-0", 27 | "prettier": "^1.19.1" 28 | }, 29 | "eslintConfig": { 30 | "root": true, 31 | "env": { 32 | "node": true 33 | }, 34 | "extends": [ 35 | "plugin:vue/vue3-essential", 36 | "eslint:recommended", 37 | "@vue/prettier" 38 | ], 39 | "parserOptions": { 40 | "parser": "babel-eslint" 41 | }, 42 | "rules": {} 43 | }, 44 | "browserslist": [ 45 | "> 1%", 46 | "last 2 versions", 47 | "not dead" 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /front/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuegoio/fastapi-frontend-auth-example/8f28cb4a1805d66ba1d01fb6fa44ab06c374561d/front/public/favicon.ico -------------------------------------------------------------------------------- /front/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /front/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | -------------------------------------------------------------------------------- /front/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import App from "./App.vue"; 3 | import router from "./router"; 4 | 5 | createApp(App) 6 | .use(router) 7 | .mount("#app"); 8 | -------------------------------------------------------------------------------- /front/src/router/index.js: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from "vue-router"; 2 | import Home from "../views/Home.vue"; 3 | import GithubCallback from "../views/GithubCallback.vue"; 4 | 5 | const routes = [ 6 | { 7 | path: "/", 8 | name: "Home", 9 | component: Home 10 | }, 11 | { 12 | path: "/auth/github", 13 | name: "GithubCallback", 14 | component: GithubCallback 15 | } 16 | ]; 17 | 18 | const router = createRouter({ 19 | history: createWebHistory(process.env.BASE_URL), 20 | routes 21 | }); 22 | 23 | export default router; 24 | -------------------------------------------------------------------------------- /front/src/views/GithubCallback.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 52 | -------------------------------------------------------------------------------- /front/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 21 | --------------------------------------------------------------------------------