mirror of
https://github.com/KohakuBlueleaf/KohakuHub.git
synced 2026-03-11 17:34:08 -05:00
better deploy choice
This commit is contained in:
85
scripts/deploy_board_integrated.py
Normal file
85
scripts/deploy_board_integrated.py
Normal file
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Deploy KohakuBoard integrated with KohakuHub (shared database for SSO)
|
||||
|
||||
OPTIONAL: This is only if you want to share database with KohakuHub.
|
||||
For fully standalone deployment, use deploy_board.py instead.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 60)
|
||||
print("KohakuBoard Integrated Deployment (OPTIONAL)")
|
||||
print("Shares database with KohakuHub for unified accounts")
|
||||
print("=" * 60)
|
||||
|
||||
# Check if KohakuHub is running (optional warning, not error)
|
||||
print("\n[0/3] Checking KohakuHub services...")
|
||||
result = subprocess.run(
|
||||
["docker", "ps", "--filter", "name=postgres", "--format", "{{.Names}}"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if "postgres" not in result.stdout:
|
||||
print("⚠️ Warning: KohakuHub PostgreSQL is not running")
|
||||
print("\nThis deployment expects to connect to KohakuHub's database.")
|
||||
print("If you want fully standalone deployment, use:")
|
||||
print(" python scripts/deploy_board.py")
|
||||
print("\nIf you want to deploy KohakuHub first:")
|
||||
print(" 1. cp docker-compose.example.yml docker-compose.yml")
|
||||
print(" 2. Edit docker-compose.yml (change passwords and secrets)")
|
||||
print(" 3. ./deploy.sh")
|
||||
print("\nContinuing anyway (will fail if database is not accessible)...")
|
||||
else:
|
||||
print("✓ KohakuHub PostgreSQL is running")
|
||||
|
||||
# Build frontend
|
||||
print("\n[1/3] Building frontend...")
|
||||
result = subprocess.run(
|
||||
["npm", "install", "--prefix", "./src/kohaku-board-ui"],
|
||||
check=False,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
print("✗ Failed to install dependencies")
|
||||
sys.exit(1)
|
||||
|
||||
result = subprocess.run(
|
||||
["npm", "run", "build", "--prefix", "./src/kohaku-board-ui"],
|
||||
check=False,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
print("✗ Failed to build frontend")
|
||||
sys.exit(1)
|
||||
|
||||
print("\n[2/3] Starting KohakuBoard services...")
|
||||
result = subprocess.run(
|
||||
[
|
||||
"docker-compose",
|
||||
"-f",
|
||||
"docker-compose.board-integrated.yml",
|
||||
"up",
|
||||
"-d",
|
||||
"--build",
|
||||
],
|
||||
check=False,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
print("✗ Failed to start Docker services")
|
||||
sys.exit(1)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("✓ KohakuBoard deployed successfully (integrated mode)!")
|
||||
print("=" * 60)
|
||||
print(f"\nKohakuHub: http://localhost:28080")
|
||||
print(f"KohakuBoard: http://localhost:28081")
|
||||
print(f"\n⚡ SSO enabled: Login works across both systems")
|
||||
print(f"\n📊 Shared database: Users are unified")
|
||||
print("\nView logs: docker-compose -f docker-compose.board-integrated.yml logs -f")
|
||||
print("Stop services: docker-compose -f docker-compose.board-integrated.yml down")
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -34,8 +34,9 @@ def cli():
|
||||
@click.argument("folder", type=click.Path(exists=True))
|
||||
@click.option("--port", default=48889, help="Server port (default: 48889)")
|
||||
@click.option("--host", default="0.0.0.0", help="Server host (default: 0.0.0.0)")
|
||||
@click.option("--reload", is_flag=True, help="Enable auto-reload for development")
|
||||
@click.option("--no-browser", is_flag=True, help="Do not open browser automatically")
|
||||
def open(folder, port, host, no_browser):
|
||||
def open(folder, port, host, reload, no_browser):
|
||||
"""Open local board folder in browser
|
||||
|
||||
Starts a local web server to browse boards in the specified folder.
|
||||
@@ -44,7 +45,7 @@ def open(folder, port, host, no_browser):
|
||||
Examples:
|
||||
kobo open ./kohakuboard
|
||||
kobo open /path/to/experiments --port 8080
|
||||
kobo open ./boards --no-browser
|
||||
kobo open ./boards --reload --no-browser
|
||||
"""
|
||||
folder_path = Path(folder).resolve()
|
||||
|
||||
@@ -57,6 +58,8 @@ def open(folder, port, host, no_browser):
|
||||
click.echo("🚀 Starting KohakuBoard server (local mode)")
|
||||
click.echo(f"📁 Board directory: {folder_path}")
|
||||
click.echo(f"🌐 Server URL: http://localhost:{port}")
|
||||
if reload:
|
||||
click.echo(f"🔄 Auto-reload: Enabled")
|
||||
click.echo()
|
||||
|
||||
# Open browser after delay
|
||||
@@ -79,7 +82,7 @@ def open(folder, port, host, no_browser):
|
||||
"kohakuboard.main:app",
|
||||
host=host,
|
||||
port=port,
|
||||
reload=False,
|
||||
reload=reload,
|
||||
log_level="info",
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
@@ -87,6 +90,144 @@ def open(folder, port, host, no_browser):
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option("--host", default="0.0.0.0", help="Server host (default: 0.0.0.0)")
|
||||
@click.option("--port", default=48889, help="Server port (default: 48889)")
|
||||
@click.option(
|
||||
"--data-dir",
|
||||
default="./kohakuboard",
|
||||
help="Board data directory (default: ./kohakuboard)",
|
||||
)
|
||||
@click.option(
|
||||
"--db",
|
||||
default="sqlite:///kohakuboard.db",
|
||||
help="Database URL (default: sqlite:///kohakuboard.db)",
|
||||
)
|
||||
@click.option(
|
||||
"--db-backend",
|
||||
type=click.Choice(["sqlite", "postgres"]),
|
||||
default="sqlite",
|
||||
help="Database backend (default: sqlite)",
|
||||
)
|
||||
@click.option(
|
||||
"--reload",
|
||||
is_flag=True,
|
||||
help="Enable auto-reload for development",
|
||||
)
|
||||
@click.option(
|
||||
"--workers",
|
||||
default=1,
|
||||
help="Number of worker processes (default: 1, use 4+ for production)",
|
||||
)
|
||||
@click.option(
|
||||
"--session-secret",
|
||||
help="Session secret for authentication (required in production)",
|
||||
)
|
||||
@click.option(
|
||||
"--no-browser",
|
||||
is_flag=True,
|
||||
help="Do not open browser automatically",
|
||||
)
|
||||
def serve(
|
||||
host, port, data_dir, db, db_backend, reload, workers, session_secret, no_browser
|
||||
):
|
||||
"""Start KohakuBoard server in remote mode with authentication
|
||||
|
||||
Lightweight entry point for both testing and production deployments.
|
||||
No Docker required - just Python and a database.
|
||||
|
||||
Examples:
|
||||
# Development with auto-reload (SQLite)
|
||||
kobo serve --reload
|
||||
|
||||
# Production with PostgreSQL
|
||||
kobo serve --db postgresql://user:pass@localhost/kohakuboard \\
|
||||
--db-backend postgres \\
|
||||
--workers 4 \\
|
||||
--session-secret $(openssl rand -hex 32)
|
||||
|
||||
# Custom configuration
|
||||
kobo serve --port 8080 \\
|
||||
--data-dir /var/kohakuboard \\
|
||||
--db sqlite:///data/board.db \\
|
||||
--workers 2
|
||||
"""
|
||||
data_dir_path = Path(data_dir).resolve()
|
||||
data_dir_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Set environment for remote mode
|
||||
os.environ["KOHAKU_BOARD_MODE"] = "remote"
|
||||
os.environ["KOHAKU_BOARD_BOARD_DATA_DIR"] = str(data_dir_path)
|
||||
os.environ["KOHAKU_BOARD_PORT"] = str(port)
|
||||
os.environ["KOHAKU_BOARD_HOST"] = host
|
||||
os.environ["KOHAKU_BOARD_DB_BACKEND"] = db_backend
|
||||
os.environ["KOHAKU_BOARD_DATABASE_URL"] = db
|
||||
os.environ["KOHAKU_BOARD_BASE_URL"] = f"http://localhost:{port}"
|
||||
|
||||
# Session secret (required for production)
|
||||
if session_secret:
|
||||
os.environ["KOHAKU_BOARD_AUTH_SESSION_SECRET"] = session_secret
|
||||
elif workers > 1 and not reload:
|
||||
click.echo(
|
||||
"⚠️ Warning: Using default session secret in multi-worker mode", err=True
|
||||
)
|
||||
click.echo(" Generate one with: openssl rand -hex 32", err=True)
|
||||
click.echo()
|
||||
|
||||
# Auth config defaults (can be overridden with env vars)
|
||||
os.environ.setdefault("KOHAKU_BOARD_AUTH_REQUIRE_EMAIL_VERIFICATION", "false")
|
||||
os.environ.setdefault("KOHAKU_BOARD_AUTH_INVITATION_ONLY", "false")
|
||||
|
||||
click.echo("🚀 Starting KohakuBoard server (remote mode)")
|
||||
click.echo(f"📁 Data directory: {data_dir_path}")
|
||||
click.echo(f"💾 Database: {db}")
|
||||
click.echo(f"🌐 Server URL: http://localhost:{port}")
|
||||
click.echo(f"👥 Authentication: Enabled")
|
||||
if reload:
|
||||
click.echo(f"🔄 Auto-reload: Enabled (development)")
|
||||
if workers > 1:
|
||||
click.echo(f"⚡ Workers: {workers}")
|
||||
click.echo()
|
||||
|
||||
# Open browser after delay
|
||||
if not no_browser:
|
||||
|
||||
def open_browser():
|
||||
time.sleep(2) # Wait for server to start
|
||||
click.echo(f"🔗 Opening browser at http://localhost:{port}")
|
||||
try:
|
||||
webbrowser.open(f"http://localhost:{port}")
|
||||
except Exception as e:
|
||||
click.echo(f"⚠️ Could not open browser: {e}", err=True)
|
||||
|
||||
thread = threading.Thread(target=open_browser, daemon=True)
|
||||
thread.start()
|
||||
|
||||
# Run uvicorn
|
||||
try:
|
||||
if reload or workers == 1:
|
||||
# Single worker with optional reload
|
||||
uvicorn.run(
|
||||
"kohakuboard.main:app",
|
||||
host=host,
|
||||
port=port,
|
||||
reload=reload,
|
||||
log_level="info",
|
||||
)
|
||||
else:
|
||||
# Multi-worker production mode
|
||||
uvicorn.run(
|
||||
"kohakuboard.main:app",
|
||||
host=host,
|
||||
port=port,
|
||||
workers=workers,
|
||||
log_level="info",
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
click.echo("\n👋 Server stopped")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.argument("folder", type=click.Path(exists=True))
|
||||
@click.option(
|
||||
|
||||
Reference in New Issue
Block a user