import html
from fastapi import APIRouter, Form
from fastapi.responses import HTMLResponse
from ..catalog import load_apps, save_apps
from ..config import (
DEFAULT_APPFACTORY_HOST,
DEFAULT_GITEA_ORG,
DELETE_APP_SCRIPT,
DEPLOY_SCRIPT,
GENERATE_COMPOSE_SCRIPT,
NEW_APP_SCRIPT,
read_env_value,
)
from ..shell import run_command
from ..templates.layout import page, render_result
router = APIRouter()
@router.get("/", response_class=HTMLResponse)
def index():
apps = load_apps()
gitea_url = read_env_value("GITEA_URL", "")
gitea_org = read_env_value("GITEA_ORG", DEFAULT_GITEA_ORG)
host = read_env_value("APPFACTORY_HOST", DEFAULT_APPFACTORY_HOST)
rows = ""
for item in apps:
app_id = html.escape(item.get("id", ""))
status = html.escape(item.get("status", ""))
docs = html.escape(item.get("docs", f"/apps/{app_id}/docs"))
memory = item.get("memory", "")
cpus = item.get("cpus", "")
http_clone = html.escape(f"git clone {gitea_url}/{gitea_org}/{app_id}.git")
ssh_clone = html.escape(f"git clone ssh://git@{host}:2222/{gitea_org}/{app_id}.git")
memory_options = ""
for value, label in [
("", "Default"),
("256m", "256 MB - small service"),
("512m", "512 MB - normal service"),
("1g", "1 GB - larger service"),
("2g", "2 GB - heavy service"),
]:
selected = "selected" if memory == value else ""
memory_options += f''
cpu_options = ""
for value, label in [
("", "Default"),
("0.25", "0.25 CPU - very small"),
("0.50", "0.50 CPU - normal"),
("1.00", "1 CPU - full core"),
("2.00", "2 CPU - heavy"),
]:
selected = "selected" if cpus == value else ""
cpu_options += f''
rows += f"""
{app_id}
/apps/{app_id}
|
{status} |
Swagger |
|
Clone commands
|
|
"""
if not rows:
rows = '| No apps deployed yet. |
'
return page(
"Apps",
f"""
Apps
Create, deploy, clone, tune resources and delete services.
+ New App
Backups
Create backups and copy restore commands. Restore is intentionally manual and guarded.
Manage Backups
Deployed Apps
| App |
Status |
Docs |
Resources |
Git |
Actions |
{rows}
""",
)
@router.get("/new-app", response_class=HTMLResponse)
def new_app_form():
return page(
"New App",
"""
Create New App
This creates Gitea repo, webhook, local workspace, initial commit and deploys the service.
← Back
""",
)
@router.post("/new-app", response_class=HTMLResponse)
def create_app(app_id: str = Form(...), app_name: str = Form(...), template: str = Form(...)):
if template != "python-fastapi":
return HTMLResponse("Unsupported template", status_code=400)
create_result = run_command([NEW_APP_SCRIPT, app_id, app_name])
deploy_result = run_command([DEPLOY_SCRIPT, app_id])
status = "OK" if create_result.returncode == 0 and deploy_result.returncode == 0 else "FAILED"
return render_result(
title=f"Create App: {status}",
back_url="/portal",
sections=[
("Create Output", create_result.stdout),
("Create Error", create_result.stderr),
("Deploy Output", deploy_result.stdout),
("Deploy Error", deploy_result.stderr),
],
extra_link=f"/apps/{html.escape(app_id)}/docs",
extra_label="Open Swagger",
)
@router.post("/delete-app", response_class=HTMLResponse)
def delete_app(app_id: str = Form(...)):
result = run_command([DELETE_APP_SCRIPT, app_id])
status = "OK" if result.returncode == 0 else "FAILED"
return render_result(
title=f"Delete App: {status}",
back_url="/portal",
sections=[("Output", result.stdout), ("Error", result.stderr)],
)
@router.post("/update-resources", response_class=HTMLResponse)
def update_resources(app_id: str = Form(...), memory: str = Form(""), cpus: str = Form("")):
apps = load_apps()
for item in apps:
if item.get("id") == app_id:
memory = memory.strip()
cpus = cpus.strip()
if memory:
item["memory"] = memory
else:
item.pop("memory", None)
if cpus:
item["cpus"] = cpus
else:
item.pop("cpus", None)
save_apps(apps)
compose_result = run_command([GENERATE_COMPOSE_SCRIPT])
deploy_result = run_command([DEPLOY_SCRIPT, app_id])
status = "OK" if compose_result.returncode == 0 and deploy_result.returncode == 0 else "FAILED"
return render_result(
title=f"Update Resources: {status}",
back_url="/portal",
sections=[
("Compose Output", compose_result.stdout),
("Compose Error", compose_result.stderr),
("Deploy Output", deploy_result.stdout),
("Deploy Error", deploy_result.stderr),
],
)