v0.84.0: catalog-driven initial_credentials — read an app's auto-generated first-login from a file and show it on the app page

This commit is contained in:
2026-06-26 11:00:06 +02:00
parent 49f6dbf847
commit 1705d71dd5
7 changed files with 341 additions and 0 deletions
+11
View File
@@ -477,6 +477,17 @@ func (s *Server) appDetailHandler(w http.ResponseWriter, r *http.Request, slug s
data["HasAppInfo"] = found.Meta.HasAppInfo()
data["EffectiveSubdomain"] = effectiveSubdomain
// Initial auto-generated login (e.g. Crafty writes a random admin password to a file at first
// boot). Read it live from the container so the customer doesn't have to dig through logs. Only
// for deployed apps that declare an initial_credentials spec; hidden when unreadable.
if found.Deployed && found.Meta.InitialCreds != nil {
if creds, err := s.stackMgr.ReadInitialCredentials(found.Name); err != nil {
s.logger.Printf("[WARN] [web] initial-creds for %s: %v", found.Name, err)
} else if creds != nil && creds.Available {
data["InitialCreds"] = creds
}
}
// Per-app migration (B1): offer to move this app's data to another connected drive (≠ current).
if found.Deployed {
current := ""
@@ -140,6 +140,31 @@ function appMigrate(app,label){
</div>
{{end}}
{{if .InitialCreds}}
<div class="app-info-card">
<h3>Kezdeti belépési adatok</h3>
<table class="initcred-table">
{{if .InitialCreds.Username}}
<tr>
<td class="initcred-label" style="padding:.25rem .75rem .25rem 0;color:var(--text-muted);white-space:nowrap">Felhasználónév</td>
<td><code class="initcred-user" style="user-select:all">{{.InitialCreds.Username}}</code></td>
</tr>
{{end}}
<tr>
<td class="initcred-label" style="padding:.25rem .75rem .25rem 0;color:var(--text-muted);white-space:nowrap;vertical-align:middle">Jelszó</td>
<td style="display:flex;align-items:center;gap:.5rem;flex-wrap:wrap">
<code id="initcred-pw">••••••••••••</code>
<span id="initcred-pw-val" hidden>{{.InitialCreds.Password}}</span>
<button type="button" class="btn btn-sm btn-outline" onclick="icRevealPw(this)">Megjelenítés</button>
<button type="button" class="btn btn-sm btn-outline" onclick="icCopyPw(this)">Másolás</button>
</td>
</tr>
</table>
{{if .InitialCreds.Note}}<p class="app-info-creds-warn">{{.InitialCreds.Note}}</p>{{end}}
<p class="app-info-creds-warn">Az első bejelentkezés után azonnal változtasd meg! Ez a kezdeti, automatikusan generált jelszó — ha már megváltoztattad, hagyd figyelmen kívül.</p>
</div>
{{end}}
{{if .AppInfo.DefaultCreds}}
<div class="app-info-card">
<h3>Alapértelmezett belépés</h3>
@@ -157,5 +182,38 @@ function appMigrate(app,label){
</div>
{{end}}
{{if .InitialCreds}}
<script>
// Initial-credential password reveal/copy. The value lives in a hidden element (HTML-escaped by the
// template) so it's never inlined into a JS string literal.
function icPwVal() {
var el = document.getElementById('initcred-pw-val');
return el ? el.textContent : '';
}
function icRevealPw(btn) {
var code = document.getElementById('initcred-pw');
if (!code) return;
if (code.dataset.shown === '1') {
code.textContent = '••••••••••••';
code.dataset.shown = '0';
btn.textContent = 'Megjelenítés';
} else {
code.textContent = icPwVal();
code.dataset.shown = '1';
btn.textContent = 'Elrejtés';
}
}
function icCopyPw(btn) {
var val = icPwVal();
if (!val) return;
navigator.clipboard.writeText(val).then(function () {
var orig = btn.textContent;
btn.textContent = 'Másolva ✓';
setTimeout(function () { btn.textContent = orig; }, 1500);
});
}
</script>
{{end}}
{{template "layout_end" .}}
{{end}}