mirror of
https://github.com/DarrylNixon/ghostforge
synced 2024-04-22 06:27:20 -07:00
back to SQLmodel...
This commit is contained in:
parent
6f81ef699d
commit
47931af84f
9 changed files with 158 additions and 26 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -158,3 +158,4 @@ cython_debug/
|
||||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
#.idea/
|
#.idea/
|
||||||
|
**/.DS_Store
|
||||||
|
|
|
@ -1,22 +1,37 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from fastapi import Depends
|
from fastapi import Depends
|
||||||
from fastapi_users.db import SQLAlchemyBaseUserTableUUID
|
from fastapi_users_db_sqlmodel import SQLModelBaseUserDB
|
||||||
from fastapi_users.db import SQLAlchemyUserDatabase
|
from fastapi_users_db_sqlmodel import SQLModelUserDatabaseAsync
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy.ext.asyncio import create_async_engine
|
from sqlalchemy.ext.asyncio import create_async_engine
|
||||||
from sqlalchemy.orm import DeclarativeBase
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
|
# from fastapi_users.db import SQLAlchemyBaseUserTableUUID
|
||||||
|
# from fastapi_users.db import SQLAlchemyUserDatabase
|
||||||
|
# from sqlalchemy.orm import DeclarativeBase
|
||||||
|
|
||||||
class Base(DeclarativeBase):
|
# from sqlmodel import SQLModel
|
||||||
|
|
||||||
|
|
||||||
|
# class Base(DeclarativeBase):
|
||||||
|
# pass
|
||||||
|
|
||||||
|
|
||||||
|
# Create a "Base" class for use with fastapi-users-db-sqlmodel and sqlalchemy
|
||||||
|
class Base(SQLModelBaseUserDB):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class User(SQLAlchemyBaseUserTableUUID, Base):
|
# Create a "User" class for user with fastapi-users-db-sqlmodel
|
||||||
|
class User(Base, table=True):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# class User(SQLAlchemyBaseUserTableUUID, Base):
|
||||||
|
# pass
|
||||||
|
|
||||||
|
|
||||||
database_url = (
|
database_url = (
|
||||||
f'postgresql+asyncpg://{os.environ.get("POSTGRES_USER")}:'
|
f'postgresql+asyncpg://{os.environ.get("POSTGRES_USER")}:'
|
||||||
+ f'{os.environ.get("POSTGRES_PASSWORD")}@'
|
+ f'{os.environ.get("POSTGRES_PASSWORD")}@'
|
||||||
|
@ -41,4 +56,4 @@ async def create_db_and_tables():
|
||||||
|
|
||||||
|
|
||||||
async def get_user_db(session: AsyncSession = Depends(get_session)):
|
async def get_user_db(session: AsyncSession = Depends(get_session)):
|
||||||
yield SQLAlchemyUserDatabase(session, User)
|
yield SQLModelUserDatabaseAsync(session, User)
|
||||||
|
|
105
ghostforge/ghosts.py
Normal file
105
ghostforge/ghosts.py
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
from datetime import datetime
|
||||||
|
from datetime import timezone
|
||||||
|
from typing import Annotated
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from fastapi import APIRouter
|
||||||
|
from fastapi import Depends
|
||||||
|
from fastapi import HTTPException
|
||||||
|
from fastapi import Query
|
||||||
|
from fastapi import Request
|
||||||
|
from sqlalchemy import select
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from sqlmodel import Column
|
||||||
|
from sqlmodel import Field
|
||||||
|
from sqlmodel import SQLModel
|
||||||
|
from sqlmodel import TIMESTAMP
|
||||||
|
|
||||||
|
from ghostforge.db import get_session
|
||||||
|
from ghostforge.db import User
|
||||||
|
from ghostforge.htmljson import HtmlJson
|
||||||
|
from ghostforge.users import get_current_user
|
||||||
|
|
||||||
|
gf = APIRouter()
|
||||||
|
hj = HtmlJson()
|
||||||
|
|
||||||
|
|
||||||
|
class Ghost(SQLModel, table=True):
|
||||||
|
id: Optional[int] = Field(primary_key=True)
|
||||||
|
first_name: Optional[str] = Field()
|
||||||
|
last_name: Optional[str] = Field()
|
||||||
|
middle_name: Optional[str] = Field()
|
||||||
|
birthdate: Optional[datetime] = Field(
|
||||||
|
sa_column=Column(
|
||||||
|
TIMESTAMP(timezone=True),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class GhostCreate(Ghost, table=False):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class GhostUpdate(Ghost, table=False):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class GhostRead(Ghost, table=False):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@gf.get("/ghosts/{ghost_id}")
|
||||||
|
@hj.html_or_json("ghost.html")
|
||||||
|
async def read_ghost(
|
||||||
|
ghost_id: int,
|
||||||
|
current_user: Annotated[User, Depends(get_current_user())],
|
||||||
|
session: AsyncSession = Depends(get_session),
|
||||||
|
request: Request = None,
|
||||||
|
):
|
||||||
|
ghost = await session.get(Ghost, ghost_id)
|
||||||
|
data = {"crumbs": [("ghosts", "/ghosts"), (ghost.id, False)]}
|
||||||
|
request.state.ghostforge = data | getattr(request.state, "ghostforge", {})
|
||||||
|
return ghost
|
||||||
|
|
||||||
|
|
||||||
|
@gf.put("/ghosts/{ghost_id}")
|
||||||
|
async def update_ghost(
|
||||||
|
ghost_id: int,
|
||||||
|
ghost: GhostUpdate,
|
||||||
|
current_user: Annotated[User, Depends(get_current_user())],
|
||||||
|
session: AsyncSession = Depends(get_session),
|
||||||
|
request: Request = None,
|
||||||
|
):
|
||||||
|
db_ghost = await session.get(Ghost, ghost_id)
|
||||||
|
if db_ghost is None:
|
||||||
|
raise HTTPException(status_code=404, detail="No ghost with that ID")
|
||||||
|
for k, v in ghost.dict().items():
|
||||||
|
setattr(db_ghost, k, v)
|
||||||
|
await session.commit()
|
||||||
|
await session.refresh(db_ghost)
|
||||||
|
return db_ghost
|
||||||
|
|
||||||
|
|
||||||
|
@gf.post("/ghosts")
|
||||||
|
async def create_ghost(
|
||||||
|
ghost: GhostCreate,
|
||||||
|
current_user: Annotated[User, Depends(get_current_user())],
|
||||||
|
session: AsyncSession = Depends(get_session),
|
||||||
|
request: Request = None,
|
||||||
|
):
|
||||||
|
new_ghost = Ghost.from_orm(ghost)
|
||||||
|
ghost.birthdate = ghost.birthdate.replace(tzinfo=timezone.utc)
|
||||||
|
print(ghost.birthdate)
|
||||||
|
session.add(new_ghost)
|
||||||
|
await session.commit()
|
||||||
|
await session.refresh(new_ghost)
|
||||||
|
return new_ghost
|
||||||
|
|
||||||
|
|
||||||
|
@gf.get("/ghosts")
|
||||||
|
async def read_users(
|
||||||
|
offset: int = 0, limit: int = Query(default=100, lte=100), session: Session = Depends(get_session)
|
||||||
|
):
|
||||||
|
ghosts = await session.execute(select(Ghost).offset(offset).limit(limit))
|
||||||
|
return ghosts.scalars().all()
|
|
@ -10,7 +10,7 @@ from fastapi.staticfiles import StaticFiles
|
||||||
|
|
||||||
from ghostforge.db import create_db_and_tables
|
from ghostforge.db import create_db_and_tables
|
||||||
from ghostforge.db import User
|
from ghostforge.db import User
|
||||||
from ghostforge.htmljson import HtmlJson
|
from ghostforge.ghosts import gf as gf_ghosts
|
||||||
from ghostforge.templates import templates
|
from ghostforge.templates import templates
|
||||||
from ghostforge.users import fastapi_users
|
from ghostforge.users import fastapi_users
|
||||||
from ghostforge.users import get_current_user
|
from ghostforge.users import get_current_user
|
||||||
|
@ -20,15 +20,10 @@ from ghostforge.users import UserRead
|
||||||
from ghostforge.users import UserUpdate
|
from ghostforge.users import UserUpdate
|
||||||
from ghostforge.users import web_backend
|
from ghostforge.users import web_backend
|
||||||
|
|
||||||
# from ghostforge.users import gf as gf_users
|
|
||||||
|
|
||||||
# from ghostforge.models import User
|
|
||||||
|
|
||||||
gf = FastAPI()
|
gf = FastAPI()
|
||||||
hj = HtmlJson()
|
|
||||||
gf.mount("/static", StaticFiles(directory="ghostforge/static"), name="static")
|
gf.mount("/static", StaticFiles(directory="ghostforge/static"), name="static")
|
||||||
|
gf.include_router(gf_ghosts)
|
||||||
# gf.include_router(gf_users)
|
|
||||||
|
|
||||||
gf.include_router(fastapi_users.get_auth_router(jwt_backend), prefix="/auth/jwt", tags=["auth"])
|
gf.include_router(fastapi_users.get_auth_router(jwt_backend), prefix="/auth/jwt", tags=["auth"])
|
||||||
gf.include_router(fastapi_users.get_auth_router(web_backend), prefix="/auth/cookie", tags=["auth"])
|
gf.include_router(fastapi_users.get_auth_router(web_backend), prefix="/auth/cookie", tags=["auth"])
|
||||||
|
@ -54,6 +49,11 @@ gf.include_router(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@gf.on_event("startup")
|
||||||
|
async def on_startup():
|
||||||
|
await create_db_and_tables()
|
||||||
|
|
||||||
|
|
||||||
@gf.get("/", response_class=HTMLResponse)
|
@gf.get("/", response_class=HTMLResponse)
|
||||||
async def home(request: Request, current_user=Depends(get_current_user())):
|
async def home(request: Request, current_user=Depends(get_current_user())):
|
||||||
if current_user:
|
if current_user:
|
||||||
|
@ -77,15 +77,8 @@ async def logout(response: Response):
|
||||||
|
|
||||||
|
|
||||||
@gf.get("/dashboard")
|
@gf.get("/dashboard")
|
||||||
async def authenticated_route(
|
async def dashboard(request: Request, current_user: Annotated[User, Depends(get_current_user(optional=True))]):
|
||||||
request: Request, current_user: Annotated[User, Depends(get_current_user(optional=True))]
|
|
||||||
):
|
|
||||||
if not current_user:
|
if not current_user:
|
||||||
return RedirectResponse(url="/login")
|
return RedirectResponse(url="/login")
|
||||||
crumbs = [("dashboard", False), (current_user.email, False)]
|
crumbs = [("dashboard", False)]
|
||||||
return templates.TemplateResponse("dashboard.html", {"request": request, "crumbs": crumbs, "user": current_user})
|
return templates.TemplateResponse("dashboard.html", {"request": request, "crumbs": crumbs, "user": current_user})
|
||||||
|
|
||||||
|
|
||||||
@gf.on_event("startup")
|
|
||||||
async def on_startup():
|
|
||||||
await create_db_and_tables()
|
|
||||||
|
|
16
ghostforge/templates/ghost.html
Normal file
16
ghostforge/templates/ghost.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column col-12">
|
||||||
|
<h1>Words!</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column col-3"></div>
|
||||||
|
<div class="column col-6">
|
||||||
|
<h5>More words.</h5>
|
||||||
|
</div>
|
||||||
|
<div class="column col-3"></div>
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
|
@ -2,6 +2,7 @@
|
||||||
<section class="navbar-section">
|
<section class="navbar-section">
|
||||||
{% if crumbs %}
|
{% if crumbs %}
|
||||||
<ul class="breadcrumb">
|
<ul class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><kbd>{{ user.email.split('@')[0] }}</kbd></li>
|
||||||
{% for name, url in crumbs %}
|
{% for name, url in crumbs %}
|
||||||
<li class="breadcrumb-item">
|
<li class="breadcrumb-item">
|
||||||
{% if url %}
|
{% if url %}
|
||||||
|
|
|
@ -13,11 +13,12 @@ from fastapi_users.authentication import AuthenticationBackend
|
||||||
from fastapi_users.authentication import BearerTransport
|
from fastapi_users.authentication import BearerTransport
|
||||||
from fastapi_users.authentication import CookieTransport
|
from fastapi_users.authentication import CookieTransport
|
||||||
from fastapi_users.authentication import JWTStrategy
|
from fastapi_users.authentication import JWTStrategy
|
||||||
from fastapi_users.db import SQLAlchemyUserDatabase
|
from fastapi_users_db_sqlmodel import SQLModelUserDatabase
|
||||||
|
|
||||||
from ghostforge.db import get_user_db
|
from ghostforge.db import get_user_db
|
||||||
from ghostforge.db import User
|
from ghostforge.db import User
|
||||||
|
|
||||||
|
|
||||||
SECRET = os.environ.get("GHOSTFORGE_JWT_SECRET")
|
SECRET = os.environ.get("GHOSTFORGE_JWT_SECRET")
|
||||||
|
|
||||||
gf = APIRouter()
|
gf = APIRouter()
|
||||||
|
@ -49,7 +50,7 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]):
|
||||||
print(f"Verification requested for user {user.id}. Verification token: {token}")
|
print(f"Verification requested for user {user.id}. Verification token: {token}")
|
||||||
|
|
||||||
|
|
||||||
async def get_user_manager(user_db: SQLAlchemyUserDatabase = Depends(get_user_db)):
|
async def get_user_manager(user_db: SQLModelUserDatabase = Depends(get_user_db)):
|
||||||
yield UserManager(user_db)
|
yield UserManager(user_db)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,8 @@ dependencies = [
|
||||||
"greenlet==2.0.2",
|
"greenlet==2.0.2",
|
||||||
"jinja2==3.1.2",
|
"jinja2==3.1.2",
|
||||||
"fastapi-users==11.0.0",
|
"fastapi-users==11.0.0",
|
||||||
"fastapi-users-db-sqlalchemy==5.0.0",
|
"fastapi-users-db-sqlmodel==0.3.0",
|
||||||
"httpx==0.24.1",
|
"sqlmodel==0.0.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
|
|
Loading…
Reference in a new issue