mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: init easy backend install from cli
This commit is contained in:
parent
5c7c405e41
commit
a6742d17c1
9 changed files with 134 additions and 55 deletions
112
apps/api/cli.py
Normal file
112
apps/api/cli.py
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
from typing import Annotated
|
||||
from pydantic import EmailStr
|
||||
from sqlalchemy import create_engine
|
||||
from sqlmodel import SQLModel, Session
|
||||
import typer
|
||||
|
||||
from config.config import get_learnhouse_config
|
||||
from src.db.organizations import OrganizationCreate
|
||||
from src.db.users import UserCreate
|
||||
from src.services.install.install import (
|
||||
install_create_organization,
|
||||
install_create_organization_user,
|
||||
install_default_elements,
|
||||
)
|
||||
|
||||
cli = typer.Typer()
|
||||
|
||||
|
||||
@cli.command()
|
||||
def install(
|
||||
short: Annotated[bool, typer.Option(help="Install with predefined values")] = False
|
||||
):
|
||||
# Get the database session
|
||||
learnhouse_config = get_learnhouse_config()
|
||||
engine = create_engine(
|
||||
learnhouse_config.database_config.sql_connection_string, echo=False, pool_pre_ping=True # type: ignore
|
||||
)
|
||||
SQLModel.metadata.create_all(engine)
|
||||
|
||||
db_session = Session(engine)
|
||||
|
||||
if short:
|
||||
# Install the default elements
|
||||
print("Installing default elements...")
|
||||
install_default_elements(db_session)
|
||||
print("Default elements installed ✅")
|
||||
|
||||
# Create the Organization
|
||||
print("Creating default organization...")
|
||||
org = OrganizationCreate(
|
||||
name="Default Organization",
|
||||
description="Default Organization",
|
||||
slug="default",
|
||||
email="",
|
||||
logo_image="",
|
||||
)
|
||||
install_create_organization(org, db_session)
|
||||
print("Default organization created ✅")
|
||||
|
||||
# Create Organization User
|
||||
print("Creating default organization user...")
|
||||
user = UserCreate(
|
||||
username="admin", email=EmailStr("admin@school.io"), password="adminsecret"
|
||||
)
|
||||
install_create_organization_user(user, "default", db_session)
|
||||
print("Default organization user created ✅")
|
||||
|
||||
# Show the user how to login
|
||||
print("Installation completed ✅")
|
||||
print("")
|
||||
print("Login with the following credentials:")
|
||||
print("email: admin@school.io")
|
||||
print("password: adminsecret")
|
||||
|
||||
else:
|
||||
# Install the default elements
|
||||
print("Installing default elements...")
|
||||
install_default_elements(db_session)
|
||||
print("Default elements installed ✅")
|
||||
|
||||
# Create the Organization
|
||||
print("Creating your organization...")
|
||||
orgname = typer.prompt("What's shall we call your organization?")
|
||||
slug = typer.prompt(
|
||||
"What's the slug for your organization? (e.g. school, acme)"
|
||||
)
|
||||
org = OrganizationCreate(
|
||||
name=orgname,
|
||||
description="Default Organization",
|
||||
slug=slug.lower(),
|
||||
email="",
|
||||
logo_image="",
|
||||
)
|
||||
install_create_organization(org, db_session)
|
||||
print(orgname + " Organization created ✅")
|
||||
|
||||
# Create Organization User
|
||||
print("Creating your organization user...")
|
||||
username = typer.prompt("What's the username for the user?")
|
||||
email = typer.prompt("What's the email for the user?")
|
||||
password = typer.prompt("What's the password for the user?", hide_input=True)
|
||||
user = UserCreate(username=username, email=EmailStr(email), password=password)
|
||||
install_create_organization_user(user, slug, db_session)
|
||||
print(username + " user created ✅")
|
||||
|
||||
# Show the user how to login
|
||||
print("Installation completed ✅")
|
||||
print("")
|
||||
print("Login with the following credentials:")
|
||||
print("email: " + email)
|
||||
print("password: The password you entered")
|
||||
|
||||
|
||||
|
||||
|
||||
@cli.command()
|
||||
def main():
|
||||
cli()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
cli()
|
||||
45
apps/api/poetry.lock
generated
45
apps/api/poetry.lock
generated
|
|
@ -3186,54 +3186,21 @@ telegram = ["requests"]
|
|||
|
||||
[[package]]
|
||||
name = "typer"
|
||||
version = "0.12.0"
|
||||
version = "0.12.3"
|
||||
description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "typer-0.12.0-py3-none-any.whl", hash = "sha256:0441a0bb8962fb4383b8537ada9f7eb2d0deda0caa2cfe7387cc221290f617e4"},
|
||||
{file = "typer-0.12.0.tar.gz", hash = "sha256:900fe786ce2d0ea44653d3c8ee4594a22a496a3104370ded770c992c5e3c542d"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
typer-cli = "0.12.0"
|
||||
typer-slim = {version = "0.12.0", extras = ["standard"]}
|
||||
|
||||
[[package]]
|
||||
name = "typer-cli"
|
||||
version = "0.12.0"
|
||||
description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "typer_cli-0.12.0-py3-none-any.whl", hash = "sha256:7b7e2dd49f59974bb5a869747045d5444b17bffb851e006cd424f602d3578104"},
|
||||
{file = "typer_cli-0.12.0.tar.gz", hash = "sha256:603ed3d5a278827bd497e4dc73a39bb714b230371c8724090b0de2abdcdd9f6e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
typer-slim = {version = "0.12.0", extras = ["standard"]}
|
||||
|
||||
[[package]]
|
||||
name = "typer-slim"
|
||||
version = "0.12.0"
|
||||
description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "typer_slim-0.12.0-py3-none-any.whl", hash = "sha256:ddd7042b29a32140528caa415750bcae54113ba0c32270ca11a6f64069ddadf9"},
|
||||
{file = "typer_slim-0.12.0.tar.gz", hash = "sha256:3e8a3f17286b173d76dca0fd4e02651c9a2ce1467b3754876b1ac4bd72572beb"},
|
||||
{file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"},
|
||||
{file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=8.0.0"
|
||||
rich = {version = ">=10.11.0", optional = true, markers = "extra == \"standard\""}
|
||||
shellingham = {version = ">=1.3.0", optional = true, markers = "extra == \"standard\""}
|
||||
rich = ">=10.11.0"
|
||||
shellingham = ">=1.3.0"
|
||||
typing-extensions = ">=3.7.4.3"
|
||||
|
||||
[package.extras]
|
||||
all = ["rich (>=10.11.0)", "shellingham (>=1.3.0)"]
|
||||
standard = ["rich (>=10.11.0)", "shellingham (>=1.3.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.10.0"
|
||||
|
|
@ -3730,4 +3697,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.12"
|
||||
content-hash = "0f7dbd7ca5511470158142c475dab8fad69ffb35fb31da437847bda67f3efbcb"
|
||||
content-hash = "849c99445d4d0dc5fb06a50359c4e166b357f3705003c2989103a86fd388decb"
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ sentry-sdk = {extras = ["fastapi"], version = "^1.45.0"}
|
|||
sqlmodel = "^0.0.16"
|
||||
tiktoken = "^0.6.0"
|
||||
uvicorn = "0.29.0"
|
||||
typer = "^0.12.3"
|
||||
|
||||
[build-system]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ from src.db.install import InstallRead
|
|||
from src.core.events.database import get_db_session
|
||||
from src.db.organizations import OrganizationCreate
|
||||
from src.db.users import UserCreate
|
||||
|
||||
from src.services.install.install import (
|
||||
create_install_instance,
|
||||
get_latest_install_instance,
|
||||
|
|
@ -43,7 +42,7 @@ async def api_get_latest_install_instance(
|
|||
async def api_install_def_elements(
|
||||
db_session=Depends(get_db_session),
|
||||
):
|
||||
elements = await install_default_elements(db_session)
|
||||
elements = install_default_elements(db_session)
|
||||
|
||||
return elements
|
||||
|
||||
|
|
@ -53,7 +52,7 @@ async def api_install_org(
|
|||
org: OrganizationCreate,
|
||||
db_session=Depends(get_db_session),
|
||||
):
|
||||
organization = await install_create_organization(org, db_session)
|
||||
organization = install_create_organization(org, db_session)
|
||||
|
||||
return organization
|
||||
|
||||
|
|
@ -64,7 +63,7 @@ async def api_install_user(
|
|||
org_slug: str,
|
||||
db_session=Depends(get_db_session),
|
||||
):
|
||||
user = await install_create_organization_user(data, org_slug, db_session)
|
||||
user = install_create_organization_user(data, org_slug, db_session)
|
||||
|
||||
return user
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ async def authenticate_user(
|
|||
user = await security_get_user(request, db_session, email)
|
||||
if not user:
|
||||
return False
|
||||
if not await security_verify_password(password, user.password):
|
||||
if not security_verify_password(password, user.password):
|
||||
return False
|
||||
return user
|
||||
|
||||
|
|
|
|||
|
|
@ -17,11 +17,11 @@ ALGORITHM = "HS256"
|
|||
### 🔒 Passwords Hashing ##############################################################
|
||||
|
||||
|
||||
async def security_hash_password(password: str):
|
||||
def security_hash_password(password: str):
|
||||
return pbkdf2_sha256.hash(password)
|
||||
|
||||
|
||||
async def security_verify_password(plain_password: str, hashed_password: str):
|
||||
def security_verify_password(plain_password: str, hashed_password: str):
|
||||
return pbkdf2_sha256.verify(plain_password, hashed_password)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ async def update_install_instance(
|
|||
|
||||
|
||||
# Install Default roles
|
||||
async def install_default_elements(db_session: Session):
|
||||
def install_default_elements(db_session: Session):
|
||||
"""
|
||||
"""
|
||||
# remove all default roles
|
||||
|
|
@ -300,7 +300,7 @@ async def install_default_elements(db_session: Session):
|
|||
|
||||
|
||||
# Organization creation
|
||||
async def install_create_organization(
|
||||
def install_create_organization(
|
||||
org_object: OrganizationCreate, db_session: Session
|
||||
):
|
||||
org = Organization.model_validate(org_object)
|
||||
|
|
@ -364,14 +364,14 @@ async def install_create_organization(
|
|||
return org
|
||||
|
||||
|
||||
async def install_create_organization_user(
|
||||
def install_create_organization_user(
|
||||
user_object: UserCreate, org_slug: str, db_session: Session
|
||||
):
|
||||
user = User.model_validate(user_object)
|
||||
|
||||
# Complete the user object
|
||||
user.user_uuid = f"user_{uuid4()}"
|
||||
user.password = await security_hash_password(user_object.password)
|
||||
user.password = security_hash_password(user_object.password)
|
||||
user.email_verified = False
|
||||
user.creation_date = str(datetime.now())
|
||||
user.update_date = str(datetime.now())
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ async def change_password_with_reset_code(
|
|||
)
|
||||
|
||||
# Change password
|
||||
user.password = await security_hash_password(new_password)
|
||||
user.password = security_hash_password(new_password)
|
||||
db_session.add(user)
|
||||
|
||||
db_session.commit()
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ async def create_user(
|
|||
|
||||
# Complete the user object
|
||||
user.user_uuid = f"user_{uuid4()}"
|
||||
user.password = await security_hash_password(user_object.password)
|
||||
user.password = security_hash_password(user_object.password)
|
||||
user.email_verified = False
|
||||
user.creation_date = str(datetime.now())
|
||||
user.update_date = str(datetime.now())
|
||||
|
|
@ -164,7 +164,7 @@ async def create_user_without_org(
|
|||
|
||||
# Complete the user object
|
||||
user.user_uuid = f"user_{uuid4()}"
|
||||
user.password = await security_hash_password(user_object.password)
|
||||
user.password = security_hash_password(user_object.password)
|
||||
user.email_verified = False
|
||||
user.creation_date = str(datetime.now())
|
||||
user.update_date = str(datetime.now())
|
||||
|
|
@ -340,13 +340,13 @@ async def update_user_password(
|
|||
# RBAC check
|
||||
await rbac_check(request, current_user, "update", user.user_uuid, db_session)
|
||||
|
||||
if not await security_verify_password(form.old_password, user.password):
|
||||
if not security_verify_password(form.old_password, user.password):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="Wrong password"
|
||||
)
|
||||
|
||||
# Update user
|
||||
user.password = await security_hash_password(form.new_password)
|
||||
user.password = security_hash_password(form.new_password)
|
||||
user.update_date = str(datetime.now())
|
||||
|
||||
# Update user in database
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue