fix: polish service runtime UX and harden reconcile edge cases

Backend:
- tolerate concurrent project-network creation (re-resolve on conflict)
- always publish an ephemeral host port when a port is configured, even
  with a domain set — Traefik routes on top when the edge network exists
- public_url now prefers the configured domain, falls back to the first
  published port
- service logs resolve replicas by containr.service label and merge all
  replica streams — previously queried a nonexistent containr-<id> name

Canvas/detail:
- degraded status renders amber end-to-end (node card, detail badge,
  status dot) instead of falling through to "Stopped"
- edges pick handle sides by relative node position — no more backward
  loops when the source sits right of the target
- context menu gains Start for stopped services, Visit uses the
  published URL when no domain is set, menu clamps to canvas bounds
- node cards show the image tag instead of a redundant hint
- scaling tab falls back to live runtime replica counts when no
  autoscaler state exists; manual scale no longer requires it

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-21 20:29:31 +02:00
1 parent 04c3b99531
commit b7e2e93261
9 files changed
+193 -69

No files matched your search

+17
View File
@@ -381,6 +381,10 @@ textarea {
animation: pulse-ring 2s ease-out infinite;
}
.status-degraded {
background: var(--warning);
}
.status-building {
background: var(--warning);
animation: pulse-dot 1.5s ease-in-out infinite;
@@ -979,6 +983,19 @@ textarea {
transition: all 0.3s;
}
.badge-degraded {
padding: 2px 8px;
border-radius: 6px;
background: rgba(242, 201, 76, 0.15);
font-size: 10px;
font-weight: 700;
letter-spacing: 0.7px;
color: var(--warning);
text-transform: uppercase;
display: inline-flex;
align-items: center;
}
/* Badge Stopped - self.html exact match */
.badge-stopped {
padding: 2px 8px;