Skip to content

Commit

Permalink
feat: setup initial support for local login (#48) (#82)
Browse files Browse the repository at this point in the history
  • Loading branch information
holtgrewe authored Sep 29, 2023
1 parent 863c046 commit afead65
Show file tree
Hide file tree
Showing 128 changed files with 2,247 additions and 928 deletions.
14 changes: 14 additions & 0 deletions backend/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"python.analysis.packageIndexDepths": [
{
"name": "sqlalchemy",
"depth": 4,
"includeAllSymbols": true
},
{
"name": "fastapi",
"depth": 2,
"includeAllSymbols": true
}
]
}
7 changes: 4 additions & 3 deletions backend/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
DIRS_PYTHON := app tests ../docs
DIRS_PYTHON_NO_ALEMBIC := app tests ../docs
DIRS_PYTHON := alembic $(DIRS_PYTHON_NO_ALEMBIC)

.PHONY: help
help:
Expand Down Expand Up @@ -57,7 +58,7 @@ flake8:

.PHONY: lint-mypy
lint-mypy:
pipenv run mypy $(DIRS_PYTHON)
pipenv run mypy $(DIRS_PYTHON_NO_ALEMBIC)

.PHONY: test
test:
Expand All @@ -79,7 +80,7 @@ docs:

.PHONY: serve
serve:
pipenv run uvicorn app.main:app --host 0.0.0.0 --port 8080 --reload
pipenv run uvicorn app.main:app --host 0.0.0.0 --port 8080 --reload --workers 8

.PHONY: alembic-check
alembic-check:
Expand Down
19 changes: 10 additions & 9 deletions backend/Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,28 @@ verify_ssl = true
name = "pypi"

[packages]
asyncpg = "*"
fastapi = "*"
fastapi-users = {extras = ["oauth", "sqlalchemy", "redis"], version = "*"}
httpx = "*"
install = "*"
passlib = {extras = ["bcrypt"], version = "*"}
pip = "*"
psycopg2-binary = "*"
pydantic = {extras = ["email"], version = "*"}
pydantic-settings = "*"
python-dateutil = "*"
python-dotenv = "*"
requests-mock = "*"
sqlalchemy = "*"
tenacity = "*"
uvicorn = "*"
jose = {extras = ["cryptography"], version = "*"}
passlib = {extras = ["bcrypt"], version = "*"}
psycopg2-binary = "*"
python-dateutil = "*"
sqlalchemy = "*"
alembic = "*"

[dev-packages]
aiosqlite = "*"
black = "*"
faker = "*"
flake8 = "*"
install = "*"
isort = "*"
Expand All @@ -30,16 +34,13 @@ pip = "*"
pytest = "*"
pytest-asyncio = "*"
pytest-cov = "*"
pytest-faker = "*"
pytest-httpx = "*"
pytest-subprocess = "*"
sphinx = "*"
sphinx-rtd-theme = "*"
starlette = "*"
types-python-jose = "*"
types-passlib = "*"
sqlalchemy-stubs = "*"
faker = "*"
pytest-faker = "*"

[requires]
python_version = "3.10"
494 changes: 446 additions & 48 deletions backend/Pipfile.lock

Large diffs are not rendered by default.

15 changes: 8 additions & 7 deletions backend/alembic/env.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from __future__ import with_statement

import os
from logging.config import fileConfig

from alembic import context
from dotenv import load_dotenv
from sqlalchemy import engine_from_config, pool
from logging.config import fileConfig

from alembic import context

# Load environment
env = os.environ
Expand All @@ -25,8 +26,8 @@
# target_metadata = mymodel.Base.metadata
# target_metadata = None

from app.db.base import Base # noqa
import app.models # noqa
from app.db.base import Base # noqa

target_metadata = Base.metadata

Expand Down Expand Up @@ -76,13 +77,13 @@ def run_migrations_online():
configuration = config.get_section(config.config_ini_section)
configuration["sqlalchemy.url"] = get_url()
connectable = engine_from_config(
configuration, prefix="sqlalchemy.", poolclass=pool.NullPool,
configuration,
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)

with connectable.connect() as connection:
context.configure(
connection=connection, target_metadata=target_metadata, compare_type=True
)
context.configure(connection=connection, target_metadata=target_metadata, compare_type=True)

with context.begin_transaction():
context.run_migrations()
Expand Down
2 changes: 1 addition & 1 deletion backend/alembic/script.py.mako
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ from alembic import op
import sqlalchemy as sa
${imports if imports else ""}

import app.models.guid # noqa
import fastapi_users_db_sqlalchemy.generics # noqa

# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
Expand Down
43 changes: 0 additions & 43 deletions backend/alembic/versions/315675882512_.py

This file was deleted.

40 changes: 40 additions & 0 deletions backend/alembic/versions/8ccd31a4f116_init_adminmsgs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""init adminmsgs
Revision ID: 8ccd31a4f116
Revises: c8009ed33089
Create Date: 2023-09-28 10:41:57.671434+02:00
"""
import fastapi_users_db_sqlalchemy.generics # noqa
import sqlalchemy as sa

from alembic import op

# revision identifiers, used by Alembic.
revision = "8ccd31a4f116"
down_revision = "c8009ed33089"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"adminmessages",
sa.Column("id", fastapi_users_db_sqlalchemy.generics.GUID(), nullable=False),
sa.Column("title", sa.String(length=255), nullable=False),
sa.Column("text", sa.Text(), nullable=True),
sa.Column("active_start", sa.DateTime(), nullable=False),
sa.Column("active_stop", sa.DateTime(), nullable=False),
sa.Column("enabled", sa.Boolean(), nullable=False),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_adminmessages_id"), "adminmessages", ["id"], unique=False)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_adminmessages_id"), table_name="adminmessages")
op.drop_table("adminmessages")
# ### end Alembic commands ###
62 changes: 62 additions & 0 deletions backend/alembic/versions/c8009ed33089_init_users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""init users
Revision ID: c8009ed33089
Revises:
Create Date: 2023-09-28 10:41:24.861855+02:00
"""
import fastapi_users_db_sqlalchemy.generics # noqa
import sqlalchemy as sa

from alembic import op

# revision identifiers, used by Alembic.
revision = "c8009ed33089"
down_revision = None
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"user",
sa.Column("id", fastapi_users_db_sqlalchemy.generics.GUID(), nullable=False),
sa.Column("email", sa.String(length=320), nullable=False),
sa.Column("hashed_password", sa.String(length=1024), nullable=False),
sa.Column("is_active", sa.Boolean(), nullable=False),
sa.Column("is_superuser", sa.Boolean(), nullable=False),
sa.Column("is_verified", sa.Boolean(), nullable=False),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_user_email"), "user", ["email"], unique=True)
op.create_table(
"oauth_account",
sa.Column("id", fastapi_users_db_sqlalchemy.generics.GUID(), nullable=False),
sa.Column("user_id", fastapi_users_db_sqlalchemy.generics.GUID(), nullable=False),
sa.Column("oauth_name", sa.String(length=100), nullable=False),
sa.Column("access_token", sa.String(length=1024), nullable=False),
sa.Column("expires_at", sa.Integer(), nullable=True),
sa.Column("refresh_token", sa.String(length=1024), nullable=True),
sa.Column("account_id", sa.String(length=320), nullable=False),
sa.Column("account_email", sa.String(length=320), nullable=False),
sa.ForeignKeyConstraint(["user_id"], ["user.id"], ondelete="cascade"),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
op.f("ix_oauth_account_account_id"), "oauth_account", ["account_id"], unique=False
)
op.create_index(
op.f("ix_oauth_account_oauth_name"), "oauth_account", ["oauth_name"], unique=False
)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_oauth_account_oauth_name"), table_name="oauth_account")
op.drop_index(op.f("ix_oauth_account_account_id"), table_name="oauth_account")
op.drop_table("oauth_account")
op.drop_index(op.f("ix_user_email"), table_name="user")
op.drop_table("user")
# ### end Alembic commands ###
32 changes: 31 additions & 1 deletion backend/app/api/api_v1/api.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,35 @@
from app.api.api_v1.endpoints import adminmsgs
from fastapi import APIRouter

from app.api.api_v1.endpoints import adminmsgs
from app.core.auth import auth_backend_bearer, auth_backend_cookie, fastapi_users
from app.schemas.user import UserCreate, UserRead, UserUpdate

api_router = APIRouter()
api_router.include_router(adminmsgs.router, prefix="/adminmsgs", tags=["adminmsgs"])

api_router.include_router(
fastapi_users.get_auth_router(auth_backend_bearer), prefix="/auth/bearer", tags=["auth"]
)
api_router.include_router(
fastapi_users.get_auth_router(auth_backend_cookie), prefix="/auth/cookie", tags=["auth"]
)
# api_router.include_router(
# fastapi_users.get_register_router(UserRead, UserCreate),
# prefix="/auth",
# tags=["auth"],
# )
# api_router.include_router(
# fastapi_users.get_reset_password_router(),
# prefix="/auth",
# tags=["auth"],
# )
# api_router.include_router(
# fastapi_users.get_verify_router(UserRead),
# prefix="/auth",
# tags=["auth"],
# )
api_router.include_router(
fastapi_users.get_users_router(UserRead, UserUpdate),
prefix="/users",
tags=["users"],
)
17 changes: 9 additions & 8 deletions backend/app/api/api_v1/endpoints/adminmsgs.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession

from app import crud, models, schemas
from app.api import deps
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session

router = APIRouter()


@router.get("/", response_model=list[schemas.AdminMessage])
def read_adminmsgs(
db: Session = Depends(deps.get_db),
@router.get("/", response_model=list[schemas.AdminMessageRead])
async def read_adminmsgs(
db: AsyncSession = Depends(deps.get_db),
skip: int = 0,
limit: int = 100,
) -> list[schemas.AdminMessage]:
) -> list[schemas.AdminMessageRead]:
"""Retrieve all admin messages"""
users = [
schemas.AdminMessage.model_validate(db_obj)
for db_obj in crud.adminmessage.get_multi(db, skip=skip, limit=limit)
schemas.AdminMessageRead.model_validate(db_obj)
for db_obj in await crud.adminmessage.get_multi(db, skip=skip, limit=limit)
]
return users
24 changes: 16 additions & 8 deletions backend/app/api/deps.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
from typing import Iterator
from typing import AsyncGenerator, AsyncIterator

from app.db.session import SessionLocal
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker

from app.db.session import SessionLocal, engine

def get_db() -> Iterator[SessionLocal]: # type: ignore[valid-type]
try:
db = SessionLocal()
yield db
finally:
db.close()

async def get_db() -> AsyncIterator[SessionLocal]: # type: ignore[valid-type]
db = SessionLocal()
yield db
await db.close()


async_session_maker = async_sessionmaker(engine, expire_on_commit=False)


async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
async with async_session_maker() as session:
yield session
Loading

0 comments on commit afead65

Please sign in to comment.