v0.26.1 — show auto-generated values on deploy page
- Pre-generate domain + secret field values when deploy page loads, so user sees actual domain and masked passwords (with reveal button) before deploying. Same values submitted as hidden inputs → saved to app.yaml. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,12 @@
|
|||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
### v0.26.1 — Show Auto-Generated Values on Deploy Page (2026-02-22)
|
||||||
|
|
||||||
|
#### Changed
|
||||||
|
- **`internal/stacks/deploy.go`** — Added `PreviewDeployValues()` method: pre-generates domain and secret field values when the deploy page is loaded, so the user can see (and note down) exact values before deploying. Updated `DeployStack()` to accept pre-generated secret values from the form instead of always regenerating.
|
||||||
|
- **`internal/web/handlers.go`** — `deployHandler` now calls `PreviewDeployValues()` for non-deployed apps and populates `AutoFieldValues` (previously empty for pre-deploy).
|
||||||
|
- **`internal/web/templates/deploy.html`** — "Automatikusan generált értékek" section now shows actual values on the pre-deploy page too: domain as a readonly text input, secrets as readonly password inputs with a "Megjelenítés" reveal button. Updated section description to inform the user to note down passwords. Pre-generated secret values are submitted as hidden inputs so the same values shown to the user are saved to `app.yaml`.
|
||||||
|
|
||||||
### scripts — Hub Mode + FileBrowser Controller-Managed Volumes (2026-02-22)
|
### scripts — Hub Mode + FileBrowser Controller-Managed Volumes (2026-02-22)
|
||||||
|
|
||||||
#### `scripts/docker-setup.sh` — v6.0.0
|
#### `scripts/docker-setup.sh` — v6.0.0
|
||||||
|
|||||||
@@ -129,15 +129,19 @@ The app catalog lives in a separate Git repository. The controller:
|
|||||||
#### First-Time Deploy Flow
|
#### First-Time Deploy Flow
|
||||||
|
|
||||||
1. Customer sees app card with "Telepites" button
|
1. Customer sees app card with "Telepites" button
|
||||||
2. Deploy page shows auto-filled fields (domain), auto-generated secrets (DB passwords, hex keys, base64 keys), and user-configurable inputs (admin password, language, storage path)
|
2. Deploy page pre-generates and **displays** all auto-values before the user clicks deploy:
|
||||||
|
- `domain` fields: shown as readonly text input with the customer's configured domain
|
||||||
|
- `secret` fields: pre-generated and shown as masked password inputs with a "Megjelenítés" reveal button — user can see/copy all DB passwords and keys before deploying
|
||||||
|
- User-configurable inputs (admin password, language, storage path) remain editable
|
||||||
|
- Section header prompts the user to note down any passwords they need
|
||||||
3. `checkBeforeDeploy()` JS guard fetches live state first (prevents double-deploy from another tab)
|
3. `checkBeforeDeploy()` JS guard fetches live state first (prevents double-deploy from another tab)
|
||||||
4. **Memory validation** checks `mem_request` against available RAM:
|
4. **Memory validation** checks `mem_request` against available RAM:
|
||||||
- `usable_memory = total_ram - reserved_memory_mb` (default 384MB reserved)
|
- `usable_memory = total_ram - reserved_memory_mb` (default 384MB reserved)
|
||||||
- Hard block if requests exceed usable memory
|
- Hard block if requests exceed usable memory
|
||||||
- Soft warning if limits exceed total RAM (overcommit OK)
|
- Soft warning if limits exceed total RAM (overcommit OK)
|
||||||
5. Controller generates secrets, saves `app.yaml`, sets in-memory `Deployed` flag **before** `docker compose up -d` (avoids stale UI during slow image pulls), reverts on failure
|
5. Pre-generated secret values are submitted as hidden form inputs so the **same values** the user saw are saved to `app.yaml` (no silent re-generation on submit). Controller saves `app.yaml`, sets in-memory `Deployed` flag **before** `docker compose up -d` (avoids stale UI during slow image pulls), reverts on failure
|
||||||
6. 3-step progress panel polls `GET /api/stacks/{name}` every 3s: config saved → containers starting → health check passed
|
6. 3-step progress panel polls `GET /api/stacks/{name}` every 3s: config saved → containers starting → health check passed
|
||||||
7. Post-deploy: locked fields (DB_PASSWORD, etc.) become read-only
|
7. Post-deploy: locked fields (DB_PASSWORD, etc.) become read-only; the "Automatikusan generált értékek" section continues to show the saved values on the settings page
|
||||||
|
|
||||||
#### App Info Pages
|
#### App Info Pages
|
||||||
|
|
||||||
|
|||||||
@@ -114,12 +114,17 @@ func (m *Manager) DeployStack(req DeployRequest) (string, error) {
|
|||||||
value = m.cfg.Customer.Domain
|
value = m.cfg.Customer.Domain
|
||||||
|
|
||||||
case "secret":
|
case "secret":
|
||||||
// Always auto-generate, user never sees these
|
// Use pre-generated value if provided by the deploy page (same value the user saw),
|
||||||
generated, err := generateValue(field.Generate)
|
// otherwise fall back to generating a fresh one.
|
||||||
if err != nil {
|
if userVal, ok := req.Values[field.EnvVar]; ok && userVal != "" {
|
||||||
return "", fmt.Errorf("generating %s: %w", field.EnvVar, err)
|
value = userVal
|
||||||
|
} else {
|
||||||
|
generated, err := generateValue(field.Generate)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("generating %s: %w", field.EnvVar, err)
|
||||||
|
}
|
||||||
|
value = generated
|
||||||
}
|
}
|
||||||
value = generated
|
|
||||||
|
|
||||||
case "password":
|
case "password":
|
||||||
// Password fields MUST be filled by the user (via typing or Generálás button).
|
// Password fields MUST be filled by the user (via typing or Generálás button).
|
||||||
@@ -366,6 +371,37 @@ func (m *Manager) LoadAppConfigByName(stackName string) *AppConfig {
|
|||||||
return LoadAppConfig(stackDir)
|
return LoadAppConfig(stackDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PreviewDeployValues generates the auto-field values that will be used at deploy time:
|
||||||
|
// domain from controller config and freshly-generated secrets. These values are shown
|
||||||
|
// on the deploy page so the user can see (and note down) their passwords before deploying.
|
||||||
|
// Pass them back in DeployRequest.Values so the same values are saved to app.yaml.
|
||||||
|
func (m *Manager) PreviewDeployValues(name string) (map[string]string, error) {
|
||||||
|
stack, ok := m.GetStack(name)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("stack %q not found", name)
|
||||||
|
}
|
||||||
|
stackDir := filepath.Dir(stack.ComposePath)
|
||||||
|
meta := LoadMetadata(stackDir)
|
||||||
|
|
||||||
|
result := make(map[string]string)
|
||||||
|
for _, field := range meta.DeployFields {
|
||||||
|
switch field.Type {
|
||||||
|
case "domain":
|
||||||
|
result[field.EnvVar] = m.cfg.Customer.Domain
|
||||||
|
case "secret":
|
||||||
|
if field.Generate == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val, err := generateValue(field.Generate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("generating preview for %s: %w", field.EnvVar, err)
|
||||||
|
}
|
||||||
|
result[field.EnvVar] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// --- App config persistence ---
|
// --- App config persistence ---
|
||||||
|
|
||||||
func LoadAppConfig(stackDir string) *AppConfig {
|
func LoadAppConfig(stackDir string) *AppConfig {
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ func (s *Server) deployHandler(w http.ResponseWriter, r *http.Request, name stri
|
|||||||
data["AppPageURL"] = s.cfg.AppPageURL(meta.Slug)
|
data["AppPageURL"] = s.cfg.AppPageURL(meta.Slug)
|
||||||
data["UserFields"] = meta.UserFacingFields()
|
data["UserFields"] = meta.UserFacingFields()
|
||||||
data["AutoFields"] = meta.AutoGeneratedFields()
|
data["AutoFields"] = meta.AutoGeneratedFields()
|
||||||
// Auto-generated field values for already-deployed apps
|
// Auto-generated field values: existing values for deployed apps, pre-generated for new deploys
|
||||||
autoFieldValues := make(map[string]string)
|
autoFieldValues := make(map[string]string)
|
||||||
if alreadyDeployed && appCfg != nil {
|
if alreadyDeployed && appCfg != nil {
|
||||||
for _, f := range meta.AutoGeneratedFields() {
|
for _, f := range meta.AutoGeneratedFields() {
|
||||||
@@ -262,6 +262,12 @@ func (s *Server) deployHandler(w http.ResponseWriter, r *http.Request, name stri
|
|||||||
autoFieldValues[f.EnvVar] = val
|
autoFieldValues[f.EnvVar] = val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if !alreadyDeployed {
|
||||||
|
// Pre-generate values so the user sees (and can note down) domain/passwords before deploying.
|
||||||
|
// These same values are submitted back in the form and saved to app.yaml.
|
||||||
|
if preview, err := s.stackMgr.PreviewDeployValues(name); err == nil {
|
||||||
|
autoFieldValues = preview
|
||||||
|
}
|
||||||
}
|
}
|
||||||
data["AutoFieldValues"] = autoFieldValues
|
data["AutoFieldValues"] = autoFieldValues
|
||||||
// Storage paths with free space info for deploy dropdown
|
// Storage paths with free space info for deploy dropdown
|
||||||
|
|||||||
@@ -230,14 +230,18 @@
|
|||||||
{{if .AutoFields}}
|
{{if .AutoFields}}
|
||||||
<div class="form-section">
|
<div class="form-section">
|
||||||
<h4>Automatikusan generált értékek</h4>
|
<h4>Automatikusan generált értékek</h4>
|
||||||
<p class="form-section-desc">Ezek az értékek automatikusan létrejönnek a telepítéskor.</p>
|
{{if .AlreadyDeployed}}
|
||||||
|
<p class="form-section-desc">Ezek az értékek automatikusan jöttek létre a telepítéskor.</p>
|
||||||
|
{{else}}
|
||||||
|
<p class="form-section-desc">Ezek az értékek a telepítéssel együtt mentésre kerülnek. Jegyezze fel a szükséges jelszavakat!</p>
|
||||||
|
{{end}}
|
||||||
{{$autoValues := .AutoFieldValues}}
|
{{$autoValues := .AutoFieldValues}}
|
||||||
{{$isDeployed := .AlreadyDeployed}}
|
{{$isDeployed := .AlreadyDeployed}}
|
||||||
{{range .AutoFields}}
|
{{range .AutoFields}}
|
||||||
{{$val := index $autoValues .EnvVar}}
|
{{$val := index $autoValues .EnvVar}}
|
||||||
<div class="form-group form-group-auto">
|
<div class="form-group form-group-auto">
|
||||||
<label>{{.Label}} <span class="auto-generated-badge">✓ Automatikusan generálva</span></label>
|
<label>{{.Label}} <span class="auto-generated-badge">✓ Automatikusan generálva</span></label>
|
||||||
{{if and $isDeployed $val}}
|
{{if $val}}
|
||||||
{{if eq .Type "secret"}}
|
{{if eq .Type "secret"}}
|
||||||
<div class="input-with-button">
|
<div class="input-with-button">
|
||||||
<input type="password" id="auto-field-{{.EnvVar}}" class="form-control" value="{{$val}}" readonly>
|
<input type="password" id="auto-field-{{.EnvVar}}" class="form-control" value="{{$val}}" readonly>
|
||||||
@@ -246,6 +250,9 @@
|
|||||||
{{else}}
|
{{else}}
|
||||||
<input type="text" id="auto-field-{{.EnvVar}}" class="form-control" value="{{$val}}" readonly>
|
<input type="text" id="auto-field-{{.EnvVar}}" class="form-control" value="{{$val}}" readonly>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{if and (not $isDeployed) (eq .Type "secret")}}
|
||||||
|
<input type="hidden" name="{{.EnvVar}}" value="{{$val}}">
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
Reference in New Issue
Block a user