ohmyapi/src/ohmyapi/cli.py

171 lines
5 KiB
Python
Raw Normal View History

2025-09-27 00:29:45 +02:00
import asyncio
import atexit
2025-09-27 00:29:45 +02:00
import importlib
import sys
2025-09-28 15:41:01 +02:00
from getpass import getpass
from pathlib import Path
2025-09-27 00:29:45 +02:00
import typer
import uvicorn
2025-09-28 15:41:01 +02:00
from ohmyapi.core import runtime, scaffolding
2025-10-08 12:45:27 +02:00
from ohmyapi.core.logging import setup_logging
logger = setup_logging()
2025-09-27 00:29:45 +02:00
2025-09-28 15:41:01 +02:00
app = typer.Typer(
help="OhMyAPI — Django-flavored FastAPI scaffolding with tightly integrated TortoiseORM."
)
2025-09-27 00:29:45 +02:00
@app.command()
def startproject(name: str):
2025-09-27 16:50:15 +02:00
"""Create a new OhMyAPI project in the given directory."""
2025-09-27 00:29:45 +02:00
scaffolding.startproject(name)
2025-10-08 12:45:27 +02:00
logger.info(f"✅ Project '{name}' created successfully.")
logger.info(f"🔧 Next, configure your project in {name}/settings.py")
2025-09-27 00:29:45 +02:00
@app.command()
def startapp(app_name: str, root: str = "."):
2025-09-27 16:50:15 +02:00
"""Create a new app with the given name in your OhMyAPI project."""
2025-09-27 00:29:45 +02:00
scaffolding.startapp(app_name, root)
2025-10-08 12:45:27 +02:00
print(f"✅ App '{app_name}' created in project '{root}' successfully.")
print(f"🔧 Remember to add '{app_name}' to your INSTALLED_APPS!")
2025-09-27 00:29:45 +02:00
2025-10-02 05:47:48 +02:00
@app.command()
def dockerize(root: str = "."):
"""Create template Dockerfile and docker-compose.yml."""
scaffolding.copy_static("docker", root)
2025-10-08 12:45:27 +02:00
logger.info(f"✅ Templates created successfully.")
logger.info(f"🔧 Next, run `docker compose up -d --build`")
2025-10-02 05:47:48 +02:00
2025-09-27 00:29:45 +02:00
@app.command()
def serve(root: str = ".", host="127.0.0.1", port=8000):
"""
Run this project in via uvicorn.
"""
project_path = Path(root)
project = runtime.Project(project_path)
app_instance = project.configure_app(project.app())
2025-10-08 12:45:27 +02:00
uvicorn.run(app_instance, host=host, port=int(port), reload=False, log_config=None)
2025-09-27 00:29:45 +02:00
@app.command()
def shell(root: str = "."):
2025-10-02 05:47:48 +02:00
"""An interactive shell with your loaded project runtime."""
2025-09-27 00:29:45 +02:00
project_path = Path(root).resolve()
project = runtime.Project(project_path)
banner = f"""
OhMyAPI Project Shell: {getattr(project.settings, 'PROJECT_NAME', 'MyProject')}
Find your loaded project singleton via identifier: `p`; i.e.: `p.apps`
"""
async def init_and_cleanup():
try:
await project.init_orm()
return True
except Exception as e:
print(f"Failed to initialize ORM: {e}")
return False
async def cleanup():
try:
await project.close_orm()
print("Tortoise ORM closed successfully.")
except Exception as e:
print(f"Error closing ORM: {e}")
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(init_and_cleanup())
2025-10-08 12:41:05 +02:00
# Prepare shell vars that are to be immediately available
shell_vars = {"p": project}
2025-09-27 00:29:45 +02:00
try:
from IPython import start_ipython
from traitlets.config.loader import Config
2025-09-27 00:29:45 +02:00
c = Config()
c.TerminalIPythonApp.display_banner = True
c.TerminalInteractiveShell.banner2 = banner
2025-09-27 00:29:45 +02:00
start_ipython(argv=[], user_ns=shell_vars, config=c)
except ImportError:
import code
2025-09-28 15:41:01 +02:00
code.interact(local=shell_vars, banner=banner)
finally:
loop.run_until_complete(cleanup())
2025-09-27 00:29:45 +02:00
@app.command()
def makemigrations(app: str = "*", name: str = "auto", root: str = "."):
"""
Create a DB migration based on your models.
"""
project_path = Path(root).resolve()
project = runtime.Project(project_path)
if app == "*":
for app in project.apps.keys():
asyncio.run(project.makemigrations(app_label=app, name=name))
else:
asyncio.run(project.makemigrations(app_label=app, name=name))
2025-10-08 12:45:27 +02:00
logger.info(f"✅ Migrations created successfully.")
logger.info(f"🔧 To migrate the DB, run `ohmyapi migrate`, next.")
2025-09-27 00:29:45 +02:00
@app.command()
def migrate(app: str = "*", root: str = "."):
"""
Run all DB migrations.
"""
project_path = Path(root).resolve()
project = runtime.Project(project_path)
if app == "*":
for app in project.apps.keys():
asyncio.run(project.migrate(app))
else:
asyncio.run(project.migrate(app))
2025-10-08 12:45:27 +02:00
logger.info(f"✅ Migrations ran successfully.")
2025-09-27 00:29:45 +02:00
@app.command()
def createsuperuser(root: str = "."):
2025-09-27 16:50:15 +02:00
"""Create a superuser in the DB.
This requires the presence of `ohmyapi_auth` in your INSTALLED_APPS to work.
"""
2025-09-27 00:29:45 +02:00
project_path = Path(root).resolve()
project = runtime.Project(project_path)
if not project.is_app_installed("ohmyapi_auth"):
2025-09-28 15:41:01 +02:00
print(
"Auth app not installed! Please add 'ohmyapi_auth' to your INSTALLED_APPS."
)
2025-09-27 00:29:45 +02:00
return
import asyncio
2025-09-28 15:41:01 +02:00
2025-09-27 00:29:45 +02:00
import ohmyapi_auth
2025-09-28 15:41:01 +02:00
2025-09-27 15:05:12 +02:00
email = input("E-Mail: ")
2025-09-27 00:29:45 +02:00
username = input("Username: ")
2025-09-27 16:50:15 +02:00
password1, password2 = "foo", "bar"
while password1 != password2:
password1 = getpass("Password: ")
password2 = getpass("Repeat Password: ")
if password1 != password2:
print("Passwords didn't match!")
2025-09-28 15:41:01 +02:00
user = ohmyapi_auth.models.User(
2025-10-08 12:41:05 +02:00
username=username, is_staff=True, is_admin=True
2025-09-28 15:41:01 +02:00
)
2025-10-08 12:41:05 +02:00
user.set_email(email)
2025-09-27 16:50:15 +02:00
user.set_password(password1)
2025-09-27 00:29:45 +02:00
asyncio.run(project.init_orm())
asyncio.run(user.save())
asyncio.run(project.close_orm())