From 732b14866c1af37dc593ece211104ce4895d207f Mon Sep 17 00:00:00 2001 From: swve Date: Sat, 28 Oct 2023 19:36:58 +0200 Subject: [PATCH] wip: initiate user creation --- apps/api/app.py | 3 +- apps/api/requirements.txt | 2 + apps/api/src/core/events/database.py | 26 ++++++++---- apps/api/src/rewrite/__init__.py | 0 apps/api/src/rewrite/routers/users.py | 23 +++++++++++ apps/api/src/rewrite/services/db/__init__.py | 0 apps/api/src/rewrite/services/db/users.py | 32 +++++++++++++++ apps/api/src/rewrite/services/users/users.py | 43 ++++++++++++++++++++ apps/api/src/router.py | 6 +++ docker-compose.yml | 2 +- 10 files changed, 126 insertions(+), 11 deletions(-) create mode 100644 apps/api/src/rewrite/__init__.py create mode 100644 apps/api/src/rewrite/routers/users.py create mode 100644 apps/api/src/rewrite/services/db/__init__.py create mode 100644 apps/api/src/rewrite/services/db/users.py create mode 100644 apps/api/src/rewrite/services/users/users.py diff --git a/apps/api/app.py b/apps/api/app.py index 3a7f62c6..dc066251 100644 --- a/apps/api/app.py +++ b/apps/api/app.py @@ -1,7 +1,7 @@ from fastapi import FastAPI, Request from config.config import LearnHouseConfig, get_learnhouse_config from src.core.events.events import shutdown_app, startup_app -from src.router import v1_router +from src.router import v1_router, rewrite from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from fastapi.staticfiles import StaticFiles @@ -60,6 +60,7 @@ app.mount("/content", StaticFiles(directory="content"), name="content") # Global Routes app.include_router(v1_router) +app.include_router(rewrite) # General Routes @app.get("/") diff --git a/apps/api/requirements.txt b/apps/api/requirements.txt index 3f69c553..afec2b0a 100644 --- a/apps/api/requirements.txt +++ b/apps/api/requirements.txt @@ -1,8 +1,10 @@ fastapi==0.101.1 pydantic>=1.8.0,<2.0.0 +sqlmodel==0.0.10 uvicorn==0.23.2 pymongo==4.3.3 motor==3.1.1 +psycopg2 python-multipart boto3 botocore diff --git a/apps/api/src/core/events/database.py b/apps/api/src/core/events/database.py index 174575a6..984c23c4 100644 --- a/apps/api/src/core/events/database.py +++ b/apps/api/src/core/events/database.py @@ -1,19 +1,27 @@ import logging from fastapi import FastAPI import motor.motor_asyncio +from sqlmodel import Field, SQLModel, Session, create_engine +from src.rewrite.services.db import users +engine = create_engine('postgresql://learnhouse:learnhouse@db:5432/learnhouse', echo=True) +SQLModel.metadata.create_all(engine) async def connect_to_db(app: FastAPI): - logging.info("Connecting to database...") - try: - app.mongodb_client = motor.motor_asyncio.AsyncIOMotorClient( # type: ignore - app.learnhouse_config.database_config.mongodb_connection_string) # type: ignore - app.db = app.mongodb_client["learnhouse"] # type: ignore - logging.info("Connected to database!") - except Exception as e: - logging.error("Failed to connect to database!") - logging.error(e) + app.db_engine = engine # type: ignore + logging.info("LearnHouse database has been started.") + + SQLModel.metadata.create_all(engine) + + # mongodb + app.mongodb_client = motor.motor_asyncio.AsyncIOMotorClient( # type: ignore + app.learnhouse_config.database_config.mongodb_connection_string) # type: ignore + app.db = app.mongodb_client["learnhouse"] # type: ignore + +def get_db_session(): + with Session(engine) as session: + yield session async def close_database(app: FastAPI): app.mongodb_client.close() # type: ignore diff --git a/apps/api/src/rewrite/__init__.py b/apps/api/src/rewrite/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/api/src/rewrite/routers/users.py b/apps/api/src/rewrite/routers/users.py new file mode 100644 index 00000000..bd01e15e --- /dev/null +++ b/apps/api/src/rewrite/routers/users.py @@ -0,0 +1,23 @@ +from fastapi import APIRouter, Depends, Request +from sqlmodel import Session +from src.core.events.database import get_db_session + +from src.rewrite.services.db.users import UserCreate, UserRead +from src.rewrite.services.users.users import create_user + + +router = APIRouter() + + +@router.post("/", response_model=UserRead, tags=["users"]) +async def api_create_user( + *, + request: Request, + db_session: Session = Depends(get_db_session), + user_object: UserCreate, + org_slug: str +): + """ + Create new user + """ + return await create_user(request, db_session, None, user_object, org_slug) diff --git a/apps/api/src/rewrite/services/db/__init__.py b/apps/api/src/rewrite/services/db/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/api/src/rewrite/services/db/users.py b/apps/api/src/rewrite/services/db/users.py new file mode 100644 index 00000000..a02d6c19 --- /dev/null +++ b/apps/api/src/rewrite/services/db/users.py @@ -0,0 +1,32 @@ +from typing import Optional +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class UserBase(SQLModel): + username: str + first_name: str + last_name: str + email: str + avatar_image: Optional[str] = "" + bio: Optional[str] = "" + + +class UserCreate(UserBase): + password: str + + +class UserUpdate(UserBase): + password: Optional[str] = None + + +class UserRead(UserBase): + id: int + + +class User(UserBase, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + password: str = "" + user_uuid: str = "" + email_verified: bool = False + creation_date: str = "" + update_date: str = "" diff --git a/apps/api/src/rewrite/services/users/users.py b/apps/api/src/rewrite/services/users/users.py new file mode 100644 index 00000000..5bb69ecb --- /dev/null +++ b/apps/api/src/rewrite/services/users/users.py @@ -0,0 +1,43 @@ +from datetime import datetime +from uuid import uuid4 +from fastapi import Depends, Request +from sqlmodel import Session +from src.rewrite.services.db.users import User, UserCreate +from src.security.security import security_hash_password +from src.services.users.schemas.users import PublicUser + + +async def create_user( + request: Request, + db_session: Session, + current_user: PublicUser | None, + user_object: UserCreate, + org_slug: str, +): + user = User.from_orm(user_object) + + # Complete the user object + user.user_uuid = f"user_{uuid4()}" + user.password = await security_hash_password(user_object.password) + user.email_verified = False + user.creation_date = str(datetime.now()) + user.update_date = str(datetime.now()) + + # Verifications + #todo: add username uniqueness verification + #todo: add email uniqueness verification + + + #todo: add user to org as member if org is not None + + # Exclude unset values + user_data = user.dict(exclude_unset=True) + for key, value in user_data.items(): + setattr(user, key, value) + + # Add user to database + db_session.add(user) + db_session.commit() + db_session.refresh(user) + + return user diff --git a/apps/api/src/router.py b/apps/api/src/router.py index 895873b3..08a5325b 100644 --- a/apps/api/src/router.py +++ b/apps/api/src/router.py @@ -1,5 +1,7 @@ from fastapi import APIRouter, Depends +from src import rewrite from src.routers import blocks, dev, trail, users, auth, orgs, roles +from src.rewrite.routers import users as rw_users from src.routers.courses import chapters, collections, courses, activities from src.routers.install import install from src.services.dev.dev import isDevModeEnabledOrRaise @@ -7,6 +9,7 @@ from src.services.install.install import isInstallModeEnabled v1_router = APIRouter(prefix="/api/v1") +rewrite = APIRouter(prefix="/api/rewrite") # API Routes @@ -35,3 +38,6 @@ v1_router.include_router( tags=["install"], dependencies=[Depends(isInstallModeEnabled)], ) + +# Rewrite Routes +rewrite.include_router(rw_users.router, prefix="/users", tags=["users"]) diff --git a/docker-compose.yml b/docker-compose.yml index eb8a2dab..a1bd0ef5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ services: - .:/usr/learnhouse environment: - LEARNHOUSE_COOKIE_DOMAIN=.localhost - postgresql: + db: image: postgres:16-alpine restart: always ports: