Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: setup initial support for local login (#48) #82

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading