diff --git a/app/db/apps.py b/app/db/apps.py index 8f611ab..320fef9 100644 --- a/app/db/apps.py +++ b/app/db/apps.py @@ -82,14 +82,26 @@ def get_app(app_id: str): return dict(row) if row else None -def get_app_templates(): +def get_app_templates(create_enabled: bool = False): run_migrations() con = get_connection() + create_filter = "AND create_enabled = 1" if create_enabled else "" rows = con.execute( - """ - SELECT name, runtime, description + f""" + SELECT + id, + name, + runtime, + language, + description, + default_port, + default_health_path, + COALESCE(is_enabled, 1) AS is_enabled, + COALESCE(create_enabled, 0) AS create_enabled FROM app_templates + WHERE COALESCE(is_enabled, 1) = 1 + {create_filter} ORDER BY name """ ).fetchall() @@ -98,6 +110,66 @@ def get_app_templates(): return [dict(row) for row in rows] +def get_app_template(template_id: str, create_enabled: bool = False): + run_migrations() + con = get_connection() + + create_filter = "AND COALESCE(create_enabled, 0) = 1" if create_enabled else "" + row = con.execute( + f""" + SELECT + id, + name, + runtime, + language, + description, + default_port, + default_health_path, + COALESCE(is_enabled, 1) AS is_enabled, + COALESCE(create_enabled, 0) AS create_enabled + FROM app_templates + WHERE id = ? + AND COALESCE(is_enabled, 1) = 1 + {create_filter} + """, + (template_id,), + ).fetchone() + + con.close() + return dict(row) if row else None + + +def update_app_template_metadata(app_id: str, metadata: dict): + run_migrations() + con = get_connection() + + con.execute( + """ + UPDATE apps + SET name = ?, + template = ?, + runtime = ?, + language = ?, + health_url = ?, + container_port = ?, + updated_at = CURRENT_TIMESTAMP + WHERE id = ? + """, + ( + metadata.get("name") or None, + metadata.get("template") or None, + metadata.get("runtime") or None, + metadata.get("language") or None, + metadata.get("health_url") or None, + metadata.get("container_port"), + app_id, + ), + ) + + con.commit() + con.close() + + def update_app_metadata(app_id: str, metadata: dict): run_migrations() con = get_connection() diff --git a/app/db/migrations.py b/app/db/migrations.py index a1ffe68..a7b46c2 100644 --- a/app/db/migrations.py +++ b/app/db/migrations.py @@ -88,9 +88,15 @@ def run_migrations(): con.execute( """ CREATE TABLE IF NOT EXISTS app_templates ( - name TEXT PRIMARY KEY, + id TEXT PRIMARY KEY, + name TEXT NOT NULL, runtime TEXT, + language TEXT, description TEXT, + default_port INTEGER, + default_health_path TEXT, + is_enabled INTEGER NOT NULL DEFAULT 1, + create_enabled INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP ) @@ -125,8 +131,15 @@ def run_migrations(): """ ) for statement in ( + "ALTER TABLE app_templates ADD COLUMN id TEXT", + "ALTER TABLE app_templates ADD COLUMN name TEXT", "ALTER TABLE app_templates ADD COLUMN runtime TEXT", + "ALTER TABLE app_templates ADD COLUMN language TEXT", "ALTER TABLE app_templates ADD COLUMN description TEXT", + "ALTER TABLE app_templates ADD COLUMN default_port INTEGER", + "ALTER TABLE app_templates ADD COLUMN default_health_path TEXT", + "ALTER TABLE app_templates ADD COLUMN is_enabled INTEGER NOT NULL DEFAULT 1", + "ALTER TABLE app_templates ADD COLUMN create_enabled INTEGER NOT NULL DEFAULT 0", "ALTER TABLE app_templates ADD COLUMN created_at TEXT", "ALTER TABLE app_templates ADD COLUMN updated_at TEXT", "ALTER TABLE app_variables ADD COLUMN created_at TEXT", @@ -136,6 +149,10 @@ def run_migrations(): con.execute(statement) except Exception: pass + try: + con.execute("UPDATE app_templates SET id = name WHERE id IS NULL OR id = ''") + except Exception: + pass con.execute("CREATE INDEX IF NOT EXISTS idx_app_variables_app_id ON app_variables(app_id)") con.execute("CREATE INDEX IF NOT EXISTS idx_service_incidents_status_started ON service_incidents(status, started_at)") con.execute("CREATE INDEX IF NOT EXISTS idx_service_incidents_service_started ON service_incidents(service_id, started_at)") diff --git a/app/routes/apps.py b/app/routes/apps.py index 321854d..0db84fd 100644 --- a/app/routes/apps.py +++ b/app/routes/apps.py @@ -19,12 +19,14 @@ from ..db.apps import ( create_app_variable, delete_app_variable, get_app, + get_app_template, get_app_deployments, get_app_templates, get_app_variables, get_apps, update_app_metadata, update_app_resources, + update_app_template_metadata, update_app_variable, ) from ..db.audit import log_audit_event @@ -73,9 +75,11 @@ def bool_checked(value) -> str: return " checked" if value else "" -def render_template_options(templates: list[dict], selected_template: str) -> str: - options = [''] +def render_template_options(templates: list[dict], selected_template: str, include_blank: bool = True) -> str: + options = [''] if include_blank else [] + selected_exists = not selected_template for template in templates: + template_id = template.get("id", "") or "" name = template.get("name", "") or "" runtime = template.get("runtime", "") or "" description = template.get("description", "") or "" @@ -84,13 +88,28 @@ def render_template_options(templates: list[dict], selected_template: str) -> st label_parts.append(runtime) if description: label_parts.append(description) - selected = " selected" if selected_template == name else "" + selected = " selected" if selected_template == template_id else "" + if selected: + selected_exists = True options.append( - f'' + f'' + ) + if not selected_exists: + options.append( + f'' ) return "".join(options) +def render_template_label(templates: list[dict], template_id: str) -> str: + if not template_id: + return "" + for template in templates: + if template.get("id") == template_id: + return html.escape(template.get("name", "") or template_id) + return "Neznámá šablona" + + def diff_metadata(before: dict, after: dict) -> dict: changes = {} for field in METADATA_FIELDS: @@ -404,7 +423,6 @@ def app_detail(app_id: str, request: Request, user=Depends(require_user)): updated_at = html.escape(app.get("updated_at", "") or "") description = html.escape(app.get("description", "") or "") owner = html.escape(app.get("owner", "") or "") - template_value = html.escape(app.get("template", "") or "") runtime = html.escape(app.get("runtime", "") or "") repository_url = html.escape(app.get("repository_url", "") or "") repository_name = html.escape(app.get("repository_name", "") or "") @@ -416,6 +434,7 @@ def app_detail(app_id: str, request: Request, user=Depends(require_user)): is_enabled = bool(app.get("is_enabled")) templates = get_app_templates() template_options = render_template_options(templates, app.get("template", "") or "") + template_value = render_template_label(templates, app.get("template", "") or "") variables = get_app_variables(app.get("id", "")) incidents = get_service_incidents(app.get("id", ""), limit=20) current_health = get_service_health(app.get("id", "")) @@ -867,9 +886,10 @@ def redeploy_app(app_id: str, user=Depends(require_user)): @router.get("/new-app", response_class=HTMLResponse) def new_app_form(request: Request, user=Depends(require_user)): + template_options = render_template_options(get_app_templates(create_enabled=True), "", include_blank=False) return page( "Nová služba", - """ + f"""

Vytvořit novou službu

Vytvoří Gitea repozitář, webhook, lokální workspace, první commit a nasadí službu.

@@ -887,9 +907,7 @@ def new_app_form(request: Request, user=Depends(require_user)):


- +

@@ -909,10 +927,23 @@ def create_app( template: str = Form(...), user=Depends(require_user), ): - if template != "python-fastapi": + selected_template = get_app_template(template, create_enabled=True) + if not selected_template: return HTMLResponse("Nepodporovaná šablona", status_code=400) create_result = run_command([NEW_APP_SCRIPT, app_id, app_name]) + if create_result.returncode == 0: + update_app_template_metadata( + app_id, + { + "name": app_name, + "template": selected_template.get("id"), + "runtime": selected_template.get("runtime"), + "language": selected_template.get("language"), + "health_url": selected_template.get("default_health_path"), + "container_port": selected_template.get("default_port"), + }, + ) deploy_result = run_command([DEPLOY_SCRIPT, app_id]) status = "OK" if create_result.returncode == 0 and deploy_result.returncode == 0 else "FAILED" @@ -924,7 +955,7 @@ def create_app( metadata={ "app_id": app_id, "app_name": app_name, - "template": template, + "template": selected_template.get("id"), "status": status, "create_returncode": create_result.returncode, "deploy_returncode": deploy_result.returncode,