v0.77.0: per-app open_path for the Megnyitás link

Optional .felhom.yml open_path appended to the open-app URL in dashboard/deploy/app-info
(via .Meta.OpenPath). Empty=bare root. Fixes apps whose UI isn't at / (gokapi /admin, ghost /ghost/).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-23 12:01:12 +02:00
parent 0b7163e5af
commit 904e787dda
5 changed files with 15 additions and 3 deletions
+9
View File
@@ -1,5 +1,14 @@
## Changelog
### v0.77.0 — per-app open_path for the "Megnyitás" link (2026-06-23)
- The dashboard/deploy/app-info **"Megnyitás"** (open) button was hardcoded to the bare subdomain root
`https://{sub}.{domain}` for every app. Apps whose UI isn't at `/` (e.g. Gokapi redirects `/` away;
Ghost's admin is at `/ghost/`) opened to the wrong place.
- New optional `open_path` field on `Metadata` (`internal/stacks/metadata.go`, `.felhom.yml`) appended to
the URL in all three link sites (`dashboard.html`, `deploy.html`, `app_info.html` via `.Meta.OpenPath`).
Empty = bare root (unchanged for the other 50 apps). No handler changes (all three templates already
carry `.Meta`). Catalog: `gokapi``/admin`, `ghost``/ghost/`.
### v0.76.0 — campaign-#3 hardening: settings recovery, restore-name validation, quiesce-marker quarantine (2026-06-22)
Three controller findings from chaos campaign #3, all small, all controller-side.
+3
View File
@@ -16,6 +16,9 @@ type Metadata struct {
Category string `yaml:"category" json:"category"`
Subdomain string `yaml:"subdomain" json:"subdomain"`
Slug string `yaml:"slug" json:"slug"`
// OpenPath is appended to the app's public URL for the "Megnyitás" (open) link, for apps whose UI
// isn't at "/" (e.g. Gokapi → "/admin"). Empty = bare root. Must start with "/".
OpenPath string `yaml:"open_path,omitempty" json:"open_path,omitempty"`
Resources ResourceHints `yaml:"resources" json:"resources"`
DeployFields []DeployField `yaml:"deploy_fields" json:"deploy_fields"`
AppInfo AppInfo `yaml:"app_info" json:"app_info"`
@@ -10,7 +10,7 @@
{{if .Stack.Deployed}}
<span class="stack-state-badge state-{{stateColor .Stack.State}}">{{stateLabel .Stack.State}}</span>
{{if .Stack.Orphaned}}<span class="badge badge-orphaned">Elavult</span>{{end}}
{{if .EffectiveSubdomain}}<a href="https://{{.EffectiveSubdomain}}.{{.Domain}}" target="_blank" class="btn btn-sm btn-outline">Megnyitás ↗</a>{{end}}
{{if .EffectiveSubdomain}}<a href="https://{{.EffectiveSubdomain}}.{{.Domain}}{{.Meta.OpenPath}}" target="_blank" class="btn btn-sm btn-outline">Megnyitás ↗</a>{{end}}
<a href="/stacks/{{.Stack.Name}}/logs" class="btn btn-sm btn-outline">Napló</a>
{{if .Stack.Orphaned}}
<button class="btn btn-sm btn-danger" onclick="deleteOrphanStack('{{.Stack.Name}}')">Törlés</button>
@@ -170,7 +170,7 @@
{{else}}
{{if isOperational .State}}
{{$subdomain := index $.Subdomains .Name}}
{{if $subdomain}}<a href="https://{{$subdomain}}.{{$.Domain}}" target="_blank" class="btn btn-sm btn-outline" onclick="event.stopPropagation()">Megnyitás ↗</a>{{end}}
{{if $subdomain}}<a href="https://{{$subdomain}}.{{$.Domain}}{{.Meta.OpenPath}}" target="_blank" class="btn btn-sm btn-outline" onclick="event.stopPropagation()">Megnyitás ↗</a>{{end}}
<button class="btn btn-sm btn-warning" onclick="stackAction(event, '{{.Name}}', 'restart')"></button>
<button class="btn btn-sm btn-danger" onclick="stackAction(event, '{{.Name}}', 'stop')"></button>
{{else}}
@@ -15,7 +15,7 @@
<button class="btn btn-sm btn-success" onclick="stackAction(event, '{{.Stack.Name}}', 'start')">Indítás</button>
{{end}}
{{if and (isOperational .Stack.State) .EffectiveSubdomain}}
<a href="https://{{.EffectiveSubdomain}}.{{.Domain}}" target="_blank" class="btn btn-sm btn-outline">Megnyitás ↗</a>
<a href="https://{{.EffectiveSubdomain}}.{{.Domain}}{{.Meta.OpenPath}}" target="_blank" class="btn btn-sm btn-outline">Megnyitás ↗</a>
{{end}}
{{end}}
<a href="/apps/{{.Meta.Slug}}" class="btn btn-sm btn-outline">️ Részletek</a>