fixes + guest

This commit is contained in:
JiriUhlir
2026-06-16 10:23:43 +02:00
parent fa4307852c
commit bad55a7d41
18 changed files with 295 additions and 209 deletions
+52 -52
View File
@@ -144,7 +144,7 @@ def ensure_variable_key_allowed(app_id: str, key: str, variable_id: int | None =
def render_template_options(templates: list[dict], selected_template: str, include_blank: bool = True) -> str:
options = ['<option value="">Bez &scaron;ablony</option>'] if include_blank else []
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 ""
@@ -164,7 +164,7 @@ def render_template_options(templates: list[dict], selected_template: str, inclu
)
if not selected_exists:
options.append(
f'<option value="{html.escape(selected_template)}" selected>Nezn&aacute;m&aacute; &scaron;ablona</option>'
f'<option value="{html.escape(selected_template)}" selected>Neznámá šablona</option>'
)
return "".join(options)
@@ -175,7 +175,7 @@ def render_template_label(templates: list[dict], template_id: str) -> str:
for template in templates:
if template.get("id") == template_id:
return html.escape(template.get("name", "") or template_id)
return "Nezn&aacute;m&aacute; &scaron;ablona"
return "Neznámá šablona"
def render_environment_usage_example(language: str, runtime: str, template: str) -> str:
@@ -361,8 +361,8 @@ def apps_page(
<label>CPU</label>
<input name="cpus" value="{cpus}" placeholder="např. 0.50, 1.00 nebo prázdné pro výchozí">
<div class="modal-actions">
<button type="button" class="btn btn-secondary" onclick="closeResourceModal('{resource_modal_id}')">Zrušit</button>
<button type="submit">Použít</button>
<button type="button" class="btn btn-secondary" onclick="closeResourceModal('{resource_modal_id}')"><i class="fa-solid fa-xmark" aria-hidden="true"></i> Zrušit</button>
<button type="submit"><i class="fa-solid fa-check" aria-hidden="true"></i> Použít</button>
</div>
</form>
</dialog>"""
@@ -377,13 +377,13 @@ def apps_page(
<label class="muted">HTTP</label>
<div class="cmd-row">
<input readonly value="{http_clone}" id="http-{app_id}">
<button type="button" onclick="return copyText(this)">Kopírovat</button>
<button type="button" onclick="return copyText(this)"><i class="fa-solid fa-copy" aria-hidden="true"></i> Kopírovat</button>
</div>
<label class="muted">SSH</label>
<div class="cmd-row">
<input readonly value="{ssh_clone}" id="ssh-{app_id}">
<button type="button" onclick="return copyText(this)">Kopírovat</button>
<button type="button" onclick="return copyText(this)"><i class="fa-solid fa-copy" aria-hidden="true"></i> Kopírovat</button>
</div>
</details>"""
else:
@@ -470,12 +470,12 @@ def apps_page(
return f"/portal/apps?{urlencode(params)}"
previous_link = (
f'<a class="btn btn-secondary" href="{page_url(page_number - 1)}">Předchozí</a>'
f'<a class="btn btn-secondary" href="{page_url(page_number - 1)}"><i class="fa-solid fa-chevron-left" aria-hidden="true"></i> Předchozí</a>'
if page_number > 1
else ""
)
next_link = (
f'<a class="btn btn-secondary" href="{page_url(page_number + 1)}">Další</a>'
f'<a class="btn btn-secondary" href="{page_url(page_number + 1)}">Další <i class="fa-solid fa-chevron-right" aria-hidden="true"></i></a>'
if page_number < total_pages
else ""
)
@@ -502,7 +502,7 @@ def apps_page(
"""
<p><a class="btn" href="/portal/new-app"><i class="fa-solid fa-plus" aria-hidden="true"></i> Nová služba</a></p>
<form method="post" action="/portal/apps/environment/apply-all" class="inline-form">
<button type="submit" class="btn-secondary"><i class="fa-solid fa-arrows-rotate" aria-hidden="true"></i> Regenerovat v&scaron;echny .env z datab&aacute;ze</button>
<button type="submit" class="btn-secondary"><i class="fa-solid fa-arrows-rotate" aria-hidden="true"></i> Regenerovat všechny .env z databáze</button>
</form>
"""
if can_manage
@@ -709,17 +709,17 @@ def app_detail(app_id: str, request: Request, message: str = "", error: str = ""
<input name="key" value="{variable_key}" required>
<input name="value" value="{value_input}"{secret_hint}>
<label class="checkbox-label"><input type="checkbox" name="is_secret" value="1"{secret_checked}> Secret</label>
<button type="submit" class="btn btn-compact">Ulo&zcaron;it</button>
<button type="submit" class="btn btn-compact"><i class="fa-solid fa-floppy-disk" aria-hidden="true"></i> Uložit</button>
</form>
<form method="post" action="/portal/apps/{app_url_id}/variables/{variable_id}/delete" class="inline-form" onsubmit="return confirm('Smazat promennou {variable_key}?');">
<button type="submit" class="btn btn-secondary btn-compact">Smazat</button>
<button type="submit" class="btn btn-secondary btn-compact"><i class="fa-solid fa-trash" aria-hidden="true"></i> Smazat</button>
</form>
</td>
</tr>
"""
if not variable_rows:
variable_rows = '<tr><td colspan="4">Zat&iacute;m nejsou evidovan&eacute; &zcaron;&aacute;dn&eacute; prom&ecaron;nn&eacute;.</td></tr>'
variable_rows = '<tr><td colspan="4">Zatím nejsou evidované žádné proměnné.</td></tr>'
def render_method_options(selected: str) -> str:
selected_upper = (selected or "").upper()
@@ -749,10 +749,10 @@ def app_detail(app_id: str, request: Request, message: str = "", error: str = ""
<td><input name="ip_cidr" value="{rule_ip}" form="{form_id}" required></td>
<td><select name="methods" form="{form_id}">{render_method_options(rule.get("methods", ""))}</select></td>
<td><input name="description" value="{rule_desc}" form="{form_id}"></td>
<td><label class="checkbox-label"><input type="checkbox" name="is_enabled" value="1" form="{form_id}"{enabled_checked}> Aktivn&iacute;</label></td>
<td><label class="checkbox-label"><input type="checkbox" name="is_enabled" value="1" form="{form_id}"{enabled_checked}> Aktivní</label></td>
<td>
<form method="post" action="/portal/apps/{app_url_id}/ip-access-rules/{rid}/update" id="{form_id}" class="inline-form"></form>
<button type="submit" form="{form_id}" class="btn btn-compact"><i class="fa-solid fa-floppy-disk" aria-hidden="true"></i> Ulo&zcaron;it</button>
<button type="submit" form="{form_id}" class="btn btn-compact"><i class="fa-solid fa-floppy-disk" aria-hidden="true"></i> Uložit</button>
<form method="post" action="/portal/apps/{app_url_id}/ip-access-rules/{rid}/delete" class="inline-form" onsubmit="return confirm('Smazat pravidlo {ip_label}?');">
<button type="submit" class="btn btn-secondary btn-compact"><i class="fa-solid fa-trash" aria-hidden="true"></i> Smazat</button>
</form>
@@ -761,7 +761,7 @@ def app_detail(app_id: str, request: Request, message: str = "", error: str = ""
"""
if not ip_access_rule_rows:
ip_access_rule_rows = '<tr><td colspan="5">Zat&iacute;m nejsou evidovan&aacute; &zcaron;&aacute;dn&aacute; IP access pravidla.</td></tr>'
ip_access_rule_rows = '<tr><td colspan="5">Zatím nejsou evidovaná žádná IP access pravidla.</td></tr>'
notice = ""
if message:
@@ -774,7 +774,7 @@ def app_detail(app_id: str, request: Request, message: str = "", error: str = ""
redeploy_block = (
f"""
<form method="post" action="/portal/apps/{app_url_id}/redeploy" onsubmit="return confirm('Spustit nov\u00e9 nasazen\u00ed slu\u017eby {escaped_app_id}?');">
<button type="submit">Nasadit znovu</button>
<button type="submit"><i class="fa-solid fa-rotate-right" aria-hidden="true"></i> Nasadit znovu</button>
</form>
"""
if can_manage
@@ -783,17 +783,17 @@ def app_detail(app_id: str, request: Request, message: str = "", error: str = ""
detail_tabs = (
"""
<div class="detail-tabs" aria-label="Sekce detailu slu&zcaron;by">
<a class="btn btn-secondary" href="#metadata">Metadata</a>
<a class="btn btn-secondary" href="#promenne">Prom&ecaron;nn&eacute;</a>
<a class="btn btn-secondary" href="#ip-access">Security / IP Access</a>
<a class="btn btn-secondary" href="#historie">Historie</a>
<div class="detail-tabs" aria-label="Sekce detailu služby">
<a class="btn btn-secondary" href="#metadata"><i class="fa-solid fa-table-list" aria-hidden="true"></i> Metadata</a>
<a class="btn btn-secondary" href="#promenne"><i class="fa-solid fa-sliders" aria-hidden="true"></i> Proměnné</a>
<a class="btn btn-secondary" href="#ip-access"><i class="fa-solid fa-shield-halved" aria-hidden="true"></i> Security / IP Access</a>
<a class="btn btn-secondary" href="#historie"><i class="fa-solid fa-clock-rotate-left" aria-hidden="true"></i> Historie</a>
</div>
"""
if can_manage
else """
<div class="detail-tabs" aria-label="Sekce detailu slu&zcaron;by">
<a class="btn btn-secondary" href="#historie">Historie</a>
<div class="detail-tabs" aria-label="Sekce detailu služby">
<a class="btn btn-secondary" href="#historie"><i class="fa-solid fa-clock-rotate-left" aria-hidden="true"></i> Historie</a>
</div>
"""
)
@@ -803,13 +803,13 @@ def app_detail(app_id: str, request: Request, message: str = "", error: str = ""
<div class="card" id="metadata">
<h2>Metadata</h2>
<form method="post" action="/portal/apps/{app_url_id}/metadata" class="metadata-form">
<label>N&aacute;zev</label>
<label>Název</label>
<input name="name" value="{name}" required>
<label>Popis</label>
<textarea name="description" rows="3">{description}</textarea>
<label>Vlastn&iacute;k</label>
<label>Vlastník</label>
<input name="owner" value="{owner}">
<label>Template</label>
@@ -836,11 +836,11 @@ def app_detail(app_id: str, request: Request, message: str = "", error: str = ""
<label>Container Port</label>
<input name="container_port" value="{container_port}" inputmode="numeric">
<label class="checkbox-label"><input type="checkbox" name="is_public" value="1"{bool_checked(is_public)}> Ve&rcaron;ejn&aacute; slu&zcaron;ba</label>
<label class="checkbox-label"><input type="checkbox" name="is_enabled" value="1"{bool_checked(is_enabled)}> Aktivn&iacute; slu&zcaron;ba</label>
<label class="checkbox-label"><input type="checkbox" name="is_public" value="1"{bool_checked(is_public)}> Veřejná služba</label>
<label class="checkbox-label"><input type="checkbox" name="is_enabled" value="1"{bool_checked(is_enabled)}> Aktivní služba</label>
<div class="form-actions">
<button type="submit">Ulo&zcaron;it metadata</button>
<button type="submit"><i class="fa-solid fa-floppy-disk" aria-hidden="true"></i> Uložit metadata</button>
</div>
</form>
</div>
@@ -852,9 +852,9 @@ def app_detail(app_id: str, request: Request, message: str = "", error: str = ""
variables_card = (
f"""
<div class="card" id="promenne">
<h2>Prom&ecaron;nn&eacute;</h2>
<h2><i class="fa-solid fa-sliders" aria-hidden="true"></i> Proměnné</h2>
<form method="post" action="/portal/apps/{app_url_id}/environment/apply" class="inline-form">
<button type="submit" class="btn-secondary">Regenerovat .env z datab&aacute;ze</button>
<button type="submit" class="btn-secondary"><i class="fa-solid fa-rotate" aria-hidden="true"></i> Regenerovat .env z databáze</button>
</form>
{environment_usage_example}
<table>
@@ -868,14 +868,14 @@ def app_detail(app_id: str, request: Request, message: str = "", error: str = ""
</table>
<form method="post" action="/portal/apps/{app_url_id}/variables" class="metadata-form add-variable-form">
<h3>P&rcaron;idat prom&ecaron;nnou</h3>
<h3>Přidat proměnnou</h3>
<label>Key</label>
<input name="key" required>
<label>Value</label>
<input name="value">
<label class="checkbox-label"><input type="checkbox" name="is_secret" value="1"> Secret</label>
<div class="form-actions">
<button type="submit">P&rcaron;idat prom&ecaron;nnou</button>
<button type="submit"><i class="fa-solid fa-plus" aria-hidden="true"></i> Přidat proměnnou</button>
</div>
</form>
</div>
@@ -889,9 +889,9 @@ def app_detail(app_id: str, request: Request, message: str = "", error: str = ""
<div class="card" id="ip-access">
<h2><i class="fa-solid fa-shield-halved" aria-hidden="true"></i> Security / IP Access</h2>
<p class="muted">
Pravidla ur&ccaron;uj&iacute;, z jak&yacute;ch IP/CIDR adres lze volat dan&eacute; HTTP metody.
<strong>WRITE</strong> = POST, PUT, PATCH, DELETE; <strong>ALL</strong> = v&scaron;echny b&ecaron;&zcaron;n&eacute; metody v&ccaron;etn&ecaron; GET.
Pravidla se zat&iacute;m pouze ukl&aacute;daj&iacute; (Caddy je za&ccaron;ne vynucovat pozd&ecaron;ji).
Pravidla určují, z jakých IP/CIDR adres lze volat dané HTTP metody.
<strong>WRITE</strong> = POST, PUT, PATCH, DELETE; <strong>ALL</strong> = všechny běžné metody včetně GET.
Pravidla se zatím pouze ukládají (Caddy je začne vynucovat později).
</p>
<table>
<tr>
@@ -905,16 +905,16 @@ def app_detail(app_id: str, request: Request, message: str = "", error: str = ""
</table>
<form method="post" action="/portal/apps/{app_url_id}/ip-access-rules" class="metadata-form add-variable-form">
<h3>P&rcaron;idat pravidlo</h3>
<h3>Přidat pravidlo</h3>
<label>IP/CIDR</label>
<input name="ip_cidr" placeholder="nap&rcaron;. 185.10.20.30 nebo 10.0.0.0/24" required>
<input name="ip_cidr" placeholder="např. 185.10.20.30 nebo 10.0.0.0/24" required>
<label>Methods</label>
<select name="methods">{render_method_options("WRITE")}</select>
<label>Description</label>
<input name="description" placeholder="nap&rcaron;. Office IP">
<label class="checkbox-label"><input type="checkbox" name="is_enabled" value="1" checked> Aktivn&iacute;</label>
<input name="description" placeholder="např. Office IP">
<label class="checkbox-label"><input type="checkbox" name="is_enabled" value="1" checked> Aktivní</label>
<div class="form-actions">
<button type="submit"><i class="fa-solid fa-plus" aria-hidden="true"></i> P&rcaron;idat pravidlo</button>
<button type="submit"><i class="fa-solid fa-plus" aria-hidden="true"></i> Přidat pravidlo</button>
</div>
</form>
</div>
@@ -932,8 +932,8 @@ def app_detail(app_id: str, request: Request, message: str = "", error: str = ""
{notice}
<p>
<a class="btn" href="/portal/apps">&larr; Zpět na služby</a>
<a class="btn btn-secondary" href="/portal/deployments?app_id={app_url_id}">Nasazení služby</a>
<a class="btn btn-secondary" href="/portal/jobs?target={app_url_id}">Úlohy služby</a>
<a class="btn btn-secondary" href="/portal/deployments?app_id={app_url_id}"><i class="fa-solid fa-rocket" aria-hidden="true"></i> Nasazení služby</a>
<a class="btn btn-secondary" href="/portal/jobs?target={app_url_id}"><i class="fa-solid fa-list-check" aria-hidden="true"></i> Úlohy služby</a>
</p>
{redeploy_block}
</div>
@@ -952,7 +952,7 @@ def app_detail(app_id: str, request: Request, message: str = "", error: str = ""
<tr><th>ID</th><td>{escaped_app_id}</td></tr>
<tr><th>Název</th><td>{name}</td></tr>
<tr><th>Popis</th><td>{description}</td></tr>
<tr><th>Vlastn&iacute;k</th><td>{owner}</td></tr>
<tr><th>Vlastník</th><td>{owner}</td></tr>
<tr><th>Template</th><td>{template_value}</td></tr>
<tr><th>Runtime</th><td>{runtime}</td></tr>
<tr><th>Repository URL</th><td>{repository_url}</td></tr>
@@ -961,8 +961,8 @@ def app_detail(app_id: str, request: Request, message: str = "", error: str = ""
<tr><th>Domain</th><td>{domain}</td></tr>
<tr><th>Health URL</th><td>{health_url}</td></tr>
<tr><th>Container Port</th><td>{container_port}</td></tr>
<tr><th>Ve&rcaron;ejn&aacute; slu&zcaron;ba</th><td>{"Ano" if is_public else "Ne"}</td></tr>
<tr><th>Aktivn&iacute; slu&zcaron;ba</th><td>{"Ano" if is_enabled else "Ne"}</td></tr>
<tr><th>Veřejná služba</th><td>{"Ano" if is_public else "Ne"}</td></tr>
<tr><th>Aktivní služba</th><td>{"Ano" if is_enabled else "Ne"}</td></tr>
<tr><th>Jazyk</th><td>{language}</td></tr>
<tr><th>Verze</th><td>{version}</td></tr>
<tr><th>Status</th><td><span class="pill">{status}</span></td></tr>
@@ -998,13 +998,13 @@ def app_detail(app_id: str, request: Request, message: str = "", error: str = ""
</div>
<div class="card">
<h2>Incidenty slu&zcaron;by</h2>
<h2>Incidenty služby</h2>
<table>
<tr>
<th>N&aacute;zev</th>
<th>Za&ccaron;&aacute;tek</th>
<th>Název</th>
<th>Začátek</th>
<th>Konec</th>
<th>Trv&aacute;n&iacute;</th>
<th>Trvání</th>
<th>Stav</th>
</tr>
{render_incident_history_rows(incidents, include_service=False)}
@@ -1364,10 +1364,10 @@ def new_app_form(request: Request, user=Depends(require_developer)):
</p>
<div class="form-actions">
<button type="submit">Vytvo&rcaron;it slu&zcaron;bu</button>
<button type="submit"><i class="fa-solid fa-circle-plus" aria-hidden="true"></i> Vytvořit službu</button>
<span class="form-loader" id="new-app-loader" hidden>
<span class="spinner" aria-hidden="true"></span>
Vytv&aacute;&rcaron;&iacute;m slu&zcaron;bu...
Vytvářím službu...
</span>
</div>
<div class="async-status" id="new-app-status" aria-live="polite"></div>