Co se změnilo:
app_templates je teď jediný source of truth. Detail služby načítá šablony přes is_enabled = 1. Vytvoření služby načítá šablony přes is_enabled = 1 AND create_enabled = 1. Select hodnoty používají app_templates.id, takže fastapi odpovídá existující službě microsoft-365-service. Při vytvoření služby se z vybrané šablony nastaví template, runtime, language, container_port a health_url. Neznámá šablona se zobrazí jako Neznámá šablona a detail nespadne. Hardcoded python-fastapi / seznam šablon byl odstraněn.
This commit is contained in:
+75
-3
@@ -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()
|
||||
|
||||
+18
-1
@@ -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)")
|
||||
|
||||
+42
-11
@@ -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 = ['<option value="">Bez šablony</option>']
|
||||
def render_template_options(templates: list[dict], selected_template: str, include_blank: bool = True) -> str:
|
||||
options = ['<option value="">Bez šablony</option>'] 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'<option value="{html.escape(name)}"{selected}>{html.escape(" - ".join(label_parts))}</option>'
|
||||
f'<option value="{html.escape(template_id)}"{selected}>{html.escape(" - ".join(label_parts))}</option>'
|
||||
)
|
||||
if not selected_exists:
|
||||
options.append(
|
||||
f'<option value="{html.escape(selected_template)}" selected>Neznámá šablona</option>'
|
||||
)
|
||||
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"""
|
||||
<div class="card">
|
||||
<h2>Vytvořit novou službu</h2>
|
||||
<p class="muted">Vytvoří Gitea repozitář, webhook, lokální workspace, první commit a nasadí službu.</p>
|
||||
@@ -887,9 +907,7 @@ def new_app_form(request: Request, user=Depends(require_user)):
|
||||
|
||||
<p>
|
||||
<label>Šablona</label><br>
|
||||
<select name="template">
|
||||
<option value="python-fastapi">Python FastAPI</option>
|
||||
</select>
|
||||
<select name="template" required>{template_options}</select>
|
||||
</p>
|
||||
|
||||
<button type="submit">Vytvořit službu</button>
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user